Bill Zissimopoulos
Bill Zissimopoulos

Reputation: 555

Wix and Deferred Custom Action Result

SHORT VERSION

Is it possible to set an MSI property from a deferred custom action written in C++? If, as I suspect, it is not, is there any other way of communicating the result of a deferred custom action to the installer so that I can then make a conditional ScheduleReboot decision?

TLDR VERSION

I am creating a Wix installer for an open source file system driver (WinFsp). The installer must install the driver (FSD) and a DLL. After some research I determined that the Windows Installer does not cleanly support the installation of such drivers. Nevertheless I have now created an MSI, which handles the install/uninstall rather well.

There is however a gotcha. For a variety of reasons this FSD cannot be stopped or unloaded. When the user initiates an uninstall, the MSI is able to delete the FSD service from the SCM and then delete the FSD file. This is possible even when the FSD service is not stopped. [This is by design with such drivers on Windows!]

The issue is that although the uninstall has completed, the FSD is still loaded by Windows. In such cases I would like to execute a ScheduleReboot with a ServiceExists condition determined by a custom action:

    <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>

Unfortunately I am finding that it appears that it is not possible to execute WcaSetIntProperty from a deferred custom action. On the other hand I am a complete n00b when it comes to Wix and just a couple of days ago I had not heard anything about it. So feel free to explain to me how to do things.

PS: I am aware of ServiceInstall, but it does not support file system drivers. Futhermore the Wix Difxapp extension does not appear to be the best solution for file system drivers (my driver is a legacy driver and does not have an INF file).

UPDATE

Thinking about this problem further two ideas come to mind:

  1. Use RegistrySearch to check the registry directly for the existence of the FSD instead of through the Service Control Manager. It is not clear to me whether it is possible to run a RegistrySearch after files have been removed.
  2. Instead of running a ServiceExists deferred custom action after RemoveFiles, perhaps I should be running a ServiceRunning immediate custom action that simply checks if the FSD is running (on uninstall). This is the only scenario where the FSD service will not be properly deleted but simply marked for delete by DeleteService. I will try this approach and post it as the answer if it works.

Upvotes: 1

Views: 1314

Answers (1)

Bill Zissimopoulos
Bill Zissimopoulos

Reputation: 555

As promised in the last update here is my answer.

I created a ServiceRunning immediate custom action that calls QueryServiceStatus on the file system driver (FSD). The immediate custom action is able to execute WcaSetIntProperty without issues. I use the returned property to ScheduleReboot if the driver is running (because the FSD cannot be stopped/unloaded and will therefore remain "pending delete" after uninstallation).

Here is the relevant Wix code:

    <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>

Upvotes: 2

Related Questions