6

I have a custom action that adds files to the installation directory. When the program is being uninstalled, another custom action tries to remove those files, so that the installation directory can be deleted.

The problem is that my custom uninstallation action runs after the removal of standard install files, so the installation directory is left there, although it's empty.

The config looks similar to this:

<CustomAction Id="AddFilesAction" BinaryKey="installerActions" DllEntry="AddFiles" Execute="deferred" Return="check" Impersonate="no" />
<CustomAction Id="CleanupAction" BinaryKey="installerActions" DllEntry="Cleanup" Execute="deferred" Return="check" Impersonate="no" />

<InstallExecuteSequence>
  <Custom Action="CleanupAction" Before="InstallFiles">Installed</Custom>
  <Custom Action="AddFilesAction" After="InstallFiles">NOT Installed</Custom>
</InstallExecuteSequence>

Can I make the CleanupAction run before the msi starts removing installation files, so that the custom file is already removed and msi can remove the main installation dir?

František Žiačik
  • 7,511
  • 1
  • 34
  • 59

3 Answers3

10

The problem is that my custom uninstallation action runs after the removal of standard install files

That's because you have scheduled it before InstallFiles, which comes after RemoveFiles in a standard InstallExecuteSequence. You can also open your MSI file in an editor like Orca or InstEd and have a look at the InstallExecuteSequence table. Sort it by the Sequence column to see the order of execution.

Can I make the CleanupAction run before the msi starts removing installation files

Sure, just schedule it before RemoveFiles:

<Custom Action="CleanupAction" Before="RemoveFiles">
    (REMOVE~="ALL") AND (NOT UPGRADINGPRODUCTCODE)
</Custom>

Edit: I have also improved the custom action condition after Stein Åsmul made me aware of it. See his answer for the detailed reasoning.


In case you don't already know, WiX already has support for removing application-generated files which may be able to replace your custom action. It comes in the form of the RemoveFile and util:RemoveFolderEx elements.

In case these don't fulfill your needs, so you still need a custom action, I suggest to add temporary records for the files to be removed to the RemoveFile table at runtime (in an immediate custom action!). This gives you the benefits of using the MSI engine for the actual file removal, i. e. automatic rollback if the user decides to cancel the uninstallation or when an error happens. I have done so in the past (before RemoveFolderEx was invented), so if you need more information, just ask another question.

zett42
  • 25,437
  • 3
  • 35
  • 72
  • Perfect. Thank you. Adding the temporary records sounds interesting, I'll add another question if I won't be able to figure it out. Thanks – František Žiačik Jun 19 '18 at 07:28
  • Btw, I tried to replace the custom cleanup action with RemoveFolderEx, but it worked the same as when my cleanup action was running after RemoveFiles. The folder (actually a subfolder inside of the install folder) got removed, but the root install folder (now empty) remained in place after the uninstall finished. Not sure why. – František Žiačik Jun 20 '18 at 07:55
  • 1
    @FrantišekŽiačik Try to add a `` element to a component that resides in the root install folder. – zett42 Jun 20 '18 at 09:32
  • Yes, this works, although it looks a little bit hacky to me. – František Žiačik Jun 20 '18 at 09:58
7

Short Answer: Your condition and sequencing seems to be wrong. Please schedule your cleanup custom action to run before RemoveFiles and maybe set a better condition to make the action run only when desired (not in unexpected setup modes). Below I suggest (REMOVE~="ALL") AND (NOT UPGRADINGPRODUCTCODE). If you use this condition, please test thoroughly. This condition is explained below.

Quick Sample:

<InstallExecuteSequence>
  <Custom Action="CleanupAction" 
          Before="RemoveFiles">(REMOVE~="ALL") AND (NOT UPGRADINGPRODUCTCODE)</Custom>
</InstallExecuteSequence>

Please be sure to read the details below as well. You might also want to tighten the condition for your copy files action - since it otherwise will run on major upgrade as well - which may or may not be what you want.


Custom Action Alteratives: Please avoid custom actions if you can - summary of some custom action problems - they are serious). Custom actions are the leading cause of deployment failure. Are you sure you need them? Very often there are other ways to achive what you implement in custom actions using built-in MSI features or WiX-specific constructs. Common examples are: installing services, deleting files, updating XML files or INI files, etc... Sometimes custom actions are necessary though - obviously. Zett42 has already written well about the alternatives, so I won't repeat it here - please check his / her answer.


RemoveFiles: There are further problems here - and I will try to describe these below - but files are uninstalled when the standard action RemoveFiles runs. In other words you need to schedule the cleanup custom action to run before this standard action in the InstallExecuteSequence.


Condition: Your condition Installed for your cleanup custom action will make the custom action run on modify, repair and minor upgrade patching in addition to uninstall and major upgrade initiated uninstalls. This is most likely not what you want. To specify to run on uninstall only, the most run-of-the-mill condition is REMOVE~="ALL". This will make the cleanup happen on a manually initiated uninstall and also on a major upgrade initiated uninstall (not what you want I think). You could try (REMOVE~="ALL") AND (NOT UPGRADINGPRODUCTCODE) (run only on regular uninstall - not on major upgrade uninstalls).


Tips: Conditions are very easy to mess up - even for experienced WiX / MSI users. Some resources that might help:


Some Further Links (for reference):

Stein Åsmul
  • 39,960
  • 25
  • 91
  • 164
  • Thanks for another view. We have two reasons for using custom actions. The customer has to select a zip file during installation that must be unzipped in the installation directory. Second one is that we need to update java properties file (not an ini file). – František Žiačik Jun 19 '18 at 07:30
  • Good catch of the incorrect custom action condition. – zett42 Jun 19 '18 at 09:16
  • Btw, I have the product Id fixed (like `Id="SOME-GUID"` instead of `Id="*"`) because I need to be able to do automated uninstallation in a script like `msiexec /x {SOME-GUID}`. But this way, it doesn't allow me to do upgrades, I think. – František Žiačik Jun 19 '18 at 11:18
  • 2
    You need to change your product GUID in order to use major upgrades for your package. Do you use [minor upgrades](https://msdn.microsoft.com/en-us/library/windows/desktop/aa370037(v=vs.85).aspx) only? It is relatively easy to [query a machine to determine what MSI packages are installed (to find the product GUID)](https://stackoverflow.com/a/29937569/129130) - even remotely if your AD is set up correctly. [Here is a blurb on retrieving upgrade codes from remote machines](https://stackoverflow.com/a/46637095/129130). – Stein Åsmul Jun 19 '18 at 12:53
0

I would not schedule your actions between InstallInitialize and InstallFinalize. Place your files before Initialize and cleanup your files after Finalize. Beware that you will lose your property values after InstallFinalize and you'll need to account for that.

Doc
  • 698
  • 3
  • 16
  • 1
    Custom actions run after `InstallFinalize` or before `InstallInitialize` will not run elevated, and hence could bomb out with error messages when trying to perform privileged operations - unless the whole setup is launched from a command prompt with admin rights (or equivalent mechanism). *Running with **elevated rights** means that standard users run with temporary admin rights during the install* (between InstallInitialize and InstallFinalize), whereas *a setup launched with "real" admin rights means the whole process runs with admin rights*. Corporate deployment uses elevated rights. – Stein Åsmul Jun 18 '18 at 21:53
  • 1
    It is also a violation of Windows Installer best practice to change the system outside the "transaction" - which is defined between `InstallInitialize` and `InstallFinalize`. All changes to the system are to be performed in this sequence, and the changes should be properly rolled back if the installation fails. Frankly I don't know why it is possible to insert custom actions after `InstallFinalize` - you can't insert `deferred mode custom actions` there, but `immediate mode custom actions` can be inserted. – Stein Åsmul Jun 18 '18 at 21:58