Launching services with a GUI from a Supervisor running in a Windows service

We ran into a scenario a while back with a service that launches a browser which acts as a front end to a POS system. When the Habitat Supervisor runs in a Windows service, there is no interactive desktop and the browser fails to visibly appear. The initial work around was to launch the supervisor through a PS Job. This worked fine in modern powershell versions but had some unwanted behavior on Windows 7 nodes with PSv2 where the jobs would terminate if the powershell window closed.

We found a better solution where we ran habitat in a Windows service but had the run hook create a scheduled task that launches the browser. This eliminates the need for PS jobs altogether. Here is an example run hook using this pattern:

Write-Host "creating gui task"
# Note this creates a scheduled task to run under an interactive session (/IT)
# It will run under the user account that is logged in
schtasks.exe /create /RU USERS /TN gui /TR "'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe' --new-window http://google.com" /IT /RL HIGHEST /SC ONCE /ST 00:00 /f
Write-Host "Starting gui task..."
schtasks.exe /run /tn gui

# The task should start immediately but just in case we will allow some time
# for it to start. This might be unnecesary.
Start-Sleep -Seconds 5

try {
    # If no one has logged in, the task will not be in the Running state
    # and this while loop will be skipped. Otherwise this should keep the hab
    # hook running for as long as the task is running. If someone closes the
    # browser, the task will end and exit this loop.
    # There are some processes that I experimented with like calc.exe or chrome
    # without --new-window when a chrome window is already open where the task
    # would end immediately but the window would remain open. If that ends up
    # being the case with gui's "real" services, we can come up with some ways
    # to capture the PID of the process and watch it instead of the task.
    while ((ConvertFrom-CSV $(schtasks.exe /query /tn gui /fo csv)).Status -eq "Running") {
        Start-Sleep -Seconds 1
    }

    # Someone has either closed the browser or there is no interactive desktop
    Write-Host "Task is not running. Current state: $((ConvertFrom-CSV $(schtasks.exe /query /tn gui /fo csv)).Status)"
} finally {
    # Either someone closed the browser or the hab service is stopping
    if((ConvertFrom-CSV $(schtasks.exe /query /tn gui /fo csv)).Status -eq "Running") {
        Write-Host "service is stopping ending task"
        schtasks.exe /end /tn gui
    }
}

Hopefully the comments fill in the details of what is happening. This seems to work in our scenario but could possibly suffer some problems in other scenarios. For example, I found that some binaries like calc.exe or even chrome where you already have a window running, will launch the GUI process but the task will then immediately end. I’m guessing that the process first invoked is handing off the app to another process unattached to the first but have not researched.

I can certainly imagine of some strategies for dealing with such a case where you have the task instead launch a powershell script that spawns the GUI and log the PID of that gui to a file that the hook would monitor. We did not take that route since it clearly makes this more complicated and has so far been unnecessary.