Wix升级时Windows服务偶发卸载问题的排查与解决咨询
组件动作的判定逻辑
咱们先搞清楚MSI是怎么决定组件动作的:
- 状态对比是基础:MSI会先查组件当前的
Installed状态(已装/未装),再结合安装流程定义的Request状态(要装/要删),先定一个大方向。 - KeyPath是核心判定依据:每个组件都有一个KeyPath(你这里是
SDKWebAPI.Service.exe),MSI会把已安装的KeyPath文件和安装包里的对比:- 如果旧文件不存在、版本更低或者哈希不匹配,MSI就会判定需要替换文件,这时动作是
FileAbsent——说白了就是先删旧文件再装新的,组件本身不会被移除。 - 如果MSI找不到组件的注册表记录(比如缓存坏了),就会误判这个组件不需要了,触发
Action: Absent,直接把整个组件(包括关联的服务)删掉。
- 如果旧文件不存在、版本更低或者哈希不匹配,MSI就会判定需要替换文件,这时动作是
- 服务和组件绑定:因为你的服务是定义在这个组件里的,所以组件被标记为
Absent时,MSI会自动执行DeleteServices动作把服务卸载掉。
为何不同机器上组件动作不同?
看你的日志和配置,核心问题出在这几个地方:
- 组件Guid是空的!:这是致命错误!MSI全靠唯一的Guid来追踪组件的安装状态,你把Guid留空,MSI根本没法在注册表中正确标记这个组件的存在,不同机器的注册表缓存状态不一样,自然会出现不同的判定结果。
- MSI注册表缓存损坏:失败日志里的
1402错误(访问UNKNOWN\Installer\Features\...注册表项失败),说明这台机器的MSI组件缓存已经坏了,导致MSI认不出这个组件已经装过了,所以误判要删掉它。 - 服务进程残留:虽然你手动停了服务,但有些机器上服务进程可能没完全退出,导致
SDKWebAPI.Service.exe被锁定,MSI没法替换文件,只能退而求其次删掉组件。 - 升级状态计算差异:不同机器上已安装的服务程序版本、哈希可能有细微差别,或者
REINSTALLMODE属性设置不同,导致MSI的成本计算结果不一样,触发不同的动作。
如何解决该问题?
按优先级来,先解决最根本的问题:
- 给组件补全唯一Guid:用Wix工具或者Visual Studio生成一个唯一的Guid,填到
<Component>的Guid属性里。记住,这个Guid一旦确定就别随便改,除非组件的功能发生了根本性变化——MSI靠它识别组件呢。 - 给服务安装/卸载加条件:确保只有首次安装才装服务,只有完全卸载才删服务,升级时碰都别碰服务的卸载:
- 给
ServiceInstall加条件:NOT Installed(只有首次安装时执行)。 - 给
ServiceControl设置Remove="uninstall"(只有卸载时才删服务)。
示例配置:
<Component Id="cmp.SR.SDKWebAPI.Service.exe" Guid="这里填你生成的唯一Guid"> <File Id="fil.SDKWebAPI.Service.exe" Source="SDKWebAPI.Service.exe" KeyPath="yes" /> <File Id="fil.SDKWebAPI.Service.exe.config" Source="SDKWebAPI.Service.exe.config" KeyPath="no" /> <ServiceInstall Id="SDKWebAPI.Service.exe.Installer" Type="ownProcess" Name="RidderIQWebApi" DisplayName="Ridder iQ Web API" Description="Ridder iQ Web API service" Start="auto" Account="LocalSystem" ErrorControl="ignore"> <Condition>NOT Installed</Condition> <util:ServiceConfig FirstFailureActionType="restart" SecondFailure="..." /> </ServiceInstall> <ServiceControl Id="ManageService" Name="RidderIQWebApi" Stop="both" Wait="yes" Remove="uninstall" /> </Component> - 给
- 强制终止服务进程:升级前加个自定义动作,把服务进程彻底杀掉,避免文件锁定:
<CustomAction Id="KillServiceProcess" Execute="immediate" Impersonate="no" Return="ignore" Command='taskkill /F /IM SDKWebAPI.Service.exe' /> <InstallExecuteSequence> <!-- 升级前执行,确保进程被终止 --> <Custom Action="KillServiceProcess" Before="StopServices">UPGRADINGPRODUCTCODE</Custom> </InstallExecuteSequence> - 修复损坏的MSI缓存:已经出问题的机器,执行下面的命令修复安装,重建组件缓存:
msiexec /i "你的MSI文件路径" REINSTALL=ALL REINSTALLMODE=vomus - 强制升级时替换文件:在Wix里设置
REINSTALLMODE属性为amus,强制MSI替换所有文件,避免误判组件需要移除:<Property Id="REINSTALLMODE" Value="amus" />
内容的提问来源于stack exchange,提问作者urk_forever
相关产品推荐
相关产品推荐

