如何让 Wix Heat.exe 保留自定义文件 ID?
Posted
技术标签:
【中文标题】如何让 Wix Heat.exe 保留自定义文件 ID?【英文标题】:How to make Wix Heat.exe retain custom file id? 【发布时间】:2019-07-23 11:21:38 【问题描述】:我有一个 ASP.NET 核心应用程序,我正在为其创建 WIX 安装程序。我正在使用 Heat 生成所有文件:
<!-- Remove read-only attribute -->
<Exec Command="attrib -R %(ProjectReference.Filename).wxs" Condition="'%(ProjectReference.WebProject)'=='True'" />
<ItemGroup>
<LinkerBindInputPaths Include="%(ProjectReference.RootDir)%(ProjectReference.Directory)bin\publish\" />
</ItemGroup>
<!-- Generate a WiX installer file using Heat Tool -->
<HeatDirectory OutputFile="%(ProjectReference.Filename).wxs"
Directory="%(ProjectReference.RootDir)%(ProjectReference.Directory)bin\publish\"
DirectoryRefId="INSTALLFOLDER"
ComponentGroupName="%(ProjectReference.Filename)"
AutogenerateGuids="True"
SuppressCom="True"
SuppressFragments="True"
SuppressRegistry="True"
ToolPath="$(WixToolPath)"
Condition="'%(ProjectReference.WebProject)'=='True'" />
在我的WebApp.sxs
文件中放入如下条目:
<Component Id="cmp64BF6D207C595218157C321E631ED310" Guid="*">
<File Id="myExe" KeyPath="yes" Source="SourceDir\MyExe.exe" />
</Component>
问题是,我修改了Id
属性,这样我就可以绑定到Product.wxs
中的版本了:
<Product Id="*"
Name="..."
Manufacturer="..."
Version="!(bind.fileVersion.myExe)"
Language="1033"
UpgradeCode="143521a5-99df-4594-9d71-b028cddb9ed8">
我怎样才能让 heat 为这个文件保持相同的 ID?但同时添加任何新文件?
【问题讨论】:
Please check this link for now. 您可以通过应用 XSLT(HeatDirectory Transforms="MyTransform.xslt"
,this answer 中给出 XSLT 的示例)来删除或修改“MyExe.exe”的组件。
我认为你在倒退。不要替换 heat 生成的 ID,而是将 .wxs 文件中的“myExe”替换为 heat 生成的 ID。 Heat 每次都会生成相同的 ID。
【参考方案1】:
基本思路是从 Heat 输出中过滤掉“MyExe.exe”的组件,然后手动将其添加回主 WiX 创作,我们可以在其中指定常量File/@Id
属性。
可以通过将 XSLT 文件传递给 Heat 来完成过滤:
<HeatDirectory Transforms="$(ProjectDir)RemoveMyExeComponent.xslt" ... />
RemoveMyExeComponent.xslt 可能如下所示(来自this answer 的想法,但针对当前问题进行了简化)。在此文件中,将MyExe.exe
替换为您的EXE 的实际名称,并将- 8
替换为文件名的长度,减一。
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:wix="http://schemas.microsoft.com/wix/2006/wi"
xmlns="http://schemas.microsoft.com/wix/2006/wi"
version="1.0"
exclude-result-prefixes="xsl wix">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<xsl:strip-space elements="*" />
<!--
Find the component of the main EXE and tag it with the "ExeToRemove" key.
Because WiX's Heat.exe only supports XSLT 1.0 and not XSLT 2.0 we cannot use `ends-with( haystack, needle )` (e.g. `ends-with( wix:File/@Source, '.exe' )`...
...but we can use this longer `substring` expression instead (see https://github.com/wixtoolset/issues/issues/5609 )
-->
<xsl:key
name="ExeToRemove"
match="wix:Component[ substring( wix:File/@Source, string-length( wix:File/@Source ) - 8 ) = 'MyExe.exe' ]"
use="@Id"
/> <!-- In this expression "-8" is the length of "MyExe.exe" - 1 because XSLT uses 1-based indexes, not 0-based indexes. -->
<!-- By default, copy all elements and nodes into the output... -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
<!-- ...but if the element has the "ExeToRemove" key then don't render anything (i.e. removing it from the output) -->
<xsl:template match="*[ self::wix:Component or self::wix:ComponentRef ][ key( 'ExeToRemove', @Id ) ]" />
</xsl:stylesheet>
现在我们已经删除了生成的 EXE 组件,我们可以手动将其添加回 WiX 创作,让我们完全控制所有属性:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="WixHeatConstantExeFileId" Language="1033" Version="!(bind.fileVersion.myExe)" Manufacturer="zett42" UpgradeCode="PUT-GUID-HERE">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate />
<Feature Id="ProductFeature" Title="WixHeatConstantExeFileId" Level="1">
<ComponentGroupRef Id="ProductComponents" />
<ComponentGroupRef Id="HarvestedComponents" />
</Feature>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="WixHeatConstantExeFileId" />
</Directory>
</Directory>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<!-- Manually add back the EXE component that was filtered out from Heat output. -->
<Component Id="myExe" Guid="*">
<File Id="myExe" KeyPath="yes" Source="SourceDir\MyExe.exe" />
</Component>
</ComponentGroup>
</Product>
</Wix>
在我的测试项目中就像一个魅力!
【讨论】:
【参考方案2】:在 Heat 中使用 SuppressRootDirectory="true"
(-srd 选项)。这样 Heat 会忽略完整路径,只使用文件名来生成 ID。只要文件名相同,ID就永远相同。
然后,不要替换 heat 生成的 ID,而是将 .wxs 文件中的“myExe”替换为 heat 生成的 ID。
看 Wix Harvest: Same Component/File ID when files are in different folders
【讨论】:
以上是关于如何让 Wix Heat.exe 保留自定义文件 ID?的主要内容,如果未能解决你的问题,请参考以下文章
WiX 安装程序:使用 xslt 和 heat.exe 如何在找到父/子匹配后更改父 ID 的值?
Wix Installer Heat.exe 错误 参数“exePath”无效