检索使用C#在MSI内部定义的实例转换的ProductCodes

时间:2016-01-29 09:11:56

标签: c# wix windows-installer bootstrapper instance-transform

我在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>

我想通过我的引导程序检索实例转换 ProductCodes ,这是一个唤起.msi的C#windows应用程序。由于这些ProductCodes是在安装程序构建期间动态生成的,我认为我必须使用Microsoft.Deployment.WindowsInstaller程序集查询msi。但是,在使用ORCA检查.msi时,我似乎无法找到允许我查询实例转换的ProductCodes的表。属性表只有Product元素的ProductCode。

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

  

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

如果有人可以帮我解决这个问题,那就太棒了 - 非常感谢!

3 个答案:

答案 0 :(得分:0)

我就是这样做的(使用bootstrapper)。我只是检查下一个可用的转换实例。您可以在注册表中检查密钥并获取下一个密钥:

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

答案 1 :(得分:0)

ProductCode是该特定安装的注册表项容器的名称。这些可以在这里找到:

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。

答案 2 :(得分:0)

不是使用随机 guid,而是通过 InstanceIds 生成 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);
        }
    }