windows-serviceswindows-installerwix3.6

Can't delete folder WIX installer


I have a service that is installed by my WIX installer:

<Component Id="cmp_myService" Guid="{5FC8815E-33T8-4C3D-9654-849EE4CB1E22}">
    <File Id="f_myService" Name="MyService.exe" Source="$(var.SourcePath)MyService.exe" KeyPath="yes" />
    <ServiceInstall Id="si_myServiceInstall" Name="My Service" DisplayName="It is my service" Type="ownProcess" Interactive="yes" Start="auto" ErrorControl="normal" Description="Here is some description" />
    <ServiceControl Id="sc_startStopMyService" Name="My Service" Start="install" Stop="both" Remove="uninstall" Wait="yes" />
</Component>

Also, I have a custom action ca1_removeInstallDirOnUnsinstall that deletes a folder with all its content (MyService.exe is inside this folder)

<Custom Action="ca1_removeInstallDirOnUnsinstall" After="DeleteServices"><![CDATA[REMOVE="ALL"]]></Custom>

I schedule this custom action to run right after DeleteServices action in the execution sequence. I assume that at this point MyService.exe should be stopped and deleted. But I get an exception Access to the path 'MyService.exe' is denied which means that the service isn't deleted yet. Why does that happen and where my custom action should be scheduled to be sure that the service is already deleted?

Related log files:

MSI (s) (64:3C) [08:42:32:597]: Doing action: StopServices
MSI (s) (64:3C) [08:42:32:597]: Note: 1: 2205 2:  3: ActionText 
Action ended 8:42:32: UnpublishFeatures. Return value 1.
Action start 8:42:32: StopServices.
MSI (s) (64:3C) [08:42:32:597]: Doing action: DeleteServices
MSI (s) (64:3C) [08:42:32:597]: Note: 1: 2205 2:  3: ActionText 
Action ended 8:42:32: StopServices. Return value 1.
Action start 8:42:32: DeleteServices.
MSI (s) (64:3C) [08:42:32:597]: Doing action: ca1_removeInstallDirOnUnsinstall
MSI (s) (64:3C) [08:42:32:597]: Note: 1: 2205 2:  3: ActionText 
Action ended 8:42:32: DeleteServices. Return value 1.
MSI (s) (64:80) [08:42:32:597]: Invoking remote custom action. DLL: C:\windows\Installer\MSIA125.tmp, Entrypoint: RemoveInstallDirRecursively
MSI (s) (64:B8) [08:42:32:597]: Generating random cookie.
MSI (s) (64:B8) [08:42:32:597]: Created Custom Action Server with PID 6176 (0x1820).
MSI (s) (64:98) [08:42:32:644]: Running as a service.
MSI (s) (64:B4) [08:42:32:644]: Hello, I'm your 32bit Impersonated custom action server.
Action start 8:42:32: ca1_removeInstallDirOnUnsinstall.
SFXCA: Extracting custom action to temporary directory: C:\windows\Installer\MSIA125.tmp-\
SFXCA: Binding to CLR version v4.0.30319
Calling custom action CustomAction!CustomAction.FilesAndFoldersCustomAction.RemoveInstallDirRecursively
Access to the path 'MyService.exe' is denied.

Solution

  • InstallFinalize: DeleteServices happens before InstallFiles. I suppose msiexec.exe might have a lock on the folder for potential re-install as part of a major upgrade, but I am not sure (find locks using: perfmon.exe, procexp64.exe). You can put the delete action right before InstallFinalize (which ends elevated operations that change the system). This location should work, but no guarantees. It is not good practice to "nuke" whole folders.

    Best Practice: Generally you should not delete whole folders (it is quite risky - mess something up and you could end up deleting half your computer. Seriously. I have seen it happen. Not with MSI but with a "cleanup EXE"). I would put log files in the user profile or some other location outside of %ProgramFiles%. Or maybe use the event log or even a database to upload to online to avoid too many files left behind.

    Acceptable Remains: In my opinion log files and other user data should not be attempted uninstalled automagically. Why? They are user data - in other words: they belong to the user. You can't just delete them summarily? I would just leave the data in place in case they want to analyze them or re-install the application (the latter is particularly important for license keys - do you leave them behind?).

    WiX: WiX has several built-in constructs to help remove files and folders. There are the built-in MSI varieties (RemoveFile, RemoveFolder) that just delete files by wildcard or name. Then there is the RemoveFileEx described by Bob Arnson (WiX developer team). Please read the blog post. I believe it can do the job recursively (all sub folders). As you understand I don't use these constructs much since I favor other cleanup approaches. Quite frankly I just try to document the cleanup procedure in a PDF or an online KDB article.


    Links: