Wix 和延迟的自定义操作结果

Wix and Deferred Custom Action Result

简短版本

是否可以从用 C++ 编写的延迟自定义操作中设置 MSI 属性?如果,正如我所怀疑的那样,是否有任何其他方式将延迟自定义操作的结果传达给安装程序,以便我可以做出有条件的 ScheduleReboot 决定?

TLDR 版本

我正在为开源文件系统驱动程序 (WinFsp) 创建 Wix 安装程序。安装程序必须安装驱动程序 (FSD) 和 DLL。经过一些研究,我确定 Windows 安装程序不完全支持此类驱动程序的安装。尽管如此,我现在已经创建了一个 MSI,它可以很好地处理 install/uninstall。

但是有一个陷阱。由于各种原因,无法停止或卸载此 FSD。当用户启动卸载时,MSI 能够从 SCM 中删除 FSD 服务,然后删除 FSD 文件。即使 FSD 服务未停止,这也是可能的。 [这是 Windows 上此类驱动程序的设计!]

问题是虽然卸载已经完成,但Windows仍然加载了FSD。在这种情况下,我想执行 ScheduleRebootServiceExists 由自定义操作确定的条件:

    <Binary Id="CustomActions" SourceFile="..\build$(var.Configuration)\CustomActions.dll" />
    <CustomAction
        Id="Param.ServiceExists"
        Property="A.ServiceExists"
        Value="$(var.MyProductName)" />
    <CustomAction
        Id="A.ServiceExists"
        BinaryKey="CustomActions"
        DllEntry="ServiceExists"
        Execute="deferred"
        Impersonate="no"
        Return="ignore" />
    <InstallExecuteSequence>
        <Custom Action="Param.ServiceExists" Before="A.ServiceExists">REMOVE ~= "ALL"</Custom>
        <Custom Action="A.ServiceExists" After="RemoveFiles">REMOVE ~= "ALL"</Custom>
        <ScheduleReboot After="A.ServiceExists">(REMOVE ~= "ALL") AND SERVICEEXISTS</ScheduleReboot>
    </InstallExecuteSequence>

不幸的是,我发现似乎无法从延迟的自定义操作中执行 WcaSetIntProperty。另一方面,当谈到 Wix 时,我完全是个 n00b,就在几天前,我还没有听说过任何关于它的消息。请随时向我解释如何做事。

PS:我知道 ServiceInstall,但它不支持文件系统驱动程序。此外,Wix Difxapp 扩展似乎不是文件系统驱动程序的最佳解决方案(我的驱动程序是旧版驱动程序,没有 INF 文件)。

更新

进一步思考这个问题,我想到了两个想法:

  1. 使用RegistrySearch直接检查注册表是否存在 FSD,而不是通过服务控制管理器。我不清楚在删除文件后是否可以 运行 a RegistrySearch
  2. 不是 运行在 RemoveFiles 之后执行 ServiceExists 延迟自定义操作,也许我应该 运行 执行 ServiceRunning 即时自定义操作检查 FSD 是否 运行ning(卸载时)。这是 FSD 服务不会被正确删除而只是被 DeleteService 标记为删除的唯一场景。我会尝试这种方法,如果可行,post 它将作为答案。

正如上次更新中承诺的那样,这里是我的答案。

我创建了一个 ServiceRunning 即时自定义操作,它在文件系统驱动程序 (FSD) 上调用 QueryServiceStatus。即时自定义操作能够毫无问题地执行 WcaSetIntProperty。如果驱动是运行,我使用返回的属性到ScheduleReboot(因为FSD不能是stopped/unloaded,因此卸载后会保持"pending delete")。

这里是相关的 Wix 代码:

    <Binary Id="CustomActions" SourceFile="..\build$(var.Configuration)\CustomActions.dll" />
    <CustomAction
        Id="Params.ServiceRunning"
        Property="ServiceRunning"
        Value="$(var.MyProductName)" />
    <CustomAction
        Id="Action.ServiceRunning"
        BinaryKey="CustomActions"
        DllEntry="ServiceRunning"
        Execute="immediate"
        Return="ignore" />
    <InstallExecuteSequence>
        <Custom Action="Params.ServiceRunning" Before="Action.ServiceRunning">REMOVE ~= "ALL"</Custom>
        <Custom Action="Action.ServiceRunning" After="RemoveFiles">REMOVE ~= "ALL"</Custom>
        <ScheduleReboot After="Action.ServiceRunning">
            <![CDATA[(REMOVE ~= "ALL") AND (0 <> ServiceRunning)]]>
        </ScheduleReboot>
    </InstallExecuteSequence>