【问题标题】:InstallShield fails because of a bad uninstall由于卸载错误,InstallShield 失败
【发布时间】:2017-10-24 15:55:35
【问题描述】:

我正在构建一个 InstallShield 项目,该项目对现有安装进行 [主要升级][1](即,我更改了 Product Code,同时保留了 Upgrade Code)。

之前版本(已部署)的卸载程序损坏,无法运行某些脚本。这本身并不是一个严重的问题,因为所有相关文件都已预先删除。

问题是当新的安装程序尝试卸载以前的版本时,它会在卸载程序失败时中止。我尝试设置“失败时继续”标志(媒体-->升级-->主要升级-->高级-->附加设置),但没有任何效果。

问题:

  • 有没有办法将 IS 配置为静默删除以前的版本?
  • 如果没有,我可以编写一些自定义 VB-action 以静默卸载以前的版本吗?

编辑

不确定这是否相关,但这是以前的卸载程序的问题:

安装顺序包括一些自定义操作。其中两个调用可执行文件,然后安装一些设备驱动程序。显然,它们应该只在安装过程中运行。不幸的是,以前的设计者没有设置“不删除”条件,这会导致操作在卸载操作期间运行。因此卸载程序失败(因为它找不到驱动程序可执行文件)。

【问题讨论】:

  • 在您的旧 MSI 中 InstallFinalize 之后,您是否碰巧有即时模式自定义操作设置为“检查返回码”?
  • @SteinÅsmul - 旧的 MSI 确实包含一些经过 InstallFinilize 的自定义操作,但它们不检查返回码。但它们确实存在错误——其中两个操作调用可执行文件来安装特定的设备驱动程序。显然,它们应该只在安装应用程序时运行,但没有设置“NOT REMOVE”条件。这会导致卸载操作无法运行该操作,并且操作失败。
  • 一般情况下 InstallFinalize 之后不应该有即时模式自定义操作(除非它们是只读的),并且即时模式自定义操作绝不能对系统进行更改(它们不会运行提升并且会失败对于非管理员用户并且他们在安装事务之外运行,因此回滚不可用)。最后,如果您仅在 UI 序列中插入自定义操作,则在静默运行时您的设置将不会完成,因为在这种情况下会完全跳过 UI 序列。您确定没有检查退出代码吗?请报告自定义操作表中“类型”字段的内容。
  • @SteinÅsmul - 感谢您的建议。这些操作出现在“序列-->安装-->执行”下,它们会在没有 UI 的情况下运行吗?至于类型,它们都是脚本函数(类型 0x0x1:msidbCustomActionTypeDll + msidbCustomActionTypeBinaryData),然后在另一个 exe 上调用LaunchAppAndWait()。
  • @SteinÅsmul - 两个项目都有Return Processing: Synchronous (Check exit code)。这是你的意思吗?

标签: windows-installer upgrade installshield


【解决方案1】:

这更像是一个评论而不是一个答案,但是评论太长了。

通常真正的修复是创建次要升级(或次要升级补丁)以“修复”卸载顺序中的错误,然后卸载产品以正常方式(无论是主要升级卸载还是手动触发卸载都可以使用)。这是可能的,因为次要升级不会卸载现有产品(失败的卸载自定义操作将永远不会运行),而只是“就地”更新产品(或覆盖它)。这允许您在调用卸载序列之前修复任何错误。

我已经多次成功地使用这种方法来修复企业软件包中的严重错误,这些错误可能有很多已安装的实例,并且卸载顺序非常失败。然而,这并不是一件小事——这需要时间和测试工作。我通常也会以补丁的形式提供更新,但小的升级就足够了(补丁很复杂)。

对于您的次要升级包,最简单的方法可能是设置一个条件,在所有失败的卸载自定义操作上始终为假,这样它们就永远不会运行一旦实际触发卸载。这显然会在盒子上留下“一些垃圾”,但你可以忽略它或者更好地清理它。不过要小心清理代码——它往往包含新的错误,你必须在适当的时候处理这​​些错误。部署是一个过程,其中每个添加的发布周期、错误修复和调整都会为意外错误打开新的可能性,然后为下一个版本增加新的复杂性和不可预测性。让事情尽可能简单。简而言之:只是更多可以破坏而不增加任何好处的东西。如果你不这样做,如果有操作问题,一定要清理干净。

我不会详细说明应该如何实施小升级。 Installshield 帮助文件 在这个主题上非常好,应该可以帮助您完成工作。我会做一个更新,除了改变卸载操作的条件之外什么都不做。

一旦您进行了次要升级,您需要使用适当的命令行应用它,或者只使用 Installshield 的 setup.exe 为您执行此操作。再次查看 Installshield 帮助文件(“Run-Time Behavior for Minor Upgrades” - 这个帮助页面应该是您所需要的)。

在没有 setup.exe 包装的情况下应用次要升级的命令行通常是:

msiexec.exe /i product.msi REINSTALLMODE=vomus REINSTALL=ALL

如果您在没有 setup.exe 包装器的情况下通过 msiexec.exe 手动安装,则为 REINSTALLMODE 添加 the v option 非常重要。这会在系统上缓存新的 MSI,对于小升级正常工作至关重要 - 特别是对于您修复卸载顺序的目的。

Applying Minor Upgrades 的 MSDN 文档。

【讨论】:

  • “让事情尽可能简单。” - 如此金句;)。出于类似的原因,我们不再执行“主要”升级,即使产品本身在主要版本中遇到了问题。过去我们曾多次使用这种技术来修复“主要”升级为“次要”。
【解决方案2】:

通过次要升级更改或删除现有软件包是修复现有软件包的好方法。但是确保在您的主要升级之前发生这种情况是一个难题。您不能将它集成到单个基本 MSI 包中,主要是因为这在 Windows Installer 本身中是不可能的。虽然您可以向 UI 序列添加自定义操作,但这不适用于静默安装它的任何人。由于 Windows Installer 的限制,您无法在执行序列中执行任何操作。

如果您可以使用 Suite/Advanced UI 项目,理论上您可以交付一个可以安装次要升级和主要升级的 exe;我不确定这种方法的障碍是什么。通常,套件不会像次要升级那样用于安装临时包,也没有干净的方式在安装过程中自动删除包。

这里有两个选项可以帮助您的用户以最少的困惑到达他们需要的地方。

识别、指示和中止

您的主要升级的 ActionProperty 填充了机器上存在的相关软件包的产品代码。通常这是一个单一的产品代码。如果您遵循 ISPreventDowngrade 条目和自定义操作的模式,您可以使用类型 19“错误”自定义操作来显示消息并在存在旧版本时中止主要升级。该消息应指示用户在安装新版本之前卸载旧版本的产品;或者,或者,应用次要升级,然后继续。

使自定义操作的条件尽可能具体;如果您知道单个产品代码表现出该行为,请让您的条件检查其在 action 属性中的值。如果所有特定范围的版本都表现出这种行为(但旧版本工作正常),请考虑添加次要“仅检测”主要升级,并根据次要升级的操作属性调整您的自定义操作。

注意:您可以尝试将此与尝试自动卸载或次要升级的 UI 序列操作结合使用。但一定要保留执行顺序中的条件,以确保静默卸载失败在日志文件中获得良好信息。

尽量忽略错误

(哎呀;写完这篇文章后,我重读了你说你已经尝试过这种方法但它对你不起作用的地方。所以你可能回到识别、指示和中止,或者可能使用套件/高级 UI 项目来交付东西。不过,我将把它留给后代,因为我相信它包含很好的建议,可以避免使用此选项时出现其他问题。)

我没有使用过这个,所以我不知道它的行为的全部后果,我不会推荐它。但是Upgrade table 的属性列中有一个选项,可让您在删除旧版本期间忽略失败。 InstallShield 在“高级”选项卡上将其显示为 Continue on Failure。

如果您 100% 确定您的旧版本最终处于令人满意的状态,并且您的新版本将在此之后运行,您可以试试这个。我强烈建议对这种方法进行彻底的 QA,包括伪造一个较新的主要版本,以确保进一步的升级仍然做正确的事情。

与我在识别、指示和中止中给出的建议类似,尝试为尽可能少的先前版本设置失败时继续。例如,您可能希望将升级条目分成三个版本范围:问题之前的版本(不要继续);受问题影响的人(继续);问题之后的那些,即未来的重大升级(不要继续)。这样可以避免隐藏将来出现的任何类似问题,以便您可以明确选择如何处理它们。

【讨论】:

    【解决方案3】:

    如果只有一个部署没有该条件,那么您可以编写一个自定义操作,您可以在其中运行以下命令行:

    msiexec.exe /x {PRODUCT_CODE_OF_OLD_MSI} REMOVE="ALL" /qn

    另一个可能有效的技巧是在 InstallInitialize 操作之前安排 RemoveExistingProducts 操作,如下所述:How to uninstall previous version as part of msi install?

    您还可以编写自己的自定义 MSI DLL,在继续之前终止正在运行的进程。

    【讨论】:

      猜你喜欢
      • 2017-02-21
      • 2012-11-12
      • 1970-01-01
      • 1970-01-01
      • 2018-05-20
      • 2021-10-07
      • 2022-11-02
      • 2013-01-29
      • 2021-07-02
      相关资源
      最近更新 更多