Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I find the upgrade code for an installed application in C#?

I am using the C# wrapper for the Windows Installer API from the WIX Toolset. I use the ProductInstallation class to get information about the installed products such as the product code and product name.

For example

  • Product Name - "My Test Application"
  • Product Code - {F46BA620-C027-4E68-9069-5D5D4E1FF30A}
  • Product Version - 1.4.0

Internally this wrapper uses the MsiGetProductInfo function. Unfortunately this function does not return the product's upgrade code.

How can I retrieve the upgrade code for an installed application using C#?

like image 358
Alex Wiese Avatar asked Jul 29 '13 23:07

Alex Wiese


People also ask

Where is upgrade code in registry?

The registry key name is the upgrade code and the registry key value name is the product code. I can easily extract these values however the codes are stored in a different format. The red circle shows the formatted upgrade code, the blue circle shows the formatted product code when viewing it in regedit.exe .

How do you find MSI upgrade code?

In Windows 10 it seems you can hover over an MSI with the pointer and you get a pop-up with some MSI details. You then just click through the list until you find the right product and open the MSI and find the upgrade code in the Property table.


1 Answers

I have discovered the upgrade codes are stored in the following registry location.

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes

The registry key name is the upgrade code and the registry key value name is the product code. I can easily extract these values however the codes are stored in a different format. The red circle shows the formatted upgrade code, the blue circle shows the formatted product code when viewing it in regedit.exe.

Red circle is the formatted upgrade code, the blue circle the formatted product code

The hyphens are stripped out of the Guid and then a series of string reversals are done. The first 8 characters are reversed, then the next 4, then the following 4 and then the rest of the string is reversed in sets of 2 characters. Normally when reversing a string we need to take care in making sure control and special characters are handled correctly (see Jon Skeet's aricle here) but as we are, in this case, dealing with a Guid string we can be confident the string will be reversed correctly.

Below is the complete code I used to extract the upgrade code for a known product code from the registry.

internal static class RegistryHelper
{
    private const string UpgradeCodeRegistryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes";

    private static readonly int[] GuidRegistryFormatPattern = new[] { 8, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2 };

    public static Guid? GetUpgradeCode(Guid productCode)
    {
        // Convert the product code to the format found in the registry
        var productCodeSearchString = ConvertToRegistryFormat(productCode);

        // Open the upgrade code registry key
        var localMachine = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
        var upgradeCodeRegistryRoot = localMachine.OpenSubKey(UpgradeCodeRegistryKey);

        if (upgradeCodeRegistryRoot == null)
            return null;

        // Iterate over each sub-key
        foreach (var subKeyName in upgradeCodeRegistryRoot.GetSubKeyNames())
        {
            var subkey = upgradeCodeRegistryRoot.OpenSubKey(subKeyName);

            if (subkey == null)
                continue;

            // Check for a value containing the product code
            if (subkey.GetValueNames().Any(s => s.IndexOf(productCodeSearchString, StringComparison.OrdinalIgnoreCase) >= 0))
            {
                // Extract the name of the subkey from the qualified name
                var formattedUpgradeCode = subkey.Name.Split('\\').LastOrDefault();

                // Convert it back to a Guid
                return ConvertFromRegistryFormat(formattedUpgradeCode);
            }
        }

        return null;
    }

    private static string ConvertToRegistryFormat(Guid productCode)
    {
        return Reverse(productCode, GuidRegistryFormatPattern);
    }

    private static Guid ConvertFromRegistryFormat(string upgradeCode)
    {
        if (upgradeCode == null || upgradeCode.Length != 32)
            throw new FormatException("Product code was in an invalid format");

        upgradeCode = Reverse(upgradeCode, GuidRegistryFormatPattern);

        return Guid.Parse(upgradeCode);
    }

    private static string Reverse(object value, params int[] pattern)
    {
        // Strip the hyphens
        var inputString = value.ToString().Replace("-", "");

        var returnString = new StringBuilder();

        var index = 0;

        // Iterate over the reversal pattern
        foreach (var length in pattern)
        {
            // Reverse the sub-string and append it
            returnString.Append(inputString.Substring(index, length).Reverse().ToArray());

            // Increment our posistion in the string
            index += length;
        }

        return returnString.ToString();
    }
}
like image 192
Alex Wiese Avatar answered Oct 03 '22 07:10

Alex Wiese