如何使WIX在执行重大升级时不删除特定文件

how to make WIX not to delete particular files while performing major upgrade

我有两个组件,其中一个组件有 simple.exe,另一个组件在一个 MSI 中有 sample.dll。如果我安装 MSI,simple.exesample.dll 都会安装,并且在某个文件夹下的程序文件中它有两个 .exe.dll。如果我对整个 MSI 进行重大升级,它将用新文件夹替换在程序文件中创建的文件夹。那么,如果我为 .dll 文件更改/提供更新,如何使 wix 不替换 .exe,反之亦然。在为 .dll 提供更新的同时,我将使用一些 .bat 文件注册和注销密钥。请帮助我解决我需要有条件的重大升级,当我更改它们时将替换特定文件。

请找到以下代码:

product.wxs

<?xml version="1.0" encoding="UTF-8"?>
<?define ProductVersion = "0.0.4"?>
<?define ProductUpgradeCode = "d3170abf-b41c-4274-a3a0-85576052f35c"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Product Id="*" Name="saranSample" Language="1033" Version="$(var.ProductVersion)" Manufacturer="example" UpgradeCode="$(var.ProductUpgradeCode)">
    <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />

    <MajorUpgrade DowngradeErrorMessage="A newer version of product is already installed." AllowSameVersionUpgrades="no" AllowDowngrades="no" />
    <MediaTemplate EmbedCab="yes" />

<Upgrade Id="$(var.ProductUpgradeCode)">
  <UpgradeVersion Minimum="$(var.ProductVersion)" OnlyDetect="yes" Property="NEWERVERSIONDETECTED"/>
  <UpgradeVersion Minimum="0.0.0" Maximum="$(var.ProductVersion)" IncludeMinimum="yes" IncludeMaximum="no" Property="OLDERVERSIONBEINGUPGRADED"/>
</Upgrade>
<InstallExecuteSequence>
  <Custom Action="Filecleaner" After="InstallFinalize"></Custom>
</InstallExecuteSequence>
<Condition Message="A newer version of this software is already installed.">NOT NEWERVERSIONDETECTED</Condition>

    <Directory Id="TARGETDIR" Name="SourceDir">
        <Directory Id="ProgramFilesFolder">
    <Directory Id="INSTALLFOLDER" Name="saranSample_$(var.ProductVersion)">
      <Component Id="exeFiles" Guid="12345678-1234-1234-1234-222222222222">
        <File Id="exe" Source="$(sys.CURRENTDIR)npp.7.5.7.Installer.exe" KeyPath="yes"/>
      </Component>
      <Component Id="dllFiles" Guid="12345678-1234-1234-1234-222222222223">
        <File Id="dll" Source="$(sys.CURRENTDIR)saran.dll" KeyPath="yes"/>
      </Component>
    </Directory>
        </Directory>
    </Directory>

<Feature Id="ProductFeature" Title="saranSample" Level="1">
  <ComponentRef Id="exeFiles"/>
  <ComponentRef Id="dllFiles"/>
</Feature>

几件事:

  1. 为什么你有 MajorUpgrade 元素和 Upgrade 元素? MajorUpgrade 元素应该是您所需要的一切,所以它会让人感到困惑。

  2. 如 MajorUpgrade 文档中所述,升级的默认顺序是在 InstallValidate 之后。这是升级中的 "early",在安装新版本之前有效地卸载了所有旧产品。这就是文件被删除(通过卸载)然后再次安装的原因。

  3. 看来您的 MajorUpgrade Schedule 需要在 InstallExecute 之后。这将使用文件版本覆盖规则在旧文件上安装新产品文件。如果二进制文件的文件版本没有改变,那么它们将不会被替换。此类升级程序还要求您遵循组件规则:

http://robmensching.com/blog/posts/2003/10/18/component-rules-101/

https://docs.microsoft.com/en-us/windows/desktop/msi/what-happens-if-the-component-rules-are-broken

此外,您提到使用 bat 文件进行注册,这不是必需的。 WiX 具有用于在构建时提取注册信息的 Heat 工具。

我支持 Phil 的所有观点,也许我可以补充几点:

每个组件一个文件:确保每个安装的文件使用一个组件。我对所有文件类型都这样做,但你必须对二进制文件(dll 和 exe)这样做。这是为了使安装程序正确运行并符合 MSI 组件规则(Phil 提到)。我看到您有一个组件 EXE 文件和一个 DLL 文件。为您添加到设置中的每个二进制文件创建新组件,并将二进制文件设置为组件的关键路径。


Selective File Update: 如果你想做的是在磁盘上保留旧版本的文件,同时安装更高版本的新包版本文件以选择性的方式,那么我不知道除了尝试将文件设置为只读只读(我从来没有尝试过——老实说,不确定会发生什么)。您还可以 设置一个空白组件 GUID(这样的 GUID 也称为 "Little Bobby Void-GUID", just check this very official source)- 这将安装一个或多个文件,并且永远不会再触及或覆盖它们。还有一个组件标志可以设置为 如果组件密钥路径存在则从不覆盖(我通常将此与设置组件永久性结合使用)。也可以通过 post 处理您构建的 MSI 来 伪造文件的版本号 。一些第三方工具,如 Installshield 和 Advanced Installer 提供了一个 GUI,可以在编译之前执行此操作。

MSI 通常只会用设置中的较高版本二进制文件覆盖目标位置中的较低版本文件(这是根据默认 file versioning rules, which can be affected and modified by setting a custom value for REINSTALLMODE - 文件覆盖规则的 "modifier")。如果目标目的地中的文件与正在安装的文件版本相同或更高,则目标不会被覆盖(除非 REINSTALLMODE 设置为 amus - 强制覆盖 - 这是一个可怕的概念并且有很多副作用)。

如果不是很明显:如果您的某些文件没有针对给定版本进行更新,那么您只需包含那些包含在较旧设置中的文件的先前版本,它们就会显示在磁盘上以及您拥有新版本的文件。您无需执行任何操作即可实现这一点,它会自动产生魔力。


延迟排序的主要升级:菲尔的第 3 点是关于延迟排序的主要升级。与卸载所有文件然后重新安装它们不同,这样的重大升级有效地作为补丁安装,比较新旧软件包之间的差异,并仅删除在最新软件包中删除的文件。所有其他文件都保留在磁盘上或根据文件覆盖规则进行适当更新。


Preserve Settings Files:如果您有要在版本之间保留的设置文件,典型的问题是重大升级不会覆盖已更改的非版本文件(文件覆盖规则不会覆盖已更改的设置文件),但它会愉快地卸载并重新安装它们,除非它们在原始设置中被标记为永久 - 然后以原始状态重新安装它们使它们出现覆盖(当它们实际上被卸载并重新安装时 - 或者如果您愿意,可以恢复)。在我看来这是一种技术反模式。

上面提到的后期主要升级功能/方法可以防止此设置文件还原问题,因为文件不会被立即卸载和重新安装,而是在安装版本之间进行比较,如果它们是有更改的设置文件则保持不变 - 即前提是您已根据 MSI 组件规则正确实施所有内容,而这绝非易事。您需要 100% 的组件规则合规性以避免其他副作用,例如更新完成后丢失文件。 A short piece on the component rules.

或者,您可以将设置文件永久保存在 MSI 中。然后它们将永远不会被卸载,但也可能永远不会更新,也永远不会卸载,除非您创建自己的构造来这样做。


路径变量$(sys.CURRENTDIR) 是否适合您?我喜欢为自己定义我自己的路径变量,我可以轻松地重新提取到我想要的任何文件夹:

<?define SourceFiles= "C:\Projects\MySetup\Releases.0.0\"?>

<...>

<Icon Id="Icon.exe" SourceFile="$(var.SourceFiles)\MyApp.exe" />

部分链接: