2012-03-26 12 views
3

freizugeben ich unter ASP.NET 4.0 Framework den folgenden Code verwenden die Version der MSI-Datei aus einem Web-App zu erhalten:Wie COM Griff in .NET

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 = ""; 
} 

Es funktioniert, außer dass feines der Code, wenn beendet, es hält ein internes MSI-Dateihandle, wenn ich versuche, die MSI-Datei zu verschieben oder umzubenennen, erhalte ich den Fehler, dass die Datei noch verwendet wird. Dies wird fortgesetzt, bis ich tatsächlich von der ASPX-Seite weg navigiere, die die obige Methode aufruft.

Meine Frage ist, habe ich offensichtlich keinen Griff oder Objekt im obigen Code geschlossen. Aber was könnte das sein?

PS. Ich teste es in einer Entwicklungs-IDE von VS2010.

EDIT: Bearbeitete den Code wie es nach Adriano Vorschlag sein sollte. Vielen Dank!

+2

Führt eine der WindowsInstaller-Klassen IDisposable? Wenn dies der Fall ist, sollten Sie sie in Blöcken platzieren. –

+0

Ich weiß ehrlich gesagt nicht viel über WindowsInstaller. Ich habe versucht mit "verwenden" und es ging nicht durch. Also ich denke, nicht ... – ahmd0

+0

Windows Installer hat eine Win32-API und eine Automatisierungsschnittstelle (COM). Aus einer .NET-Perspektive ist die Win32 API viel sauberer als P/Invoke, dann die COM. Zusätzlich gibt es bereits eine Reihe von Klassen, um all dies für Sie zu kapseln. Siehe meine Antwort unten für weitere Informationen. –

Antwort

9

Das COM-Objekt wurde nicht freigegeben (sollte es sein auto-freigegeben, wenn es außerhalb des Gültigkeitsbereichs geht aber in .NET dies nicht wirklich gut funktioniert). Da es die IDisposable Schnittstelle nicht implementiert, können Sie seine Dispose() Methode nicht aufrufen, und Sie können es in einer using Anweisung nicht verwenden. Sie müssen explizit Marshal.FinalReleaseComObject aufrufen. Zum Beispiel:

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

Darüber hinaus beachten Sie, dass Sie nicht wirklich einen Aufruf an die Commit() Methode, weil Sie keine Änderung gemacht hat, sondern nur eine Abfrage.

+1

Super! Du hast mir gerade eine Menge Zeit gespart !!! Vielen Dank, Alter! Bitte fügen Sie diese Zeile auch zu Ihrer Antwort hinzu: System.Runtime.InteropServices.Marshal.FinalReleaseComObject (dv); Wenn du es neben dem nennst, was du gepostet hast, löst du das Problem. Und der Grund, warum ich Commit() aufgerufen habe, ist wegen dieser Seite: http://msdn.microsoft.com/en-us/library/windows/desktop/aa368254(v=vs.85).aspx#methods – ahmd0

+0

Das Update der Beispiel, danke! –

+2

Ich würde hinzufügen, dass dies in einem 'endlich' Block sein sollte. Daher wird das COM-Objekt immer freigegeben, auch wenn eine Ausnahme ausgelöst wird. – Grhm

-2

versuchen Sie, die Dispose Objekte.

dv.Dispose(); 
db.Dispose(); 
+1

Diese Methoden existieren nicht in dv und db. – ahmd0

+0

Ja Eigentlich dachte ich, dass sie IDisposable Schnittstelle implementieren können, aber sie donot ... so können Sie die Dispose Methode nicht verwenden. Adriano hat Recht .. Sie müssen Marshal.FinalReleaseComObject (db) verwenden, um COM-Referenzen freizugeben. – Pranav

3

FWIW, Sie sollten Windows Installer XML (WiX) Deployment Tools Foundation (DTF) verwenden. Es ist ein FOSS-Projekt von Microsoft, das auf CodePlex zu finden ist. Es hat MSI-Interop-Bibliotheken mit Klassen, die den COM-Klassen sehr ähnlich sind, aber IDisosable implementieren und P/Invoke statt COM hinter den Kulissen verwenden. Es gibt sogar Unterstützung für Linq zu MSI, wenn Sie möchten. Und der vollständige Quellcode ist verfügbar.

DTF ist der Goldstandard für MSI Interop in einer .NET-Welt. Hier zwei Beispiele:

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

Was stimmt nicht mit dem Code, den ich oben gepostet habe? – ahmd0

+0

Es ist sinnlos, das Rad mit einer minderwertigen Lösung neu zu erfinden. Microsoft stellte eine geeignete Bibliothek zum Kapseln der Windows Installer-API bereit. –