15

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

例如

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

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

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

4

6 回答 6

40

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

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

注册表项名称是升级代码,注册表项值名称是产品代码。我可以轻松提取这些值,但是代码以不同的格式存储。红圈为格式化后的升级码,蓝圈为在查看时格式化的产品码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();
    }
}
于 2013-07-29T23:43:04.810 回答
7

这是从 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);
    }
}
于 2016-03-02T16:57:08.870 回答
5

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

于 2013-07-30T01:43:22.427 回答
3

只是发表评论,以防将来对任何人都派上用场!

如果您只有 GUID 或代码可用,可以使用以下站点在两者之间进行转换:

https://daysoff.io/flipguid

希望这可以节省一些未来的头痛!

于 2020-01-28T18:05:00.737 回答
1

这是一种以注册表格式格式化 GUID 的更简单的方法(基本上只是原始字节表示)

首先是获取原始字节:

var guidBytes = Guid.Parse(productCode).ToByteArray();

然后只需翻转 BitConverter.ToString() 结果的字节顺序

var convertedString = String.Concat(BitConverter.ToString(guidBytes).Split('-').SelectMany(s => s.Reverse()));
于 2021-02-22T20:22:18.830 回答
0

这是您的帮助程序,其修改方式也适用于 .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<Guid> GetUpgradeCodes()
    {
        var list = new List<Guid>();

        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;
    }
}
于 2017-11-07T09:24:48.983 回答