1

I have an MSI file from which I need to read the ProductName and ProductVersion info. This is not the SummaryInfo data.

I am using Microsoft.Deployment.WindowsInstaller and have the following code:

using (var database = new Database(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"));
            }
        }
    }
}

This does a fine job of enumerating all the Property/Value pairs, but I just need ProductName and ProductVersion to add to a Tuple<string, string>. How can I read just these two values from the MSI?

UPDATE

I got this far, and it works, but there is probably a more succint/efficient solution:

public static IEnumerable<Tuple<string, string>> GetVersionInfo()
{
    // Enumerate MSI files in C:\SetupFiles (or wherever)
    var setupFilesDir = ConfigurationManager.AppSettings["setupFilesDir"] ?? string.Empty;
    var di = new DirectoryInfo(setupFilesDir);
    var msiFiles = from msi in di.GetFiles("*.msi", SearchOption.AllDirectories)
        select msi.FullName;

    var tuples = new List<Tuple<string, string>>();

    foreach (var msi in msiFiles)
    {
        using (var db = new Database(msi, DatabaseOpenMode.ReadOnly))
        {
            var productVersion = (string)db.ExecuteScalar("SELECT `Value` FROM `Property` WHERE `Property` = 'ProductVersion'");
            var productName = (string)db.ExecuteScalar("SELECT `Value` FROM `Property` WHERE `Property` = 'ProductName'");

            tuples.Add(new Tuple<string, string>(productName, productVersion));
        }
    }

    return tuples;
}
Mark Richman
  • 28,948
  • 25
  • 99
  • 159
  • 1
    ExecuteScalar is the about as easy as it gets. There is a Linq to MSI provider if you want. – Christopher Painter May 08 '14 at 18:08
  • 1
    That's quite compact, and that's the best using managed code that you're likely to find - there are no "official" Windows Installer managed classes in the NET FW, and the underlying Win32 APIs might be more efficient because you avoid the entire mapping, but you probably don't want to go there just for a potential minor efficiency gain. – PhilDW May 08 '14 at 18:10
  • You should write your answer as an answer and, when you are satisfied with it, accept it. – Tom Blodget May 10 '14 at 02:54

1 Answers1

0

I got this far, and it works, but there is probably a more succint/efficient solution:

    public static IEnumerable<Tuple<string, string>> GetVersionInfo()
    {
        // Enumerate MSI files in C:\SetupFiles (or wherever)
        var setupFilesDir = ConfigurationManager.AppSettings["setupFilesDir"] ?? string.Empty;
        var di = new DirectoryInfo(setupFilesDir);
        var msiFiles = from msi in di.GetFiles("*.msi", SearchOption.AllDirectories)
            select msi.FullName;

        var tuples = new List<Tuple<string, string>>();

        foreach (var msi in msiFiles)
        {
            using (var db = new Database(msi, DatabaseOpenMode.ReadOnly))
            {
                var productVersion = (string)db.ExecuteScalar("SELECT `Value` FROM `Property` WHERE `Property` = 'ProductVersion'");
                var productName = (string)db.ExecuteScalar("SELECT `Value` FROM `Property` WHERE `Property` = 'ProductName'");

                tuples.Add(new Tuple<string, string>(productName, productVersion));
            }
        }

        return tuples;
    }
Mark Richman
  • 28,948
  • 25
  • 99
  • 159