现有卸载程序在升级过程中损坏 - 修补 MSI?
Existing Uninstaller Broken During Upgrade - Patch MSI?
我编写了一个自定义操作来帮助升级我的产品(从 1.0 到 1.1)。现在我需要从 1.1 升级到 1.2,但现有的卸载程序在升级过程中失败。我弄错了自定义操作的执行条件。 (经验教训,在部署之前始终测试升级到下一个版本)。
现在看来我最好的选择是修改现有 .msi 中的 InstallExecuteSequence table 以禁用失败的自定义操作。我必须创建 另一个 自定义操作来浏览注册表,在 C:\ 中找到现有的 .msi
Windows\Installer,打补丁,然后继续升级。这听起来像是一个糟糕的、容易出错的解决方案,但我真的不知所措。这应该是从远程云推送的自动、静默升级。
另一种选择是编写批处理脚本来卸载现有产品,然后执行新的安装程序。
有什么建议吗?
编辑 这个问题已经在这里回答了:I screwed up, how can I uninstall my program?
支持的方法是打补丁(我指的是 MSP 文件,而不是编码来更改缓存的 MSI 文件)。这是迄今为止摆脱困境的最直接方法。之后,进行升级。使用 WiX,您可能可以将 MSP 和升级捆绑在一起。
在任何情况下,您都不会使用另一个 MSI 来执行您提议的更改。一个小的可执行文件可以执行您的建议,并且:
MsiGetProductInfo(产品代码,...,INSTALLPROPERTY_LOCALPACKAGE)
是您找到缓存的 MSI 的方式。
条件:您为失败的自定义操作设置了什么条件?更重要的是,您打算使用的新条件是什么? 听起来常规卸载可以,但主要升级失败?典型的问题是卸载完全失败,然后通常的解决方案是次要升级我会快速描述。
次要升级:通常我使用的是次要升级来修复任何问题当前安装的(卸载)安装顺序错误。次要升级不会卸载现有安装,它会升级它 "in-place",因此永远不会调用卸载序列,因此您可以避免其所有错误出现。 There is no need to browse to the cached MSI file and hack it manually if you do things correctly in your minor upgrade
。如果您使用正确的次要升级命令行进行安装,Windows 安装程序引擎将自动神奇地更新缓存的 MSI。
Future Upgrades:如果你让它足够简单,小升级通常总是有效的,但问题通常是应用它,因为它通常只针对一个以前的版本。当您到达下一个版本并且如果您随后使用主要升级,如果您正在升级从未应用过次要升级的安装,您将在卸载时看到原始 MSI 清单本身中的错误 - 换句话说,它仍然是安装的最旧版本。这通常由 setup.exe
启动器解决,如果需要,它会安装次要升级。坏消息是您需要在未来的每个版本中保留该更新 - 如果您想避免任何升级错误。或者在公司环境中,您将使用分发系统检查包装盒上已有的内容并进行相应安装。 如果您的手动卸载工作正常(但主要升级卸载失败),您需要做的就是将 an uninstall command line to msiexec.exe 作为第一个命令推送到 运行 通过您的setup.exe我认为。那么就不需要在您的 setup.exe 启动器中包含任何次要的升级二进制文件。
Detect & Abort?:Michael Urman 在此处的回答解释了如何难以确保次要升级存在于应用下一版本软件之前的框:
InstallShield fails because of a bad uninstall。他建议让你的包更好地检测是否可以安全地应用新的升级。
部分链接:
- how to omit a component when we try to build .msi using wix(关于补丁如何只是 MSI 升级的分发机制,它必须已经在工作)
- Is there any possible way to perform upgrade when Product codes for old and new versions are same?(关于小升级及其技术限制)
这是我开始使用的技巧,但根据上面的答案,它似乎不是首选方法。
[CustomAction]
public static ActionResult Patch11Installer(Session session)
{
string localPackage = NativeMethods.GetMsiInstallSource("{MY-PRODUCT-CODE}");
if (String.IsNullOrEmpty(localPackage))
{
session.Log("Failed to locate the local package");
return ActionResult.Failure;
}
session.Log($"Found local package at {localPackage}");
using (Database database = new Database(localPackage, DatabaseOpenMode.Direct))
{
foreach (string action in new string[] { LIST OF CUSTOM ACTION NAMES })
{
session.Log($"Modifying condition for action {action}");
database.Execute($"UPDATE InstallExecuteSequence SET Condition='WIX_UPGRADE_DETECTED' WHERE Action='{action}'");
}
database.Commit();
}
return ActionResult.Success;
}
自定义操作调用 MsiGetProductInfo 以使用我从安装程序日志文件中获得的 v1.1 产品代码查询 v1.1 MSI。然后它打开 MSI 数据库并修改 InstallExecuteSequence table 的条件 属性 以获取失败的自定义操作列表。它将条件从 "UPGRADINGPRODUCTCODE OR WIX_UPGRADE_DETECTED" 更改为 "WIX_UPGRADE_DETECTED"。 UPGRADINGPRODUCTCODE 是导致重大升级期间卸载失败的 属性,因为此 属性 被传递给卸载程序并包含 new 产品代码;在我的例子中是 v1.2 的产品代码。这是我的安装程序文件中的自定义操作定义。
<CustomAction Id="Patch11Installer" Return="check" Impersonate="yes" Execute="immediate" BinaryKey="MyUpgradeCustomActions" DllEntry="Patch11Installer" />
我会考虑按照其他答案中的建议实施小幅升级。我只是想我会把这个解决方案留在这里。
我编写了一个自定义操作来帮助升级我的产品(从 1.0 到 1.1)。现在我需要从 1.1 升级到 1.2,但现有的卸载程序在升级过程中失败。我弄错了自定义操作的执行条件。 (经验教训,在部署之前始终测试升级到下一个版本)。
现在看来我最好的选择是修改现有 .msi 中的 InstallExecuteSequence table 以禁用失败的自定义操作。我必须创建 另一个 自定义操作来浏览注册表,在 C:\ 中找到现有的 .msi Windows\Installer,打补丁,然后继续升级。这听起来像是一个糟糕的、容易出错的解决方案,但我真的不知所措。这应该是从远程云推送的自动、静默升级。
另一种选择是编写批处理脚本来卸载现有产品,然后执行新的安装程序。
有什么建议吗?
编辑 这个问题已经在这里回答了:I screwed up, how can I uninstall my program?
支持的方法是打补丁(我指的是 MSP 文件,而不是编码来更改缓存的 MSI 文件)。这是迄今为止摆脱困境的最直接方法。之后,进行升级。使用 WiX,您可能可以将 MSP 和升级捆绑在一起。
在任何情况下,您都不会使用另一个 MSI 来执行您提议的更改。一个小的可执行文件可以执行您的建议,并且:
MsiGetProductInfo(产品代码,...,INSTALLPROPERTY_LOCALPACKAGE)
是您找到缓存的 MSI 的方式。
条件:您为失败的自定义操作设置了什么条件?更重要的是,您打算使用的新条件是什么? 听起来常规卸载可以,但主要升级失败?典型的问题是卸载完全失败,然后通常的解决方案是次要升级我会快速描述。
次要升级:通常我使用的是次要升级来修复任何问题当前安装的(卸载)安装顺序错误。次要升级不会卸载现有安装,它会升级它 "in-place",因此永远不会调用卸载序列,因此您可以避免其所有错误出现。 There is no need to browse to the cached MSI file and hack it manually if you do things correctly in your minor upgrade
。如果您使用正确的次要升级命令行进行安装,Windows 安装程序引擎将自动神奇地更新缓存的 MSI。
Future Upgrades:如果你让它足够简单,小升级通常总是有效的,但问题通常是应用它,因为它通常只针对一个以前的版本。当您到达下一个版本并且如果您随后使用主要升级,如果您正在升级从未应用过次要升级的安装,您将在卸载时看到原始 MSI 清单本身中的错误 - 换句话说,它仍然是安装的最旧版本。这通常由 setup.exe
启动器解决,如果需要,它会安装次要升级。坏消息是您需要在未来的每个版本中保留该更新 - 如果您想避免任何升级错误。或者在公司环境中,您将使用分发系统检查包装盒上已有的内容并进行相应安装。 如果您的手动卸载工作正常(但主要升级卸载失败),您需要做的就是将 an uninstall command line to msiexec.exe 作为第一个命令推送到 运行 通过您的setup.exe我认为。那么就不需要在您的 setup.exe 启动器中包含任何次要的升级二进制文件。
Detect & Abort?:Michael Urman 在此处的回答解释了如何难以确保次要升级存在于应用下一版本软件之前的框: InstallShield fails because of a bad uninstall。他建议让你的包更好地检测是否可以安全地应用新的升级。
部分链接:
- how to omit a component when we try to build .msi using wix(关于补丁如何只是 MSI 升级的分发机制,它必须已经在工作)
- Is there any possible way to perform upgrade when Product codes for old and new versions are same?(关于小升级及其技术限制)
这是我开始使用的技巧,但根据上面的答案,它似乎不是首选方法。
[CustomAction]
public static ActionResult Patch11Installer(Session session)
{
string localPackage = NativeMethods.GetMsiInstallSource("{MY-PRODUCT-CODE}");
if (String.IsNullOrEmpty(localPackage))
{
session.Log("Failed to locate the local package");
return ActionResult.Failure;
}
session.Log($"Found local package at {localPackage}");
using (Database database = new Database(localPackage, DatabaseOpenMode.Direct))
{
foreach (string action in new string[] { LIST OF CUSTOM ACTION NAMES })
{
session.Log($"Modifying condition for action {action}");
database.Execute($"UPDATE InstallExecuteSequence SET Condition='WIX_UPGRADE_DETECTED' WHERE Action='{action}'");
}
database.Commit();
}
return ActionResult.Success;
}
自定义操作调用 MsiGetProductInfo 以使用我从安装程序日志文件中获得的 v1.1 产品代码查询 v1.1 MSI。然后它打开 MSI 数据库并修改 InstallExecuteSequence table 的条件 属性 以获取失败的自定义操作列表。它将条件从 "UPGRADINGPRODUCTCODE OR WIX_UPGRADE_DETECTED" 更改为 "WIX_UPGRADE_DETECTED"。 UPGRADINGPRODUCTCODE 是导致重大升级期间卸载失败的 属性,因为此 属性 被传递给卸载程序并包含 new 产品代码;在我的例子中是 v1.2 的产品代码。这是我的安装程序文件中的自定义操作定义。
<CustomAction Id="Patch11Installer" Return="check" Impersonate="yes" Execute="immediate" BinaryKey="MyUpgradeCustomActions" DllEntry="Patch11Installer" />
我会考虑按照其他答案中的建议实施小幅升级。我只是想我会把这个解决方案留在这里。