简体   繁体   English

如何在.NET中释放COM句柄

[英]How to release COM handle in .NET

I am using the following code under ASP.NET 4.0 framework to obtain the version of MSI file from a web app: 我在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 = "";
}

It works fine except that when the code finishes running it holds an internal MSI file handle so when I try to move or rename the MSI file I get the error that the file is still in use. 它工作正常,只不过当代码完成运行时,它拥有一个内部MSI文件句柄,因此当我尝试移动或重命名MSI文件时,我得到该文件仍在使用中的错误。 This continues until I actually navigate away from the ASPX page that calls the method above. 这一直持续到我实际离开调用上述方法的ASPX页面为止。

My question is, I obviously didn't close some handle or object in the code above. 我的问题是,我显然没有在上面的代码中关闭某些句柄或对象。 But what could that be? 但是那会是什么呢?

PS. PS。 I'm testing it in a development IDE from VS2010. 我正在VS2010的开发IDE中对其进行测试。

EDIT: Edited the code like it should be after Adriano's suggestion. 编辑:像应该在Adriano的建议之后一样编辑代码。 Thanks! 谢谢!

The COM object has not been released (it should be auto-released when it goes out of scope but in .NET this doesn't work really well). COM对象尚未发布(超出范围时应自动释放,但是在.NET中,它不能很好地工作)。 Because it does not implement the IDisposable interface you can't call its Dispose() method and you can't use it inside an using statement. 因为它没有实现IDisposable接口,所以不能调用其Dispose()方法,也不能在using语句内使用它。 You have to explicitly call Marshal.FinalReleaseComObject . 您必须显式调用Marshal.FinalReleaseComObject For example: 例如:

try
{
    // Your stuffs
}
finally
{
    dv.Close();
    Marshal.FinalReleaseComObject(dv);
    Marshal.FinalReleaseComObject(db);
}

Moreover note that you do not really need a call to the Commit() method because you didn't make any change but just a query. 此外,请注意,您实际上不需要调用Commit()方法,因为您没有进行任何更改,而只是进行了查询。

FWIW, you should be using Windows Installer XML (WiX) Deployment Tools Foundation (DTF). FWIW,您应该使用Windows Installer XML(WiX)部署工具基础(DTF)。 It's an FOSS project from Microsoft that can be found on CodePlex. 这是Microsoft的FOSS项目,可以在CodePlex上找到。 It has MSI interop libraries with classes that are very similar to the COM classes but implement IDisosable and use P/Invoke instead of COM behind the scenes. 它具有MSI互操作库,其类与COM类非常相似,但是实现了IDisosable并在后台使用P / Invoke而不是COM。 There is even support for Linq to MSI if you want. 如果需要,甚至还支持Linq to MSI。 And the full source code is available. 并提供了完整的源代码。

DTF is the gold standard for MSI interop in a .NET world. DTF是.NET世界中MSI互操作的黄金标准。 Here are two examples: 这是两个示例:

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());                    
            }
        }
    }
}

try to Dispose the Objects. 尝试Dispose对象。

dv.Dispose();
db.Dispose();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM