2010-12-03 17 views
2

MSIデータベースのProductVersionをすばやく取得する方法を探して、昨日の高低を検索しました。ほとんどの場合、WindowsInstaller COMラッパーを利用していることがわかりましたが、これは仕事が終わったあと、msi.dllを使用してpinvokeで同じ結果を得たいと思っています。MSIのProductVersionをプログラムで確認する

+2

@ user529570ようこそスタックオーバーフロー - これは既にあなたがこれを解決していることは重要ではない、それはまだ有用です。あなたはそれを編集して(質問の下にある「編集」リンクを使用して)これを質問とし、解答の形で回答したいと思うかもしれません。これにより、人々は代替の回答を提供し、あなたの答えに投票することができます。 –

+0

便利です。 Codeprojectはこのようなものを投稿する場所のようです。人々が解決策を投稿できるサイトは非常に便利です。 – Nick

+0

コリンありがとうございます。 – user529570

答えて

8

ここに私が思いついたのがあります。

C#WindowsインストーラCOMライブラリ:

  // Get the type of the Windows Installer object 
      Type installerType = Type.GetTypeFromProgID("WindowsInstaller.Installer"); 

      // Create the Windows Installer object 
      Installer installer = (Installer)Activator.CreateInstance(installerType); 

      // Open the MSI database in the input file 
      Database database = installer.OpenDatabase(od.FileName, MsiOpenDatabaseMode.msiOpenDatabaseModeReadOnly); 

      // Open a view on the Property table for the version property 
      WindowsInstaller.View view = database.OpenView("SELECT * FROM Property WHERE Property = 'ProductVersion'"); 

      // Execute the view query 
      view.Execute(null); 

      // Get the record from the view 
      Record record = view.Fetch(); 

      // Get the version from the data 
      string version = record.get_StringData(2); 

C#のPInvoke:

[DllImport("msi.dll", SetLastError = true)] 
    static extern uint MsiOpenDatabase(string szDatabasePath, IntPtr phPersist, out IntPtr phDatabase); 

    [DllImport("msi.dll", CharSet = CharSet.Unicode)] 
    static extern int MsiDatabaseOpenViewW(IntPtr hDatabase, [MarshalAs(UnmanagedType.LPWStr)] string szQuery, out IntPtr phView); 

    [DllImport("msi.dll", CharSet = CharSet.Unicode)] 
    static extern int MsiViewExecute(IntPtr hView, IntPtr hRecord); 

    [DllImport("msi.dll", CharSet = CharSet.Unicode)] 
    static extern uint MsiViewFetch(IntPtr hView, out IntPtr hRecord); 

    [DllImport("msi.dll", CharSet = CharSet.Unicode)] 
    static extern int MsiRecordGetString(IntPtr hRecord, int iField, 
     [Out] StringBuilder szValueBuf, ref int pcchValueBuf); 

    [DllImport("msi.dll", ExactSpelling = true)] 
    static extern IntPtr MsiCreateRecord(uint cParams); 

    [DllImport("msi.dll", ExactSpelling = true)] 
    static extern uint MsiCloseHandle(IntPtr hAny); 

    public string GetVersionInfo(string fileName) 
    { 
     string sqlStatement = "SELECT * FROM Property WHERE Property = 'ProductVersion'"; 
     IntPtr phDatabase = IntPtr.Zero; 
     IntPtr phView = IntPtr.Zero; 
     IntPtr hRecord = IntPtr.Zero; 

     StringBuilder szValueBuf = new StringBuilder(); 
     int pcchValueBuf = 255; 

     // Open the MSI database in the input file 
     uint val = MsiOpenDatabase(fileName, IntPtr.Zero, out phDatabase); 

     hRecord = MsiCreateRecord(1); 

     // Open a view on the Property table for the version property 
     int viewVal = MsiDatabaseOpenViewW(phDatabase, sqlStatement, out phView); 

     // Execute the view query 
     int exeVal = MsiViewExecute(phView, hRecord); 

     // Get the record from the view 
     uint fetchVal = MsiViewFetch(phView, out hRecord); 

     // Get the version from the data 
     int retVal = MsiRecordGetString(hRecord, 2, szValueBuf, ref pcchValueBuf); 

     uRetCode = MsiCloseHandle(phDatabase); 
     uRetCode = MsiCloseHandle(phView); 
     uRetCode = MsiCloseHandle(hRecord); 

     return szValueBuf.ToString(); 
    } 

これは、簡単にSQLステートメントを変更することにより、MSIデータベースから任意のプロパティまたはフィールドを取得するに推定することができます。私はこれが誰かを助けることを望む。

+2

このP/Invokeコードには2つの主な欠陥があります(このコードを書くのを忘れるまであなたを噛んでしまう可能性は低いです):MSIHANDLEはInt32ではないIntPtrです(これはほとんどのHANDLEタイプと異なり、x64環境のみに関連します)。プロパティーフェッチコードは255より長いプロパティー値を扱うことができません。 –

+0

MSIHANDLEsについて、私は気づきませんでした。それは、そのバグを見つけるために下に行く巨大なウサギの穴だったでしょう。プロパティ値の長さは簡単に変更できます。私はちょうどそれを任意に255に設定しました。 – user529570

+0

エラー静的型 'Microsoft.Deployment.WindowsInstaller.Installer'に変換できません –

6

MSIで.NET interopを実行する必要がある人は、WiXのDTF SDKにあるMicrosoft.Deployment.WindowsInstallerを使用する必要があります。それは非常にきれいな図書館であり、あなた自身のことを書こうとするより良い方法です。

+0

interopライブラリをメイン実行可能ファイルに埋め込む方法はありますか?私は、アプリケーションに追加の外部DLLに沿ってタグを付けなくても、相互運用する方法を探していました。 – user529570

+0

MicrosoftのILMergeはそのトリックを行うことができます。 –

+0

恐ろしい!ありがとう。 – user529570

関連する問題