检索使用 C# 在 MSI 内部定义的实例转换的产品代码

本文关键字:实例 转换 代码 定义 内部 MSI 检索 | 更新日期: 2023-09-27 18:32:21

我在WiX端定义了一组InstanceTransforms,如下所示:

<InstanceTransforms Property="INSTANCEID">
  <Instance ProductCode="*" UpgradeCode="$(var.UpgradeCode)" ProductName="ProductName $(var.VersionText) (64 bit)" Id="$(var.InstanceId)"/>
  <Instance ProductCode="*" UpgradeCode="$(var.UpgradeCode1)" ProductName="ProductName $(var.VersionText) (64 bit)" Id="$(var.InstanceId1)"/>
  <Instance ProductCode="*" UpgradeCode="$(var.UpgradeCode2)" ProductName="ProductName $(var.VersionText) (64 bit)" Id="$(var.InstanceId2)"/>
  <Instance ProductCode="*" UpgradeCode="$(var.UpgradeCode3)" ProductName="ProductName $(var.VersionText) (64 bit)" Id="$(var.InstanceId3)"/>
  <Instance ProductCode="*" UpgradeCode="$(var.UpgradeCode4)" ProductName="ProductName $(var.VersionText) (64 bit)" Id="$(var.InstanceId4)"/>
</InstanceTransforms>

我想通过我的引导程序检索实例转换的产品代码,引导程序是一个唤起.msi的 C# 窗口应用程序。由于这些产品代码是在安装程序构建期间动态生成的,因此我认为我必须使用 Microsoft.Deployment.WindowsInstaller 程序集查询 msi。但是,在检查 ORGA 的.msi时,我似乎找不到允许我查询实例转换的产品代码的表。属性表仅包含 Product 元素的产品代码。

是否可以检索实例转换的产品代码?当我尝试使用相同的安装程序进行相同的版本升级时,我确实在.msi的错误日志中看到与 InstanceTransform 对应的产品代码:

指定实例 {8F97345E-DDAD-4F03-9D17-820E929C59FE} 通过 已安装转换。MSINEWINSTANCE 需要一个新的 未安装的实例。

如果有人能帮我解决这个问题,那就太好了 - 提前非常感谢!

检索使用 C# 在 MSI 内部定义的实例转换的产品代码

这就是我的做法(使用引导程序)。我只是检查下一个可用的转换实例.您可以在注册表中签入密钥并获取下一个可用的密钥:

   public static string[] CheckRegKeys(Product product)
    {
        var productEnvironment = MapProductToString.MapProductEnvironment(product);
        var productKeys = new string[50];
        for (var i = 0; i < productKeys.Length; i++)
        {
            if (i < 9)
            {
                productKeys[i] = string.Format("{0}#0{1}", productEnvironment, i + 1);
                continue;
            }
            productKeys[i] = string.Format("{0}#{1}", productEnvironment, i + 1);
        }
        return productKeys;
    }
    public static string FindNextEnvironmentForInstallation(Product product)
    {
        var productName = MapProductToString.MapProductToRegistryName(product);
        var productKeys = CheckRegKeys(product);
        using (var componentsKey = Registry.LocalMachine.OpenSubKey(string.Format(@"Software'Wow6432Node'{0}", productName), false))
        {
            if (componentsKey == null)
            {
                Registry.LocalMachine.CreateSubKey(string.Format(@"Software'Wow6432Node'{0}", productName));
                return string.Format("I0{0}", 1);
            }
            var environments = componentsKey.GetSubKeyNames();
            if (environments.Length <= 0)
            {
                var result = string.Format("I0{0}", 1);
                return result;
            }
            for (var i = 0; i < productKeys.Length; i++)
            {
                if (environments.Length < i + 1)
                {
                    if (i < 9)
                    {
                        var result = string.Format("I0{0}", i + 1);
                        return result;
                    }
                    var result2 = string.Format("I{0}", i + 1);
                    return result2;
                }
            }
        }
        return null;
    }

接下来,将值发送到 msi :

 using (var p = new Process())
                {
                    var info = new ProcessStartInfo
                    {
                        WindowStyle = ProcessWindowStyle.Hidden,
                        FileName = @"C:'Windows'System32'cmd.exe",
                        Arguments = string.Format("/c msiexec /i '"{0}''{6}.msi'" PATHNAME='"{0}'" SSLCERTPATH='"{1}'"" +
                        " MSINEWINSTANCE=1 TRANSFORMS='":{2}'" /L*v '"{0}''{6}Log.txt'""
                        , XmlSettings.EnvironmentFolderPath, FindCertificates.SslCertPath, environment),
                        UseShellExecute = false,
                        CreateNoWindow = true
                    };
                    p.Start();
                    p.WaitForExit();
                }

产品代码是该特定安装的注册表项容器的名称。这些可在此处找到:

Computer'HKEY_LOCAL_MACHINE'SOFTWARE'Microsoft'Windows'CurrentVersion'Uninstall

因此,您可以编写自定义操作来扫描注册表内容以确定正确的基本项。

实际上,我在安装过程中使用如下内容将许多特殊实例信息写入此安装密钥:

<Component Id="cmpMIValues" Permanent="no" Guid="YOUR-GUID-HERE" Win64 ="yes" Directory ="TARGETDIR" MultiInstance="yes" >
  <Condition><![CDATA[TRANSFORMS]]></Condition>
  <RegistryKey Root="HKLM" Key="SOFTWARE'Microsoft'Windows'CurrentVersion'Uninstall'[ProductCode]" ForceCreateOnInstall="yes">
    <RegistryValue Type="string" Name="ProductCode" Value="[ProductCode]" KeyPath="no" />
    <RegistryValue Type="string" Name="UpgradeCode" Value="[UpgradeCode]" KeyPath="no" />
    <RegistryValue Type="string" Name="InstallTime" Value="[INSTALLTIME]" KeyPath="no" />
    <RegistryValue Type="string" Name="InstanceId" Value="[INSTANCEID]" KeyPath="no" />
    <RegistryValue Type="string" Name="InstanceName" Value="[INSTANCENAME]" KeyPath="no" />
  </RegistryKey>
</Component>

(上述某些变量是由自定义操作创建的。

另一种替代方法是简单地使用固定的 GUID 而不是"*"。然后,可以在自定义操作中使用查找表来确定特定实例的正确 GUID。

而不是使用随机 guid,只需通过 InstanceId 生成 guid。下面是一个示例:

    public static Guid GenerateGuidByString(string input)
    {
        using (var md5 = MD5.Create())
        {
            var hashBytes = Encoding.Default.GetBytes(input);
            var hash = md5.ComputeHash(hashBytes);
            return new Guid(hash);
        }
    }