13

I want to read properties of MSI in C# in desktop application.I am using following code:

public static string GetMSIProperty( string msiFile, string msiProperty)
{
    string retVal= string.Empty ;

    Type classType = Type.GetTypeFromProgID("WindowsInstaller.Installer");
    Object installerObj = Activator.CreateInstance(classType);
    WindowsInstaller.Installer installer = installerObj as WindowsInstaller.Installer;

    Database database = installer.OpenDatabase("C:\\DataP\\sqlncli.msi",0 );   

    string sql = String.Format("SELECT Value FROM Property WHERE Property=’{0}’", msiProperty);

    View view = database.OpenView(sql);

    Record record = view.Fetch();

    if (record != null)
    {
        retVal = record.get_StringData(1);
    }
    else
        retVal = "Property Not Found";

    return retVal;            
}

But I am getting error as System.Runtime.InteropServices.COMException was unhandled.

the sqlncli.msi file is physically placed at c:\DataP location. While debugging I found that database does not contain the data after installer.OpenDatabase() statement.

How can I resolve this issue and get MSI properties in C#?

Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
Devashri B.
  • 2,593
  • 8
  • 25
  • 39

5 Answers5

19

Windows Installer XML's Deployment Tools Foundation (WiX DTF) is an Open Source project from Microsoft which includes the Microsoft.Deployment.WindowsInstaller MSI interop library. It's far easier and more reliable to use this to do these sorts of queries. It even has a LINQ to MSI provider that allows you to treat MSI tables as entities and write queries against them.

using System;
using System.Linq;
using Microsoft.Deployment.WindowsInstaller;
using Microsoft.Deployment.WindowsInstaller.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            using(var database = new QDatabase(@"C:\tfs\iswix.msi", DatabaseOpenMode.ReadOnly))
            {
                var properties = from p in database.Properties
                                 select p;

                foreach (var property in properties)
                {
                    Console.WriteLine("{0} = {1}", property.Property, property.Value);
                }
            }

            using (var database = new Database(@"C:\tfs\iswix.msi", DatabaseOpenMode.ReadOnly))
            {
                using(var view = database.OpenView(database.Tables["Property"].SqlSelectString))
                {
                    view.Execute();
                    foreach (var rec in view) using (rec)
                    {
                        Console.WriteLine("{0} = {1}", rec.GetString("Property"), rec.GetString("Value"));
                    }
                }
            }

            Console.Read();
        }
    }
}
Christopher Painter
  • 54,556
  • 6
  • 63
  • 100
2

I did it in following way:

String inputFile = @"C:\\Rohan\\sqlncli.msi";

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

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

// Open the MSI database in the input file
Database database = installer.OpenDatabase(inputFile, 0);

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

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

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

while (record != null)
{
    Console.WriteLine(record.get_StringData(0) + '=' + record.get_StringData(1) + '=' + record.get_StringData(2) + '=' + record.get_StringData(3));
    record = view.Fetch();
}

And its working for me.

Crono
  • 10,211
  • 6
  • 43
  • 75
Devashri B.
  • 2,593
  • 8
  • 25
  • 39
  • 1
    Downvoted for: 1) Using @ and escaping backslashes (redundant) 2) Using COM Interop when a much cleaner interop library exists that is based on P/Invoke 3) Failing to close any of your unmanaged handles. – Christopher Painter Jun 18 '15 at 12:26
  • 5
    @ChristopherPainter, feel free to edit the answer rather than downvote, as I think this adds more value to the community. – Martin Capodici Aug 19 '15 at 04:04
  • That was 3 years ago. Rewriting the answer to use an interop library instead of COM Interop is more then a little 'edit'. – Christopher Painter Aug 19 '15 at 11:51
1

The SQL string is incorrect. It should be:

SELECT `Value` FROM `Property` WHERE `Property`.`Property` = ’{0}’
Ciprian
  • 3,533
  • 1
  • 18
  • 22
1

I was trying to re-use this code, and the only change I had to make to get the code posted by Devashri to work is this line:

string sql = String.Format("SELECT `Value` FROM `Property` WHERE `Property`='{0}'", msiProperty);

Watch out for the single quotes!

dcaswell
  • 3,137
  • 2
  • 26
  • 25
-1

as of 04/2020 it would be

Type installerType { get; set; }
WindowsInstaller.Installer installerObj { get; set; }
...
installerType = Type.GetTypeFromProgID("WindowsInstaller.Installer"); 
installerObj = (WindowsInstaller.Installer)Activator.CreateInstance(installerType);
var installer = installerObj as WindowsInstaller.Installer;
...
private void lnkSelectMsi_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
   WindowsInstaller.Database msiDatabase = installerObj.OpenDatabase(txtMsiPath.Text, 0);
   readMsiTableColumn(msiDatabase, cmbTable.Text, cmbColumn.Text);
}

   private void readMsiTableColumn(WindowsInstaller.Database msiDatabase, string table)
    {
        WindowsInstaller.View msiView = null;
        Record record = null;
        string s = string.Empty;
        try
        {
            msiView = msiDatabase.OpenView($"Select * from _Columns");
            msiView.Execute();
            record = msiView.Fetch();
            int k = 0;
            while (record != null)
            {
                if (record.StringData[1].Equals(table, StringComparison.OrdinalIgnoreCase))
                {
                    k++;
                    s += $"{record.StringData[3],-50} ";
                }
                record = msiView.Fetch();
            }
            s += nl;
            s += "".PadRight(50 * k, '-') + nl;
            msiView.Close();

            msiView = msiDatabase.OpenView($"Select * from {table}"); 
            msiView.Execute();
            record = msiView.Fetch();

            while (record != null)
            {
                string recordValue = string.Empty;
                for (int i = 1; i < record.FieldCount + 1; i++)
                {
                    try { recordValue += $"{record.StringData[i],-50} "; }
                    catch (Exception ex) { recordValue += $"{i}. err {ex.Message}; "; }
                }
                s += recordValue + nl;
                record = msiView.Fetch();
            }
            msiView.Close();
            txtRes.Text = s;
        }
        catch (Exception ex) { txtRes.Text = ex.Message; }
    }
Sasha Bond
  • 984
  • 10
  • 13