Wix 在 uninstall/upgrade 停止服务:防止 "restart popup"(文件正在使用的情况)

Wix stop service on uninstall/upgrade: prevent "restart popup" (file-in-use situation)

我遇到了问题,在卸载(或升级)时,重启管理器抱怨文件正在使用,因此强制重启:

RESTART MANAGER: Detected that application with id 7000, friendly name 'javaw.exe', of type RmCritical and status 1 holds file[s] in use.
RESTART MANAGER: Did detect that a critical application holds file[s] in use, so a reboot will be necessary.

RESTART MANAGER 抱怨的服务是基于 java 的服务。该服务(此处称为 myservice.exe)正在递归启动 java 个子进程:

myservice.exe --运行
↳ javaw.exe --someArguments
↳ someother.exe --someArguments
↳ javaw.exe --someMoreArguments

服务定义的 Wix 片段:

<DirectoryRef Id="BINDIR">
        <Component Id="myservice.exe" Guid="PUT-GUID-HERE">
            <File Id="myservice.exe" KeyPath="yes" Vital="yes"
                  Source="SourceDir\bin\myservice.exe"/>
            <ServiceInstall Id="MyService" Type="ownProcess"
                            Vital="yes" Name="MyService" DisplayName="My Service"
                            Description="My Service" Start="auto" Account=".\LocalSystem"
                            ErrorControl="normal" Interactive="no" Arguments="--run"/>
            <ServiceControl Id="MyService" Name="MyService" Wait="yes" Remove="uninstall" Stop="uninstall" Start="install"/>
        </Component>
</DirectoryRef>

现在,有趣的部分:

卸载时:

到目前为止,Service* 表中的条目对我来说似乎还不错。

ServiceControl-Table:
ServiceControl  Name       Event  Arguments  Wait  Component_
MyService       MyService  161               1     myservice.exe

ServiceInstall-Table:
ServiceInstall  Name       DisplayName  ServiceType StartType ErrorControl LoadOrderGroup Dependencies StartName Password Arguments Component_     Description
MyService       MyService  My Service   16          2         32769        .\LocalSystem                                  --run     myservice.exe  My Service


所以,分解一切: 重启管理器似乎没有识别出 java 进程是子进程,将被 StopServices 操作停止。

我在这里发现了一些类似的问题: https://www.mail-archive.com/wix-users@lists.sourceforge.net/msg57924.html
Wix Installer Problem: Why does RestartManager mark Service as RMCritical and not RMService

在此先感谢您为解决此问题提供的任何帮助!

您有几个选项可以解决此问题:

-通过使用 属性 table 中的 MSIRESTARTMANAGERCONTROL= "Disable" 禁用 "Restart Manager"。这将启动旧版 "FilesInUse" 对话框。 在您的情况下,也可能不会显示 FilesinUse 对话框(因为服务没有与之关联的 window) "FilesinUse" 对话框不会列出没有 window 关联的进程。因此,在您的情况下,禁用重新启动管理器可能不会显示任何对话框(FilesInUse 和 RestartManager 均不显示)。

但是,这也意味着可能需要重新启动,不一定是因为您的服务,而是因为其他一些进程可能正在使用您的文件。如果您认为除了您自己的服务保存文件之外没有其他进程,那么请继续并遵循这种方法。如果您认为除了您的服务保存文件之外可能还有其他进程,那么启用 "Restart Manager" 是理想的选择。没有 "Restart Manager" 会导致以下情况之一:

-显示 Legacy FilesInUse 对话框,要求您关闭对话框中列出的进程。这可能导致您必须通过自定义操作关闭这些进程。

"RestartManager" 和 "FilesInUse" 对话框均由 "InstallValidate" 标准操作显示。如果您想抑制这两个对话框,请确保您的自定义操作安排在 "InstallValidate" 标准操作之前。这里有一个问题。在 InstallValidate 之前安排这样的自定义操作必须是立即模式自定义操作(您不能在 "IntsallFinalize" 之前有延迟模式自定义操作)。因此,在您不是 运行 管理员的情况下(例如在启用 UAC 的情况下),您可能没有关闭应用程序所需的权限。因此,可能需要重新启动。

-您还可以使用 WiX 实用程序扩展 CloseApplication() 函数关闭应用程序。 评估您的场景并做适合您的事情。

我想我可能会迟到,但这是解决方案。 Installer team blog post 解释了重启管理器如何决定是否弹出正在使用的文件对话框。具体来说(Windows Installer-Restart Manager Interaction in Detail 部分,项目 3.b。:

If the package is authored such that the services detected by RM would be shutdown because of the authoring of the Service* tables then those services will not be displayed in the files-in-use dialogs.

(斜体是我的)。有用但不是立即有用,因为 这样 并没有真正详细说明。但是由于我的服务导致了与 OP 所描述的相同的问题

<ServiceControl Stop="uninstall" ... />

我刚把值改成了both

<ServiceControl Stop="both" ... />

这可能是唯一剩下的可以让它“如此”的东西,轰隆声、烟花、魔法:

MSI (s) (50:A0) [21:50:30:352]: RESTART MANAGER: Detected that application with id 6408, friendly name 'XXXX', service short name 'xxxx', of type RmService and status 1 holds file[s] in use.
MSI (s) (50:A0) [21:50:30:352]: RESTART MANAGER: Detected that the service xxxx will be stopped due to a service control action authored in the package before the files are updated. So, we will not attempt to stop this service using Restart Manager

看来 both 标记 msidbServiceControlEventStop (0x002) 和 msidbServiceControlEventUninstallStop (0x020) 需要在 ServiceControl table 中设置以使 RM 愉快地断定服务将在文件更新之前停止。


回想起来这是有道理的。由于升级期间的卸载部分是使用 旧缓存 MSI 数据库执行的,因此 RM 不会查看相关产品卸载时会发生什么。严格来说,可能有多个产品需要卸载,Installer 并不要求这些 相关产品 FindRelatedProducts action 找到的那些,包括相同的旧版本升级代码)实际上current包中控制的服务相关。所以它不关心 current 包中脚本中的卸载服务操作(无论如何它不适用于安装操作!)。为了保持一致性,它需要一个简单明了的证据来证明服务将在覆盖正在使用的文件之前停止,仅从当前包中收集此类证据。

因此 RM 很可能只在安装期间关心 msidbServiceControlEventStop (0x002) 标志。