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

含NSSM服务的软件卸载失败问题求助

解决Wix安装包卸载时因服务运行触发的回滚问题

使用Wix制作的安装包,安装阶段可成功部署并启动XXXService服务,但执行卸载操作时失败并触发回滚,推测原因是XXXService服务仍处于运行状态,导致main.exe文件无法被删除。

原Wix配置代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<?if $(env.ARCH) = x64 ?>
  <?define ProductName = "Uninstall Bug (64 bit)" ?>
  <?define Win64 = "yes" ?>
  <?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
<?else ?>
  <?define ProductName = "Uninstall Bug" ?>
  <?define Win64 = "no" ?>
  <?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
<?endif ?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Product Id="1edc84cf-3a2b-4be9-ab5d-a1553c37e8ef" Name="Uninstall Bug" Language="1033" Version="1.0.0.0" Manufacturer="Example" UpgradeCode="2a82c6fe-9e93-4ccd-8e6c-c04de9a8289b">
    <Package InstallerVersion="200" Compressed="yes" />

    <MediaTemplate EmbedCab="yes" />

    <Feature Id="ProductFeature" Title="Uninstall Bug" Level="1">
        <ComponentGroupRef Id="MyComponentGroupId" />
    </Feature>

    <Directory Id="TARGETDIR" Name="SourceDir">
        <Directory Id="$(var.PlatformProgramFilesFolder)">
            <Directory Id="INSTALLDIR" Name="Uninstall Bug" />
        </Directory>
    </Directory>

    <ComponentGroup Id="MyComponentGroupId">
        <Component Id="MyComponent" Directory="INSTALLDIR" Guid="dfb1e839-1f62-4613-b323-daa3166caab5" KeyPath="yes">
            <File Id="MainFile" Source="src/main.exe" />
            <?if Win64 = "yes" ?>
            <File Id="NSSMFIle" Source="distrib/nssm/win64/nssm.exe" />
            <?else ?>
            <File Id="NSSMFIle" Source="distrib/nssm/win32/nssm.exe" />
            <?endif ?>
        </Component>
    </ComponentGroup>

    <CustomAction Id="CreateService" Directory="INSTALLDIR" Execute="deferred" Impersonate="no"
    ExeCommand='"[INSTALLDIR]nssm" install XXXService "[INSTALLDIR]main.exe" xxx' />
    <CustomAction Id="SetServiceDirectory" Directory="INSTALLDIR" Execute="deferred" Impersonate="no"
    ExeCommand='"[INSTALLDIR]nssm" set XXXService AppDirectory [INSTALLDIR]' />
    <CustomAction Id="StartService" Directory="INSTALLDIR" Execute="deferred" Impersonate="no"
    ExeCommand='"[INSTALLDIR]nssm" start XXXService' />
    <CustomAction Id="StopService" Directory="INSTALLDIR" Execute="deferred" ExeCommand='"[INSTALLDIR]nssm" stop XXXService' Impersonate="no" Return="ignore" />
    <CustomAction Id="UninstallService" Directory="INSTALLDIR" Execute="deferred" ExeCommand='"[INSTALLDIR]nssm" remove XXXService confirm' Impersonate="no" Return="ignore" />
    <InstallExecuteSequence>
        <Custom Action="CreateService" After="InstallServices" />
        <Custom Action="SetServiceDirectory" After="InstallServices" />
        <Custom Action="StartService" After="StartServices" />
        <Custom Action="UninstallService" Before="DeleteServices" />
        <Custom Action="StopService" Before="StopServices" />
    </InstallExecuteSequence>
  </Product>
</Wix>

解决方案

1. 调整卸载动作的执行顺序

当前自定义动作的时序存在逻辑问题,需确保服务先停止、再卸载,且这两个动作都在文件删除前完成。修改InstallExecuteSequence部分:

<InstallExecuteSequence>
    <Custom Action="CreateService" After="InstallServices" />
    <Custom Action="SetServiceDirectory" After="InstallServices" />
    <Custom Action="StartService" After="StartServices" />
    <!-- 仅在卸载时执行,先停止服务再卸载,都放在文件删除步骤之前 -->
    <Custom Action="StopService" Before="DeleteFiles">REMOVE="ALL"</Custom>
    <Custom Action="UninstallService" After="StopService">REMOVE="ALL"</Custom>
</InstallExecuteSequence>

2. 使用Wix原生ServiceControl管理服务(推荐)

依赖自定义nssm命令容易出现时序偏差,改用Wix原生的ServiceInstall和ServiceControl元素,能让MSI更可靠地处理服务生命周期:

<ComponentGroup Id="MyComponentGroupId">
    <Component Id="MyComponent" Directory="INSTALLDIR" Guid="dfb1e839-1f62-4613-b323-daa3166caab5" KeyPath="yes">
        <File Id="MainFile" Source="src/main.exe" />
        <?if Win64 = "yes" ?>
        <File Id="NSSMFIle" Source="distrib/nssm/win64/nssm.exe" />
        <?else ?>
        <File Id="NSSMFIle" Source="distrib/nssm/win32/nssm.exe" />
        <?endif ?>
        <!-- 注册服务基本信息,让Wix识别该服务 -->
        <ServiceInstall
            Id="XXXServiceInstall"
            Name="XXXService"
            DisplayName="XXXService"
            Type="ownProcess"
            Start="auto"
            ErrorControl="normal"
            Vital="no" />
        <!-- 自动处理服务启停与卸载,Wait="yes"确保操作完成后再继续流程 -->
        <ServiceControl
            Id="XXXServiceControl"
            Name="XXXService"
            Start="install"
            Stop="both"
            Remove="uninstall"
            Wait="yes" />
    </Component>
</ComponentGroup>

修改后可直接移除自定义的StartService、StopService、UninstallService动作。

3. 确保服务完全停止(自定义动作场景)

如果仍需使用nssm自定义动作,可修改StopService命令,增加超时等待确保服务完全停止:

<CustomAction Id="StopService" Directory="INSTALLDIR" Execute="deferred" 
ExeCommand='"[INSTALLDIR]nssm" stop XXXService timeout 10' Impersonate="no" Return="check" />

timeout 10表示等待10秒,Return="check"会在服务停止失败时反馈状态(可根据需求调整为ignore)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 14:22:52