Search code examples
wixwindows-installerwix3orca

How to extract data (file count) from MSI "File" Table


In our build process there is currently the potential for non-code based files (such as image files) to be added to our web project, but not included in the MSI installer built by WiX.

To help prevent this, I want to perform the following in the AfterBuild target for our WiX project:

  • Get a count of all files built (output from web deployment project)
  • Get a count of all files built into MSI (from "File" table in MSI)
  • Compare counts and fail build if they don't match

If I fire up Orca I can easily see the File table and count, but I don't know how to automate this from MSBuild. Is there some API or other mechanism to get this information out of an MSI?

I don't mind writing a custom MSBuild task to extract the MSI File table count.


Solution

  • Create a new visual studio project, add a reference to c:\windows\system32\msi.dll and use the following code to read the number of files in a msi file:

    Type installerType = Type.GetTypeFromProgID("WindowsInstaller.Installer");
    var installer =
       (WindowsInstaller.Installer)Activator.CreateInstance(installerType);
    var msi = installer.OpenDatabase(@"path\to\some\file.msi", 0);
    var fileView = msi.OpenView("SELECT FileName FROM File");
    fileView.Execute(null);
    int fileCount = 0;
    while (fileView.Fetch() != null)
    {
       fileCount++;
    }
    Console.WriteLine(fileCount);
    

    This code uses the WindowsInstaller.Installer COM object, which is the entry-point for the windows installer automation API. Take a look at the complete reference documentation.

    edit: apparently wix comes with managed assemblies (in C:\program files\Windows Installer XML v3\sdk) which wrap msi.dll. I guess this is what Rob is referring to by "DTF" in his answer. Using the types in the Microsoft.Deployment.WindowsInstaller assembly and namespace would simplify the code sample to this:

    var database = new Database(@"\path\to\some\file.msi");
    var list = database.ExecuteQuery("SELECT FileName FROM File");
    Console.WriteLine(list.Count);