How to release a COM handle in .NET.

I am using the following code within ASP.NET 4.0 to get the version of an MSI file from a web application:

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 great, except that when the code ends, it contains an internal MSI file descriptor, so when I try to move or rename the MSI file, I get an error that the file is still in use. This continues until I actually move away from the ASPX page calling the method above.

My question is: I obviously haven't closed any handle or object in the above code. But what could it be?

PS. I am testing it in development IDE from VS2010.

EDIT: Edited code as it should be after Adriano's suggestion. Thank!

+3


source to share


3 answers


The COM object was not released (it should be automatically released when it goes out of scope, but in .NET this doesn't work very well). Since it does not implement an interface IDisposable

, you cannot call its method Dispose()

, and you cannot use it inside a statement using

. You must explicitly call Marshal.FinalReleaseComObject . For example:

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

      



Also, note that you don't really need a method call Commit()

because you haven't made any changes, just a request.

+9


source


FWIW, you must use Windows Installer XML (WiX) Deployment Tools Foundation (DTF). This is a FOSS project from Microsoft and can be found at CodePlex. It has MSI interworking libraries with classes that are very similar to COM classes but implement IDisosable and use P / Invoke instead of COM behind the scenes. Linq support for MSI is supported even when needed. And the complete source code is available.

DTF is the gold standard for MSI interoperability in the .NET world. 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());                    
            }
        }
    }
}

      

+3


source


try Dispose

Objects.

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

      

-2


source







All Articles