0

I'd like to get list of all Upgrade codes of all installed products on Windows box. The question is: is there a dedicated MSI function to address this request?

There is MsiEnumProducts() that enumerates all installed products and MsiEnumRelatedProducts() to enumerate all products for the given Upgrade code. But I can't find a function to get all Upgrade codes in the system.

The workaround I can imagine is use MsiEnumProducts() to get list of all installed products, open each with MsiOpenProduct() function and read "UpgradeCode" property with MsiGetProductProperty(). But this should be very slow due to multiple MsiOpenProduct() calls.

Alexey Semenyuk
  • 674
  • 4
  • 9
  • Just to elaborate, if you wanted to stay with C++ you wouldn't need MsiOpenProduct(). You enumerate all the products, then MsiGetProductInfo() can get you the local package location, then you open that with MsiOpenDatabase() and query the Property for the UpgradeCode property. That seems to be what Chis's managed code does anyway. – PhilDW Apr 11 '15 at 23:02
  • PhilDW, thank for explaining how to handle this with MsiOpenDatabase! I suspect that if I stop using MsiOpenProduct() and start using MsiOpenDatabse() api to read product properties this will speed up the process and also MSI will no create a log file for every access of the product. Now, every time I call MsiOpenProduct(), MSI creates a log file. I know, this can be turned off through Registry keys, but still seems like MsiOpenProduct is overkill for reading properties from installed products. – Alexey Semenyuk Apr 23 '15 at 13:14
  • You can use WMI to retrieve all upgrade codes as described here: [**How can I find the Upgrade Code for an installed MSI file?**](https://stackoverflow.com/questions/46637094/how-can-i-find-the-upgrade-code-for-an-installed-msi-file/46637095#46637095). – Stein Åsmul Oct 26 '17 at 20:41

1 Answers1

2

I believe MsiEnumProducts loop with MsiOpenProduct and then MsiGetProductProperty is the correct official sequence. If you really need faster and are willing to bypass the API's you could read the registry directly at HKCR\Installer\UpgradeCodes. You'll have to reverse the Darwin Descriptors though. This isn't technically supported but the reality is these keys have been there for 16 years and MSFT has been doing ZERO development on The Windows Installer. Ok, maybe they updated the version number and removed ARM support in Windows 10 LOL.

FWIW, I like to use C# not C++ but the concept is the same. The following snippet ran on my developer machine in about 2 seconds.

using System;
using Microsoft.Deployment.WindowsInstaller;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            foreach (var productInstallation in ProductInstallation.AllProducts)
            {
                using(var database = new Database(productInstallation.LocalPackage, DatabaseOpenMode.ReadOnly))
                {
                    Console.WriteLine(database.ExecutePropertyQuery("UpgradeCode"));
                }
            }
        }
    }
}

According to the DTF documentation, ProductInstallation.AllProducts uses MsiEnumProducts. The Database class constructor is using MsiOpenDatabase and ExecutePropertyQuery is a higher level call that basically abstracts doing a SELECT Value from Property WHERE Property = '%s'. So it'll be calling APIs to create, execute and fetch results from views. All these classes implement IDisposable to call the correct APIs to free resources also.

Ya... that's why I love managed code. :)

Community
  • 1
  • 1
Christopher Painter
  • 54,556
  • 6
  • 63
  • 100
  • 2 seconds is very nice. My bad experience with MSI API is probably because I use MsiOpenProduct to read product properties. I should try use MsiOpenDatabase instead. – Alexey Semenyuk Apr 23 '15 at 13:12