【问题标题】:Windows installer deletes versioned file during product upgrade, instead of downgrading itWindows 安装程序在产品升级期间删除版本控制文件,而不是降级它
【发布时间】:2010-11-19 17:01:46
【问题描述】:

我们使用wix 来创建我们的设置。对于升级,我们使用this answer by Rob Mensching 中演示的主要升级。 (在较新的 wix 版本中,您可以使用 MajorUpgrade 元素。)这通常效果很好。删除旧产品,然后安装新产品。

但是,显然上述并不完全等同于手动卸载旧产品,然后手动安装新产品。

例如考虑以下场景:

  • 我们的产品1.0版本发布,包含第三方dll的5.0版本
  • 我们的产品1.1版本发布,包含相同第三方dll的5.1版本
  • 我们的产品发布了1.2版本,再次降级到第三方dll的5.0版本,因为我们发现新版本引入的问题比解决的问题多。

显然使用上面链接的 wix 升级逻辑,当从 1.1 版升级到 1.2 版时,第 3 方 dll 将消失。需要维修才能恢复它。

是否有另一种升级方式适用于这种情况?我想我正在寻找的是允许组件降级的升级逻辑,其行为就像手动卸载旧产品然后手动安装新产品一样。

【问题讨论】:

    标签: wix windows-installer


    【解决方案1】:

    我们还遇到了这个问题,即在重大升级时没有重新安装低版本的 DLL。我觉得奇怪的是安装程序会根据现有文件的版本决定安装哪些文件,然后完全卸载所有内容,但仍然只安装之前确定要安装的文件卸载旧产品。这似乎是 Windows Installer 中的一个错误...

    为解决此问题,我们将 RemoveExistingProducts 操作移至 CostFinalize 操作上方。

    我知道documentation on MSDN 建议将RemoveExistingProducts 放在InstallValidate 之后,我不确定将它放在InstallValidate 操作之前是否会对小升级产生任何负面影响,但我们决定只对我们的产品进行重大升级,因此该解决方案似乎对我们有用。

    【讨论】:

    • 你是怎么做到的?如果我尝试在我们的安装程序上执行此操作,我会收到此错误:ICE27: 'RemoveExistingProducts' Action in InstallExecuteSequence table in wrong place. Current: Costing, Correct: Execution
    • 我们正在使用 InstallShield 来构建我们的安装,它允许我们按此顺序执行操作。也许您用于构建安装程序的任何应用程序都会对特定顺序进行某种验证检查,并且不允许此顺序。如果是这样,可能有一些方法可以禁用此检查,但我不知道是否会推荐。
    • 快速谷歌搜索让我知道 ICE 代表内部一致性评估器,它必须在安装程序构建期间运行。为了解决这个问题,您可以使用正常序列构建安装程序,然后使用 Instedit 之类的工具打开 MSI,并手动更改 InstallExecuteSequence 表中的序列值。
    • @jalf 实际上正在查看序列。我们也将RemoveExistingProducts 移到了CostInitialize 之前。在成本计算操作之间插入它可能无效。
    • @jalf 好的,这篇 MSDN 文章解释了 RemoveExistingProducts 操作的有效位置。 msdn.microsoft.com/en-us/library/aa371197(v=vs.85).aspx。 -- 选项之一是“在 InstallValidate 操作和 InstallInitialize 操作之间。在这种情况下,安装程序会在安装新应用程序之前完全删除旧应用程序。这是一个低效的操作位置,因为所有重复使用的文件都必须重新复制。'
    【解决方案2】:

    此类行为通常与RemoveExistingProducts 的排序有关。如果它发生得足够晚,Windows Installer 会发现机器上有更新版本的 .dll,因此 1.2 版不需要安装它。但是,当 RemoveExistingProducts 导致删除 .dll 时,没有任何东西可以将其恢复。

    要尝试的事情包括更改 RemoveExistingProducts 的顺序,以及谎报 1.2 包中 .dll 的版本(报告版本号高于错误的版本号)。后者的缺点是对修复或修补的影响很差,因为 .dll 看起来总是过时。

    【讨论】:

    • InstallFinalize 之后调度RemoveExistingProducts 有另一个不良影响:dll 保持在较新的版本。我还考虑过在设置中覆盖文件版本,但这会带来不幸的后果,每次 DLL 更改时我都必须手动更新该虚假版本号:-(
    • 为什么要不断更新它?是不是只有一个不起作用的特定版本,并且您正在覆盖它,以便重新安装它之前的版本?也许谎言它正是新版本并设置 REINSTALLMODE=emus。
    • 成本核算过程中已经出现问题。 此时,MSI 发现存在较新版本,并决定不安装新文件。您将在日志中看到Disallowing installation of component: XXX since the same component with higher versioned keyfile exists。因此,您必须成本计算之前安排 RemoveExistingProducts。我看不出在不违反 ICE27 和 ICE63 的情况下如何做到这一点。
    • 对于它的价值,一些 ICE 似乎过于热心了。也就是说,任何需要根据文件版本降级文件的情况,至少使用 Windows 安装程序,都会产生影响。
    • 有关这方面的精彩文章,请参阅 Aaron Stebner 最近的博文:Why Windows Installer removes files during a major upgrade if they go backwards in version numbers
    【解决方案3】:

    尝试提前安排RemoveExistingProducts,紧随InstallValidate,并将REINSTALLMODE属性的值更改为amus。这样,在复制新产品中的任何文件之前,旧产品将被完全删除,a 模式将强制重新安装文件。

    【讨论】:

    • 这个答案没有帮助。建议在InstallValidate 之后放置RemoveExistingProducts 并没有帮助。将REINSTALLMODE 设置为amus 非常危险,因为a 会强制重新安装所有文件,包括您可能不想降级的共享系统文件。例如,假设您有一些安装系统运行时 DLL 的合并模块。这些 DLL 稍后会由供应商使用安全补丁进行修补。当您的软件包稍后使用amus 安装时,您将覆盖来自供应商的更新、更安全的 DLL。
    • @JamesJohnston 那么你会怎么做呢?假设您没有 有安装系统范围 DLL 的合并模块,amus 确实是我能找到的唯一可行的解​​决方案。 (但我很想听听更好的解决方案)
    • @jalf:经过大量搜索,我得出结论,REINSTALLMODE=amus 是唯一明智的解决方案。但是,只有在您的安装中没有共享组件时才能使用它。
    • 如果你在产品之间共享组件,amus 会毁了你的生活。
    【解决方案4】:

    这是次优的,但我通过重命名第三方 dll 并更改 .wxs 文件中与其关联的组件节点上的 GUID 解决了同样的问题。

    【讨论】:

      【解决方案5】:

      多年后,这个帖子帮助我朝着正确的方向前进。 RemoveExisitingProducts 在成本计算之前移动的完整性示例:

      <Upgrade Id="UPGRADE-GUID-HERE">
          <UpgradeVersion OnlyDetect="no" Property="UPGRADABLEFOUND"
              Maximum="$(var.ProductVersion)" IncludeMaximum="yes" />
          <UpgradeVersion OnlyDetect="yes" Property="NEWERFOUND"
              Minimum="$(var.ProductVersion)" IncludeMinimum="no" />
      </Upgrade>
      
      <InstallExecuteSequence>
          <Custom Action="NoDowngrade" After="FindRelatedProducts">NEWERFOUND</Custom>
          <RemoveExistingProducts Before="CostInitialize" />
      </InstallExecuteSequence>
      
      <CustomAction Id="NoDowngrade" Error="A newer version of $(var.ProductName) is already installed." />
      

      【讨论】:

        【解决方案6】:

        根据@Spacemani 给出的答案,这是我的最终解决方案。

        它生成类似于此的 MSI 表条目(升级、LaunchCondition 等)

        <MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeErrorMessage)" />
        

        但让您可以完全控制 InstallExecuteSequence。

        <!-- Product upgrade -->
        <Upgrade Id="$(var.UpgradeCode)">
          <UpgradeVersion OnlyDetect="no" Property="WIX_UPGRADE_DETECTED"
                          Maximum="$(var.ProductVersion)" IncludeMaximum="no" IncludeMinimum="no"
                          MigrateFeatures="yes" />
          <UpgradeVersion OnlyDetect="yes" Property="WIX_DOWNGRADE_DETECTED"
                          Minimum="$(var.ProductVersion)" IncludeMinimum="no" />
        </Upgrade>
        <InstallExecuteSequence>
          <RemoveExistingProducts Before="CostInitialize" />
        </InstallExecuteSequence>
        <Condition Message="!(loc.DowngradeErrorMessage)">NOT WIX_DOWNGRADE_DETECTED</Condition>
        

        请注意,您需要像这样在 .wixproj 文件中抑制 ICE27 错误。

        <PropertyGroup>
          <SuppressIces>ICE27</SuppressIces>
        </PropertyGroup>
        

        【讨论】:

        • 什么是 !(loc.DowngradeErrorMessage)?你有这方面的文件吗?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-02-27
        • 2013-05-20
        • 2011-11-13
        • 2018-02-03
        • 1970-01-01
        相关资源
        最近更新 更多