Как освободить дескриптор COM в .NET - PullRequest
3 голосов
/ 26 марта 2012

Я использую следующий код в рамках ASP.NET 4.0 для получения версии файла 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.

РЕДАКТИРОВАТЬ: Отредактировал код так, как это должно быть после предложения Адриано.Спасибо!

Ответы [ 3 ]

9 голосов
/ 26 марта 2012

COM-объект не был выпущен (он должен автоматически освобождаться, когда выходит из области видимости, но в .NET это не очень хорошо работает).Поскольку он не реализует интерфейс IDisposable, вы не можете вызвать его метод Dispose() и не можете использовать его внутри оператора using.Вы должны явно вызвать Marshal.FinalReleaseComObject .Например:

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

Кроме того, обратите внимание, что вам на самом деле не нужен вызов метода Commit(), потому что вы не внесли никаких изменений, а только запрос.

3 голосов
/ 26 марта 2012

FWIW, вы должны использовать установщик Windows XML (WiX) Deployment Tools Foundation (DTF).Это проект FOSS от Microsoft, который можно найти на CodePlex.Он имеет библиотеки взаимодействия MSI с классами, которые очень похожи на классы COM, но реализуют IDisosable и используют P / Invoke вместо COM за кулисами.Есть даже поддержка Linq to MSI, если хотите.И полный исходный код доступен.

DTF - золотой стандарт взаимодействия MSI в мире .NET.Вот два примера:

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());                    
            }
        }
    }
}
0 голосов
/ 26 марта 2012

попробуйте Dispose Объекты.

dv.Dispose();
db.Dispose();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...