Read Properties from an MSI File

Today I was working writing auto-updating for some software. I wanted to base it on the Product Version property in the installer MSI file, so I needed some code to read that from the file.

It took a fair amount of searching and code tweaking, but I finally worked it all out.

  1. Add a reference to the COM Microsoft Windows Installer Object Library.
  2. Add a using WindowsInstaller;
  3. Add the following static method to your code (error checking removed for brevity.)
public static string GetMsiProperty( string msiFile, string property )
{
   string retVal = string.Empty;

   // Create an Installer instance
   Type classType = Type.GetTypeFromProgID( “WindowsInstaller.Installer” );
   Object installerObj = Activator.CreateInstance( classType );
   Installer installer = installerObj as Installer;

   // Open the msi file for reading
   // 0 - Read, 1 - Read/Write
   Database database = installer.OpenDatabase( msiFile, 0 );

   // Fetch the requested property
   string sql = String.Format(
      “SELECT Value FROM Property WHERE Property=’{0}’”, property );
   View view = database.OpenView( sql );
   view.Execute( null );

   // Read in the fetched record
   Record record = view.Fetch();
   if ( record != null )
      retVal = record.get_StringData( 1 );

   return retVal;
}

If you want to look up the version, just pass in the name of the MSI file you want to inspect and “ProductVersion” for the property you want to return. For example;

string version = GetMsiProperty( msiFile, “ProductVersion” );

You can use this method to look up other properties in the installer. Some common ones you might want are ProductName, ProductCode, UpgradeCode, Manufacturer, ARPHELPLINK, ARPCOMMENTS, ARPCONTACT, ARPURLINFOABOUT and ARPURLUDATEINFO. For a full list of properties, see the MSDN Reference, but remember that most of the properties listed on that page are only for already installed applications and won’t be included in the installer.

If you find this code useful or the code is not self-explanatory, please leave a comment.

16 thoughts on “Read Properties from an MSI File

  1. Hello
    I’ve read your post on Alteridem Consulting Website about reading properties from an MSI file.
    I use GetMsiProperty function to find out MSI file version, but I encouraged a problem while trying to delete file after I’ve checked it’s version.
    It seems like the file is still in use by GetMsiProperty function. I tried to find some closing function but I didn’t succeed.

    Maybe you know something I forgot to do while reading Msi version?

    I would appriciate any help.

  2. Thanks for the code, Robert!

    But I too encountered the deletion problem. A colleague of mine searched and found the (somewhat unintuitive…) solution. Before returning from GetMsiProperty, do:

    if (record != null)
    {
    System.Runtime.InteropServices.Marshal.FinalReleaseComObject(record);
    }
    view.Close();
    System.Runtime.InteropServices.Marshal.FinalReleaseComObject(view);
    System.Runtime.InteropServices.Marshal.FinalReleaseComObject(database);

  3. hello….
    can we edit or modify the .msi file.???
    coz i want rebuilt that software by my own

  4. I was getting an interop exception on the OpenView method when I tried this code. I tracked it down to the SQL statement. Removing the single quotes from the reference to the Value column and the Properties table fixed the code.

  5. Here is an improved version of the above code. This new versions allows to pass in a dictionary of property names whose values are then filled in.

    ///
    /// Uses the Windows Installer COM component to retrieve property values (e.g. ProductCode) from an MSI file.
    ///
    /// The path to an MSI file.
    /// A dictionary containing the property names whose values need to be retrieved and filled in from the MSI file.
    public static void GetMsiProperties(string msiFile, SortedDictionary propertyNames)
    {
    if (string.IsNullOrEmpty(msiFile))
    throw new ArgumentException(Resources.ErrBlankMsiFilePath, “msiFile”);

    if (propertyNames == null || propertyNames.Count == 0)
    throw new ArgumentException(Resources.ErrNullOrEmptyPropertyNamesDictionary, “propertyNames”);

    Type classType = Type.GetTypeFromProgID(“WindowsInstaller.Installer”);
    Object installerObj = Activator.CreateInstance(classType);
    Installer installer = (Installer)installerObj;

    // Open the msi file for reading : 0 – Read, 1 – Read/Write
    Database database = installer.OpenDatabase(msiFile, 0);
    StringBuilder sql = new StringBuilder(“SELECT Property, Value FROM Property WHERE “);

    foreach (KeyValuePair item in propertyNames)
    {
    sql.AppendFormat(“Property = ‘{0}’ OR “, item.Key);
    }

    sql.Remove(sql.Length – 4, 4); // remove last ” OR ”

    View view = database.OpenView(sql.ToString());
    view.Execute(null);

    Record record = view.Fetch();
    while (record != null)
    {
    propertyNames[record.StringData[1]] = record.StringData[2];
    record = view.Fetch();
    }

    view.Close();
    }

  6. Thank you, thank you, thank you!!! I’ve fond this code in lots of places, but nowhere does it say what the assemly that you nood to include is! I was about to search through every .NET and COM assembly included, but then I found your article and was saved!

    Again, Thank you!

  7. Mike and Neo are correct, you MUST correct your SQL string in your code, as it generates a COM exception. Don’t you read your comments?

    So much for a “consulting” business…

  8. Hello.

    Great post, but I got a problem trying to add refrences to Microsoft Windows Installer Object Libray. It tells me that isnt registered. (TYPE_E_LIBNOTREGISTERED)

    What to do?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>