获取已安装 Msi 的产品代码

Posted

技术标签:

【中文标题】获取已安装 Msi 的产品代码【英文标题】:Get Product Code of installed Msi 【发布时间】:2011-06-14 14:35:55 【问题描述】:

我有一个 C# 程序,我必须在其中获取已安装 msi 的产品代码。我只有 msi 名称作为输入。这可以通过编程方式完成吗?

【问题讨论】:

您是否使用 Regedit 在安装了软件的机器上搜索注册表? 任何带有完整源代码的最终解决方案? DTF (Deployment Tools Foundation) 是一组随 WiX 下载提供的 .NET 类,它可以让您轻松访问和修改 MSI 文件,而无需任何 COM 互操作或笨拙。有关更多上下文,请参阅提供的链接中的信息。 Download WiX 获取 DTF 组件。 【参考方案1】:

this question 的答案有帮助吗?他们想获得产品名称,但也许它也适用于产品代码?

编辑 如果您没有 MSI 文件本身来访问数据库(如上面指向另一个问题的链接所建议的那样),您可以尝试在以下注册表路径中搜索您的 MSI 文件的名称:

HKEY_CLASSES_ROOT\Installer\Products\*\SourceList 

Products 分支下有很多条目。它们中的每一个都是一个产品密钥。每个分支都应包含SourceList 节点,而该节点又应包含值PackageName。该值包含 MSI 文件的名称。

所以我要做的是:

for each key in Products

    open SourceList subkey
    read PackageName value
    if name equals my msi file name
    
        return key-name formatted as GUID
    

【讨论】:

但是,键名实际上是产品代码的加扰版本。有关从 ProductCode 转换为注册表项格式 (***.com/questions/1881643/…) 的帮助,请参阅此处。您只需要反转它来解扰密钥名称。【参考方案2】:

这是我用来获取任何 MSI 的 UninstallString 的代码。

private string GetUninstallString(string msiName)

    Utility.WriteLog("Entered GetUninstallString(msiName) - Parameters: msiName = " + msiName);
    string uninstallString = string.Empty;
    try
    
        string path = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\S-1-5-18\\Products";

        RegistryKey key = Registry.LocalMachine.OpenSubKey(path);

        foreach (string tempKeyName in key.GetSubKeyNames())
        
            RegistryKey tempKey = key.OpenSubKey(tempKeyName + "\\InstallProperties");
            if (tempKey != null)
            
                if (string.Equals(Convert.ToString(tempKey.GetValue("DisplayName")), msiName, StringComparison.CurrentCultureIgnoreCase))
                
                    uninstallString = Convert.ToString(tempKey.GetValue("UninstallString"));
                    uninstallString = uninstallString.Replace("/I", "/X");
                    uninstallString = uninstallString.Replace("MsiExec.exe", "").Trim();
                    uninstallString += " /quiet /qn";
                    break;
                
            
        

        return uninstallString;
    
    catch (Exception ex)
    
        throw new ApplicationException(ex.Message);
    

这将给出如下结果:

MsiExec.exe /I6BB09011-69E1-472F-ACAD-FA0E7DA3E2CE

从此字符串中,您可以获取大括号 中的子字符串,即6BB09011-69E1-472F-ACAD-FA0E7DA3E2CE。我希望这可能是产品代码。

【讨论】:

如果您在 64 位机器上使用此代码并且您的应用程序是 32 位,这将失败,因为它会将您重定向到软件中的 Wow6432Node。请改用RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64).OpenSubKey(path)【参考方案3】:

有一种最快速、最简单的方法 - 将 WMI 与条件查询字符串一起使用。

    public string GetProductCode(string productName)
    
        string query = string.Format("select * from Win32_Product where Name='0'", productName);
        using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(query))
        
            foreach (ManagementObject product in searcher.Get())
                return product["IdentifyingNumber"].ToString();
        
        return null;
    

【讨论】:

关于ManagementObjectSearcher【参考方案4】:

此代码直接从 MSI 文件中获取产品代码。所以这允许在不安装文件的情况下读取代码。

class MsiHandle : SafeHandleMinusOneIsInvalid

    public MsiHandle()
        : base(true)
     

    protected override bool ReleaseHandle()
    
        return NativeMethods.MsiCloseHandle(handle) == 0;
    


class NativeMethods

    const string MsiDll = "Msi.dll";

    [DllImport(MsiDll, CharSet = CharSet.Unicode, ExactSpelling = true)]
    public extern static uint MsiOpenPackageW(string szPackagePath, out MsiHandle product);

    [DllImport(MsiDll, ExactSpelling=true)]
    public extern static uint MsiCloseHandle(IntPtr hAny);

    [DllImport(MsiDll, CharSet = CharSet.Unicode, ExactSpelling = true)]
    static extern uint MsiGetProductPropertyW(MsiHandle hProduct, string szProperty, StringBuilder value, ref int length);


    [DllImport(MsiDll, ExactSpelling = true)]
    public static extern int MsiSetInternalUI(int value, IntPtr hwnd);

    public static uint MsiGetProductProperty(MsiHandle hProduct, string szProperty, out string value)
    
        StringBuilder sb = new StringBuilder(1024);
        int length = sb.Capacity;
        uint err;
        value = null;
        if(0 == (err = MsiGetProductPropertyW(hProduct, szProperty, sb, ref length)))
        
            sb.Length = length;
            value = sb.ToString();
            return 0;
        

        return err;
    


static class Program

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static int Main(string[] args)
    
        string msiFile = args[0];

        NativeMethods.MsiSetInternalUI(2, IntPtr.Zero); // Hide all UI. Without this you get a MSI dialog

        MsiHandle msi;
        uint err;
        if (0 != (err = NativeMethods.MsiOpenPackageW(args[0], out msi)))
        
            Console.Error.WriteLine("Can't open MSI, error 0", err);
            return 1;
        

        // Strings available in all MSIs
        string productCode;
        using (msi)
        
           if (0 != NativeMethods.MsiGetProductProperty(msi, "ProductCode", out productCode))
                throw new InvalidOperationException("Can't obtain product code");

           Console.WriteLine(productCode);
           return 0;
        
    

http://ankhsvn.open.collab.net/svn/ankhsvn/trunk/src/tools/Ankh.Chocolatey/ 上的 Subversion 中的完整示例 使用用户名 'guest' 而没有密码。

【讨论】:

【参考方案5】:
private static bool GetUninstallString(string ProductName)

    try
    
        RegistryKey localKey = RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, RegistryView.Registry64);
        var key = localKey.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall") ??
            localKey.OpenSubKey(
                @"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall");

        if (key == null)
                return false;

        return key.GetSubKeyNames()
                .Select(keyName => key.OpenSubKey(keyName))
                .Select(subkey => subkey.GetValue("DisplayName") as string)
                .Any(displayName => displayName != null && displayName.Contains(ProductName));
     
     catch
     
         // Log message                  
         return false;
     
 

这对于按产品名称搜索字符串非常有用

【讨论】:

以上是关于获取已安装 Msi 的产品代码的主要内容,如果未能解决你的问题,请参考以下文章

如何找到已安装 MSI 设置的产品 G​​UID?

在 Wix 中获取生成的 ProductCode 作为变量

如何在自定义操作 DLL (MSI/Wix) 中获取“INSTALLED”属性?

我可以在linux中获取msi文件的源代码并添加一些条件吗?

我可以在linux中获取msi文件的源代码并添加一些条件吗?

如何获取安装目录?