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
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#?
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 .
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.
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
.
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();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With