You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

实现ExePackage与其他MSI的依赖,保障卸载流程正常完成

Wix Bundle 卸载场景下服务处理ExePackage依赖问题解决

背景

项目包含服务器、客户端、微服务托管三个独立组件,各组件打包为单独MSI。其中服务器和微服务托管组件会安装Windows服务,且服务器服务依赖微服务托管服务。所有组件通过Bundle.exe链式部署,用户可选择安装客户端、服务器或两者。更新采用MajorUpgrade实现,要求更新失败时能回滚至先前版本。

问题起因

Wix v3的Bundle回滚逻辑不符合预期:仅回滚失败的MSI,已安装的前置MSI会被移除。因此采用两个变通方案:

  • 优先安装最易出错的组件
  • 通过ExePackage手动处理服务器服务的启停,避免MSI间的依赖配置限制

现有实现代码

Bundle.wxs 链式部署配置

<Chain>
    <PackageGroupRef Id="SqlExpress"/>
    <PackageGroupRef Id="vcredist"/>
    <!-- 其他依赖组件 -->
    <PackageGroupRef Id="ServiceHandlingStop" />
    <PackageGroupRef Id="Server"/>
    <PackageGroupRef Id="MicroServiceHost"/>
    <PackageGroupRef Id="ServiceHandlingStart" />
    <PackageGroupRef Id="Client"/>
</Chain>

服务处理片段

<Fragment>
    <PackageGroup Id="ServiceHandlingStop">
        <ExePackage Id="ServiceHandlingStopCall"
                    Compressed="yes"
                    DisplayName="Service Management"
                    SourceFile="redist\Runner.bat"
                    InstallCommand="net stop WCFHostService"
                    UninstallCommand="net start WCFHostService"
                    DetectCondition="WixBundleInstalled = 1 AND InternalInstallType &lt;&gt; &quot;Client&quot;"
                    InstallCondition="InternalInstallType &lt;&gt; &quot;Client&quot;"
                    Permanent="no"
                    Vital="no"
                    PerMachine="yes">               
            <ExitCode Behavior="success" />
        </ExePackage>
    </PackageGroup>
    
    <PackageGroup Id="ServiceHandlingStart">
        <ExePackage Id="ServiceHandlingStartCall"
                    Compressed="yes"
                    DisplayName="Service Management"
                    SourceFile="redist\Runner.bat"
                    InstallCommand="net start WCFHostService"
                    UninstallCommand="net stop WCFHostService"
                    DetectCondition="WixBundleInstalled = 1 AND InternalInstallType &lt;&gt; &quot;Client&quot;"
                    InstallCondition="InternalInstallType &lt;&gt; &quot;Client&quot;"
                    Permanent="no"
                    Vital="yes"
                    PerMachine="yes">
            <ExitCode Value="0" Behavior="success" />
            <ExitCode Behavior="error" />
        </ExePackage>
    </PackageGroup>
</Fragment>

预期目标

  • 仅安装服务器组件时,在微服务托管组件安装完成后启动服务器服务
  • 更新失败回滚后自动启动服务器服务
  • 卸载时无需关注服务状态

当前问题

卸载Bundle时若意外终止,部分MSI可能残留。再次启动卸载时,Burn会执行ServiceHandlingStartCall,但此时服务器MSI已卸载,服务不存在,net start命令失败。由于该ExePackage设置了Vital=yes,导致卸载流程终止,用户无法完全移除产品。

解决方案建议

1. 调整ExePackage的条件与退出码处理

通过Wix内置属性检查服务是否存在,并区分安装/更新与卸载动作的行为:

<PackageGroup Id="ServiceHandlingStart">
    <ExePackage Id="ServiceHandlingStartCall"
                Compressed="yes"
                DisplayName="Service Management"
                SourceFile="redist\Runner.bat"
                InstallCommand="net start WCFHostService"
                <!-- 卸载时不执行实际服务操作 -->
                UninstallCommand="echo Skip service operation on uninstall"
                <!-- 仅当服务存在且非客户端安装时检测 -->
                DetectCondition="WixBundleInstalled = 1 AND InternalInstallType &lt;&gt; &quot;Client&quot; AND ServiceExists(WCFHostService)"
                <!-- 安装/更新时需服务存在才执行;卸载时允许跳过 -->
                InstallCondition="InternalInstallType &lt;&gt; &quot;Client&quot; AND (WixBundleAction &lt;&gt; 3 OR ServiceExists(WCFHostService))"
                Permanent="no"
                <!-- 仅在安装/更新时设为Vital,卸载时不阻塞流程 -->
                Vital="!(wix.WixBundleAction &lt;&gt; 3)"
                PerMachine="yes">
        <ExitCode Value="0" Behavior="success" />
        <!-- 卸载时,服务不存在的错误码视为成功 -->
        <ExitCode Value="2" Behavior="success" Condition="WixBundleAction = 3" />
        <!-- 安装/更新时其他错误视为失败 -->
        <ExitCode Behavior="error" Condition="WixBundleAction &lt;&gt; 3" />
    </ExePackage>
</PackageGroup>

核心调整点:

  • 使用ServiceExists(WCFHostService)检查服务是否存在,避免执行无效命令
  • 卸载时替换UninstallCommand为无操作命令
  • 动态设置Vital属性,仅在安装/更新时阻塞流程
  • 针对卸载动作单独处理退出码,服务不存在的错误不终止卸载

2. 由Windows服务管理器自动处理依赖(推荐)

放弃Bundle层面的手动服务启停,在服务器MSI的ServiceInstall中配置服务依赖:

<!-- 服务器MSI中的ServiceInstall节点 -->
<ServiceInstall Id="WCFHostServiceInstall"
                Name="WCFHostService"
                Type="ownProcess"
                Start="auto"
                ErrorControl="normal">
    <!-- 添加对微服务托管服务的依赖 -->
    <Dependency Id="MicroServiceHostDependency" Name="MicroServiceHostServiceName" />
</ServiceInstall>

这种方式让Windows服务管理器自动处理启动顺序:服务器服务会等待依赖的微服务托管服务启动后再启动,无需Bundle介入。同时MSI的ServiceControl可以正常处理服务的启停,避免Bundle中ExePackage带来的卸载问题。

3. 优化ServiceHandlingStop的卸载逻辑

同样调整ServiceHandlingStop的UninstallCommand和条件,避免卸载时执行无效的服务启动命令:

<ExePackage Id="ServiceHandlingStopCall"
            ...
            UninstallCommand="echo Skip service start on uninstall"
            DetectCondition="WixBundleInstalled = 1 AND InternalInstallType &lt;&gt; &quot;Client&quot; AND ServiceExists(WCFHostService)"
            ...>
</ExePackage>

内容的提问来源于stack exchange,提问作者ManuHArt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 20:30:56