MSI 安装程序 InstallValidate 如何确定正在使用的文件?

How does the MSI Installer InstallValidate determine files in use?

我正在研究如何防止重启管理器在我卸载应用程序时检测到需要重启。

我的应用程序有一个 Windows 服务,它使用本机 DLL (Tanuki Wrapper) 并创建另一个写入某些日志文件的进程 (Java)。我使用的是 WiX 工具集,但我对 MSI 安装程序本身更感兴趣。我一直在尝试使用 Orca 手动进行一些更改。作为参考,相关的 WiX 配置是(我省略了一些 JAR 等其他组件):

<DirectoryRef Id="logs3327407xx">
    <Component Guid="344ec345-bdd7-4c1d-801f-55ddf9e07735" Id="logs_wrapper_log88580873x">
        <File DiskId="1" Id="fl_logs_wrapper_log88580873x" Name="wrapper.log" Source="logs\wrapper.log"/>
    </Component>
</DirectoryRef>
<DirectoryRef Id="bin97543xxxx">
    <Component Guid="67c93dd8-36ad-427f-9d79-64a07c719eea" Id="bin_wrapper_windows_x86_64_exe189026768">
        <File DiskId="1" Id="fl_bin_wrapper_windows_x86_64_exe189026768" KeyPath="yes" Name="wrapper-windows-x86-64.exe" Source="bin\wrapper-windows-x86-64.exe"/>
        <ServiceInstall Account="LocalSystem" Arguments="-s &quot;..\conf\wrapper.conf&quot; wrapper.console.flush=true" Description="My Example Service." DisplayName="My Service" ErrorControl="ignore" Id="srvc_i_bin_wrapper_windows_x86_64_exe189026768" Interactive="no" Name="MyService" Start="auto" Type="ownProcess" Vital="yes"/>
        <ServiceControl Id="srvc_c_bin_wrapper_windows_x86_64_exe189026768" Name="MyService" Remove="uninstall" Start="install" Stop="both" Wait="yes"/>
    </Component>
</DirectoryRef>
<DirectoryRef Id="bin_wrapper_native490235675">
    <Component Guid="d7e4295a-1ce5-4dd2-aa92-230caac34247" Id="bin_wrapper_native_wrapper_windows_x86_64_dll156404367">
        <File DiskId="1" Id="fl_bin_wrapper_native_wrapper_windows_x86_64_dll156404367" Name="wrapper-windows-x86-64.dll" Source="bin\wrapper\native\wrapper-windows-x86-64.dll"/>
    </Component>
</DirectoryRef>

我了解 InstallValidate 操作中存在确定文件是否正在使用的逻辑。它将根据 MSIRESTARTMANAGERCONTROL property.

使用 Restart Manager 或 FilesInUse

如果我使用重启管理器,它会打开一个对话框,提示需要重启。日志说:

MSI (s) (1C:7C) [12:27:14:679]: Doing action: InstallValidate
Action ended 12:27:14: MigrateFeatureStates. Return value 0.
MSI (s) (1C:7C) [12:27:14:679]: PROPERTY CHANGE: Deleting MsiRestartManagerSessionKey property. Its current value is 'f2947dee632d694f8b4f1795ff254092'.
...
MSI (s) (1C:7C) [12:27:14:679]: Component: bin_wrapper_windows_x86_64_exe189026768; Installed: Local;   Request: Absent;   Action: Absent;   Client State: Local
MSI (s) (1C:7C) [12:27:14:679]: Component: bin_wrapper_native_wrapper_windows_x86_64_dll156404367; Installed: Local;   Request: Absent;   Action: Absent;   Client State: Local
MSI (s) (1C:7C) [12:27:14:679]: Component: logs_wrapper_log88580873x; Installed: Local;   Request: Absent;   Action: Absent;   Client State: Local
...
MSI (s) (1C:7C) [12:27:14:741]: RESTART MANAGER: Detected that application with id 11368, friendly name 'java.exe', of type RmCritical and status 1 holds file[s] in use.
MSI (s) (1C:7C) [12:27:14:741]: RESTART MANAGER: Did detect that a critical application holds file[s] in use, so a reboot will be necessary.
MSI (s) (1C:7C) [12:27:14:741]: Note: 1: 1610 

它实际上并没有说文件,但如果我禁用重新启动管理器并改用 FilesInUse,则不会出现任何对话框。这次日志说:

Info 1603. The file C:\...\wrapper-windows-x86-64.exe is being held in use by the following process: Name: wrapper-windows-x86-64, Id: 11004, Window Title: '(not determined yet)'. Close that application and retry.
MSI (s) (1C:8C) [12:33:23:458]: 2 application(s) had been reported to have files in use.
Info 1603. The file C:\...\wrapper-windows-x86-64.dll is being held in use by the following process: Name: java, Id: 8284, Window Title: '(not determined yet)'. Close that application and retry.
MSI (s) (1C:8C) [12:33:23:458]: Note: 1: 2727 2:  
MSI (c) (AC:28) [12:33:23:458]: File In Use: -wrapper-windows-x86-64- Window could not be found. Process ID: 11004
MSI (c) (AC:28) [12:33:23:458]: File In Use: -java- Window could not be found. Process ID: 8284
MSI (c) (AC:28) [12:33:23:458]: No window with title could be found for FilesInUse

有人可以解释一下 InstallValidate 如何确定正在使用的文件吗?

此外,如何防止重启管理器说文件正在使用中,一旦服务停止,这些文件将被释放?

作为附带问题,为什么重新启动管理器不显示 MsiRMFilesInUse 对话框?我已经检查了 MsiRMFilesInUse Dialog 上的所有要求,据我所知它们都是真实的。

我记得在某处读到,使用 ServiceControl 而不是自定义操作有助于重新启动管理器知道 Component 中的那些 File 被服务使用。我已经尝试将文件添加到 Component 但它似乎没有任何区别。

与 ServiceControl 相关的 InstallValidate 和正在使用的文件的行为并不复杂。如果某个服务正在使用某些文件,并且该服务位于 ServiceControl table 中并标记为在卸载时停止,那么它将忽略这些文件的使用行为。将文件放在同一个组件中没有区别,显然 Windows 无法知道服务代码将关闭导致文件使用情况的进程。据我所知,没有办法告诉 InstallValidate 某些文件在应该被卸载时实际上不会被使用。

当不使用 Restart Manager 时,您不会看到旧式的 FilesInUse 对话框,因为它需要一个活动的 window,可以提示用户关闭以关闭应用程序。这就是没有活动 window 的托盘应用程序不会导致 FilesInUse 对话框的原因。我不能确定,但​​在我看来,第一个引用 Restart Manager 的日志提取实际上是 RMFilesInUse,我认为这就是您看到的对话框。

如果您看到的唯一文件使用问题与服务将关闭的 java 进程有关,那么可能的解决方案是:

  1. 卸载期间禁止显示所有正在使用的文件对话框。
  2. 使用 ServiceControl 关闭您的服务。
  3. 确保您的 ServiceControl 为 Wait=1,并且在 java 进程终止之前它不会响应 "stop service" 请求,前提是您可以明确将其关闭。

Windows 并不是愚蠢到它会强制重启只是因为文件在 InstallValidate 时正在使用,但在实际删除时并非如此。因此,如果您可以确保文件在实际需要时不再使用 removing/replacing/whatever,则无需重新启动。看到对话框然后发现实际上不需要重新启动的情况并不少见。因此,如果您取消该对话框并关闭所有内容,您将看不到重新启动请求。