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

Ale*_*ese 13 c# registry windows-installer wix

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

例如

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

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

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

Ale*_*ese 35

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

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes
Run Code Online (Sandbox Code Playgroud)

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

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

连字符被剥离出来Guid然后进行一系列字符串反转.前8个字符相反,然后是接下来的4个字符,然后是下面的4个字符,然后字符串的其余部分以2个字符的形式反转.通常在反转字符串时我们需要注意确保正确处理控件和特殊字符(请参阅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();
    }
}
Run Code Online (Sandbox Code Playgroud)


Chr*_*ter 5

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

  • 顺便说一下,如果你想要最可靠的话,你可以使用这两种方法的*BOTH*并在那个例子中为你提供答案.如果有人愚蠢到删除MSI文件,他们可能会愚蠢到删除注册表项. (2认同)

Lui*_*sta 5

这是从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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @Alex Wiese代码对我来说很好用。此代码没有。但是,它会通过查找给定UpgradeCode的ProductCode来执行相反的查找。 (2认同)