如何在C#中找到已安装应用程序的升级代码?

我正在使用WIX工具集中的Windows Installer API的C#包装器。 我使用ProductInstallation类来获取有关已安装产品的信息,例如产品代码和产品名称。

例如

  • 产品名称 – “我的测试应用”
  • 产品代码 – {F46BA620-C027-4E68-9069-5D5D4E1FF30A}
  • 产品版本 – 1.4.0

在内部,这个包装器使用MsiGetProductInfo函数。 不幸的是,此function不会返回产品的升级代码。

如何使用C#检索已安装应用程序的升级代码?

我发现升级代码存储在以下注册表位置。

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

注册表项名称是升级代码,注册表项值名称是产品代码。 我可以轻松提取这些值,但代码以不同的格式存储。 红色圆圈显示格式化的升级代码,蓝色圆圈显示在regedit.exe查看时格式化的产品代码。

红色圆圈是格式化的升级代码,蓝色圆圈是格式化的产品代码

连字符从Guid中删除,然后进行一系列字符串反转。 前8个字符相反,然后是接下来的4个字符,然后是4个字符,然后字符串的其余部分以2个字符的forms反转。 通常在反转字符串时我们需要注意确保正确处理控件和特殊字符( 请参阅Jon Skeet的文章 ),但正如我们所知 ,在这种情况下,处理Guid字符串我们可以确信字符串将被反转正确。

下面是我用于从注册表中提取已知产品代码的升级代码的完整代码。

 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(); } } 

InstallPackage类有一个名为LocalPackage的属性。 您可以使用它来查询在C:\ Windows \ Installer中缓存的MSI数据库,并获取您可能想要了解的任何信息。

这是从UpgradeCode获取ProductCode的相反方法。 可能对某人有用。

 using Microsoft.Win32; using System; using System.IO; using System.Linq; using System.Text; 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? GetProductCode(Guid upgradeCode) { // Convert the product code to the format found in the registry var productCodeSearchString = ConvertToRegistryFormat(upgradeCode); // Open the upgrade code registry key var upgradeCodeRegistryRoot = GetRegistryKey(Path.Combine(UpgradeCodeRegistryKey, productCodeSearchString)); if (upgradeCodeRegistryRoot == null) return null; var uninstallCode = upgradeCodeRegistryRoot.GetValueNames().FirstOrDefault(); if (string.IsNullOrEmpty(uninstallCode)) { return null; } // Convert it back to a Guid return ConvertFromRegistryFormat(uninstallCode); } private static string ConvertToRegistryFormat(Guid code) { return Reverse(code, GuidRegistryFormatPattern); } private static Guid ConvertFromRegistryFormat(string code) { if (code == null || code.Length != 32) throw new FormatException("Product code was in an invalid format"); code = Reverse(code, GuidRegistryFormatPattern); return Guid.Parse(code); } 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(); } static RegistryKey GetRegistryKey(string registryPath) { var hklm64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64); var registryKey64 = hklm64.OpenSubKey(registryPath); if (((bool?)registryKey64?.GetValueNames()?.Any()).GetValueOrDefault()) { return registryKey64; } var hklm32 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32); return hklm32.OpenSubKey(registryPath); } } 

这里是你的帮助程序修改,它也适用于.Net3.5 32位应用程序。 他们需要特殊的处理,因为.net 3.5没有意识到注册表在32位和64位之间被分割。 我的解决方案是仅使用To64BitPath浏览64位部分。 还有一个很棒的教程,使用DllImports: https : //www.rhyous.com/2011/01/24/how-read-the-64-bit-registry-from-a-32-bit-application-或相反亦然/

 class RegistryHelper { private const string UpgradeCodeRegistryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes"; private const string UninstallRegistryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"; private static readonly int[] GuidRegistryFormatPattern = new[] { 8, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2 }; public static string To64BitPath(string path) { return path.Replace("SOFTWARE\\Microsoft", "SOFTWARE\\WOW6432Node\\Microsoft"); } private static RegistryKey GetLocalMachineRegistryKey(string path) { return RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, string.Empty).OpenSubKey(path); } public static IEnumerable GetUpgradeCodes() { var list = new List(); var key = GetRegistryKey(UpgradeCodeRegistryKey); if (key != null) { list.AddRange(key.GetSubKeyNames().Select(ConvertFromRegistryFormat)); } return list; } public static Guid? GetProductCode(Guid upgradeCode) { // Convert the product upgradeCode to the format found in the registry var productCodeSearchString = ConvertToRegistryFormat(upgradeCode); // Open the upgradeCode upgradeCode registry key var upgradeCodeRegistryRoot = GetRegistryKey(Path.Combine(UpgradeCodeRegistryKey, productCodeSearchString)); if (upgradeCodeRegistryRoot == null) return null; var uninstallCode = upgradeCodeRegistryRoot.GetValueNames().FirstOrDefault(); if (string.IsNullOrEmpty(uninstallCode)) { return null; } // Convert it back to a Guid return ConvertFromRegistryFormat(uninstallCode); } public static string ConvertToRegistryFormat(Guid code) { return Reverse(code, GuidRegistryFormatPattern); } private static Guid ConvertFromRegistryFormat(string code) { if (code == null || code.Length != 32) throw new FormatException("Product upgradeCode was in an invalid format"); code = Reverse(code, GuidRegistryFormatPattern); return new Guid(code); } 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(); } static RegistryKey GetRegistryKey(string registryPath) { var registryKey64 = GetLocalMachineRegistryKey(To64BitPath(registryPath)); if (((bool?)registryKey64?.GetValueNames()?.Any()).GetValueOrDefault()) { return registryKey64; } return GetLocalMachineRegistryKey(registryPath); } public static Guid? GetUpgradeCode(Guid productCode) { var productCodeSearchString = ConvertToRegistryFormat(productCode); var upgradeCodeRegistryRoot = GetRegistryKey(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 upgradeCode 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; } }