如何配置WiX安装程序在中途重启后失败时回滚所有已安装包
问题背景
我有一套基于WiX 3.11开发、搭载托管引导程序应用程序(MBA)的安装程序,其安装链中的某个包可能触发系统重启。我在MBA的PlanPackageBegin事件处理器中,参考标准引导程序应用的逻辑实现了重启后的恢复行为,代码如下:
private void Bootstrapper_PlanPackageBegin(object sender, PlanPackageBeginEventArgs e) { // _bootstrapper.Status.ForcedRestartPackage comes from the WixBundleForcedRestartPackage engine variable if (_bootstrapper.Status.ForcedRestartPackage != null) { // Resuming after forced restart // Skip packages until after the package that forced the restart e.State = RequestState.None; if (e.PackageId == _bootstrapper.Status.ForcedRestartPackage) { _bootstrapper.Status.ForcedRestartPackage = null; } } else { ... } }
当前遇到的问题是:若系统重启后恢复安装流程时某个包安装失败,安装程序仅会回滚重启之后安装的包。需求是安装失败触发回滚时,能够回滚所有已安装的包,包含重启前已经完成安装的包。
原因说明
Burn引擎默认将强制重启点作为隐式回滚边界:触发重启的包安装完成后,引擎会自动提交该节点之前所有已安装包的事务,默认回滚逻辑只会处理边界之后、也就是重启后安装的包,不会回滚已经提交的重启前安装内容,这是该现象的核心原因,和当前实现的重启后跳包逻辑无关。
实现方案
方案1:修改Bundle包配置(优先推荐,官方原生支持)
直接在可能触发强制重启的包(MsiPackage/ExePackage等)上添加属性AfterRestartAction="Rollback"即可。
- 该属性默认值为
Commit,对应目前看到的默认逻辑:包触发重启后,重启前已安装内容直接提交,后续失败不回滚。 - 设置为
Rollback后,引擎不会在重启点创建隐式提交边界,整个安装链的包属于同一个事务,只要重启后任意环节安装失败,引擎会自动按安装逆序回滚所有已安装的包,包括重启前完成安装的部分。 - 如果链中有多个可能触发重启的包,所有这类包都需要配置该属性,避免任意一个触发重启时自动提交前序事务。
方案2:MBA自定义回滚逻辑(适用于需要更灵活控制的场景)
如果配置无法满足定制化需求,可以通过托管引导程序手动接管回滚计划逻辑,步骤如下:
- 持久化记录已安装包:不要只依赖引擎自带的
WixBundleForcedRestartPackage变量,自己在ExecutePackageComplete事件中判断,只要包返回安装成功状态,就将对应包ID写入可持久化存储(注册表、安装目录下的临时文件都可以,注意重启后内存数据会清空,不能用内存变量存储)。 - 回滚场景下重写计划逻辑:检测到安装失败进入回滚流程时,在
PlanBegin阶段遍历持久化存储的已安装包列表,将所有包的计划状态设置为RequestState.Absent,强制引擎将这些包纳入卸载回滚范围。 - 调整原有跳包逻辑:在回滚计划阶段,关闭
PlanPackageBegin中跳过重启前包的逻辑,不要给重启前已安装的包设置RequestState.None,保证引擎能正常执行这些包的卸载流程。
注意事项
- 优先使用配置方案,原生逻辑稳定性远高于自定义实现,不需要额外维护持久化状态,也不会出现回滚顺序错误的问题。
- 如果使用自定义逻辑,一定要保证回滚顺序和安装顺序相反,避免因依赖关系导致回滚失败。
内容的提问来源于stack exchange,提问作者Brian Henry
相关产品推荐
相关产品推荐

