检索使用 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 需要一个新的 未安装的实例。
如果有人能帮我解决这个问题,那就太好了 - 提前非常感谢!
这就是我的做法(使用引导程序)。我只是检查下一个可用的转换实例.您可以在注册表中签入密钥并获取下一个可用的密钥:
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);
}
}