Recently I put together a Windows Installer package (MSI file) for our team to use internally. The package installs a system tray app (sometimes called a notification app or notification area app). One common property of system tray apps is that they don’t actually close on exit — they simply return to the system tray and continue running in the background.
In order to properly uninstall this component, the app needs to be shut down by the installer/uninstaller. Typically, in WiX, you’d do this with the CloseApplication element, which sends a WM_CLOSE message to the system tray app, which it ignores completely. What to do?
Here was my solution, embedded in the WiX WXS source file (toolset v3.9):
<?define SysTrayAppExe = "mySysTrayApp.exe" ?> <CustomAction Id="KillApp" Directory="InstallDir" Return="ignore" ExeCommand=""[SystemFolder]taskkill.exe" /F /IM "$(var.SysTrayAppExe)"" />
First, we define a custom action that will execute the taskkill Windows system command. We ignore the return value because we don’t care if the app was actually running. Just in case the Windows system folder has embedded spaces, we wrap the command in quotes using the XML quote specifier. The /F flag requests a forceful termination, and the /IM flag requests that we terminate the specifically named process, which is again wrapped in quotes in case it has embedded spaces.
Next, we set up the custom action to execute in the first phase of the install process. This ensures that the system tray app is always shut down when the install/uninstall begins.
Ideally, we’d use the CAQuietExec special custom action to avoid a command screen from briefly flashing on the screen, but CAQuietExec must be run after the InstallInitialize phase, by which time the Windows Installer has already detected that the system tray app is running, and it’s too late for us to do anything about it.