我在ASP.NET 4.0框架下使用以下代码从Web应用程序获取MSI文件的版本:
string strVersion = "";
try
{
Type InstallerType;
WindowsInstaller.Installer installer;
InstallerType = Type.GetTypeFromProgID("WindowsInstaller.Installer");
installer = (WindowsInstaller.Installer)Activator.CreateInstance(InstallerType);
WindowsInstaller.Database db = installer.OpenDatabase(strMSIFilePath, 0);
WindowsInstaller.View dv = db.OpenView("SELECT `Value` FROM `Property` WHERE `Property`='ProductVersion'");
WindowsInstaller.Record record = null;
dv.Execute(record);
record = dv.Fetch();
strVersion = record.get_StringData(1).ToString();
dv.Close();
//db.Commit();
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(dv);
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(db);
}
catch
{
//Failed
strVersion = "";
}
它工作正常,但是当代码完成运行时它保存一个内部MSI文件句柄,所以当我尝试移动或重命名MSI文件时,我得到文件仍在使用中的错误。这一直持续到我实际离开调用上述方法的ASPX页面。
我的问题是,我显然没有在上面的代码中关闭一些句柄或对象。但那可能是什么呢?
PS。我正在VS2010的开发IDE中测试它。
编辑:按照Adriano的建议编辑代码。谢谢!
答案 0 :(得分:9)
COM对象尚未发布(当它超出范围时应该自动释放,但在.NET中这不能很好地工作)。因为它没有实现IDisposable
接口,所以无法调用其Dispose()
方法,也无法在using
语句中使用它。您必须明确调用Marshal.FinalReleaseComObject。例如:
try
{
// Your stuffs
}
finally
{
dv.Close();
Marshal.FinalReleaseComObject(dv);
Marshal.FinalReleaseComObject(db);
}
此外请注意,您并不需要调用Commit()
方法,因为您没有进行任何更改,只是查询。
答案 1 :(得分:3)
FWIW,您应该使用Windows Installer XML(WiX)部署工具基础(DTF)。这是Microsoft的FOSS项目,可以在CodePlex上找到。它有MSI互操作库,其类与COM类非常相似,但实现了IDisosable并在后台使用P / Invoke而不是COM。如果你愿意,甚至可以支持Linq到MSI。完整的源代码可用。
DTF是.NET世界中MSI互操作的黄金标准。以下是两个例子:
using System;
using System.Linq;
using Microsoft.Deployment.WindowsInstaller;
using Microsoft.Deployment.WindowsInstaller.Linq;
namespace ConsoleApplication3
{
class Program
{
const string DATABASE_PATH = @"C:\FOO..MSI";
const string SQL_SELECT_PRODUCTVERSION = "SELECT `Value` FROM `Property` WHERE `Property`='ProductVersion'";
static void Main(string[] args)
{
using (Database database = new Database(DATABASE_PATH, DatabaseOpenMode.ReadOnly))
{
Console.WriteLine(database.ExecuteScalar(SQL_SELECT_PRODUCTVERSION).ToString());
}
using (QDatabase database = new QDatabase(DATABASE_PATH, DatabaseOpenMode.ReadOnly))
{
var results = from property in database.Properties where property.Property == "ProductVersion" select property.Value;
Console.WriteLine(results.AsEnumerable<string>().First());
}
}
}
}
答案 2 :(得分:-2)
尝试Dispose
对象。
dv.Dispose();
db.Dispose();