以编程方式检查MSI的ProductVersion

use*_*570 2 c# database pinvoke windows-installer properties

昨天我一直在搜索高低,寻找如何快速获得msi数据库的ProductVersion.大多数情况下,我发现使用WindowsInstaller COM包装器,虽然这完成了工作,我想通过pinvoke使用msi.dll实现相同的结果.

use*_*570 8

这就是我想出来的.

C#Windows Installer 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); 
Run Code Online (Sandbox Code Playgroud)

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();
    }
Run Code Online (Sandbox Code Playgroud)

这可以很容易地推断为通过更改SQL语句从msi数据库获取任何属性或字段.我希望这可以帮助别人.

  • 这个P/Invoke代码有两个主要缺陷(在你忘记编写这段代码之前不太可能咬你):MSIHANDLEs是Int32而不是IntPtr(是的,这与大多数HANDLE类型不同,仅在x64环境中相关),以及你的属性获取代码无法处理超过255的属性值. (2认同)

Chr*_*ter 6

任何需要与MSI进行.NET互操作的人都应该使用WiX的DTF SDK中的Microsoft.Deployment.WindowsInstaller.它是一个非常干净的库,比尝试编写自己的库更好.