When authoring deferred custom actions (which are custom actions that change the system state) in an MSI, it is necessary to also provide an equivalent set of rollback custom actions to undo the system state change in case the MSI fails and rolls back. The rollback behavior typically needs to behave differently depending on if the MSI is currently being installed, repaired or uninstalled. This means that the following scenarios need to be accounted for when coding and testing a set of deferred custom actions to make sure that they are working as expected during both success and failure cases:
- Successful install
- Failed install
- Successful repair
- Failed repair
- Successful uninstall
- Failed uninstall
The scenario in this blog post provides an example of what can happen when rollback custom actions do not behave the way they should.
The failure cases are often difficult to simulate by unit testing the custom action code directly because deferred custom action code typically depends on state information provided to it by Windows Installer during an active installation session. As a result, this type of testing usually requires fault injection in order to cause the rollback custom actions to be executed at the proper times during real installation scenarios.
Bob Arnson recently posted an item about a useful feature that he recently added to WiX v3.0 to help test rollback custom actions in an MSI. This feature is a simple deferred custom action named WixFailWhenDeferred that will always fail when it is executed during the installation, repair or uninstallation of an MSI. Adding the WixFailWhenDeferred custom action to your MSI allows you to easily inject a failure into your MSI in order to test your rollback custom actions.
There are 3 steps you need to take to use this technique to test the rollback custom actions in your MSI:
1. Add a reference to the WixFailWhenDeferred custom action
To add a reference to this custom action, include the following in your WiX v3.0 setup authoring:
This will cause WiX to add the WixFailWhenDeferred custom action to your MSI, schedule it immediately before the InstallFinalize action and condition it to only run if the property WIXFAILWHENDEFERRED=1.
2. Add a reference to the WixUtilExtension to your WiX project
When running WiX tools directly, this means adding a parameter to the light.exe command line like the following:
light.exe myproject.wixobj -ext WixUtilExtension.dll
When using Votive and Visual Studio for your WiX project, you can add a reference to WixUtilExtension by using the following steps:
- Right-click on the References node of the WiX project in the Solution Explorer and choose Add Reference...
- In the Add Reference dialog, click on the Browse tab.
- Locate WixUtilExtension.dll and click the Add button, then click OK to dismiss the dialog.
3. Set the WIXFAILWHENDEFERRED property to 1 when installing the MSI to trigger rollback
The WixFailWhenDeferred custom action is conditioned to run only when the Windows Installer public property WIXFAILWHENDEFERRED=1. After building an MSI that includes a reference to the WixFailWhenDeferred custom action, the following set of command lines can be used to simulate a set of standard install and rollback testing scenarios:
- Standard install: msiexec.exe /i MyProduct.msi /qb /l*vx %temp%\MyProductInstall.log
- Install rollback: msiexec.exe /i MyProduct.msi /qb /l*vx %temp%\MyProductInstallFailure.log WIXFAILWHENDEFERRED=1
- Standard repair: msiexec.exe /fvecmus MyProduct.msi /qb /l*vx %temp%\MyProductRepair.log
- Repair rollback: msiexec.exe /fvecmus MyProduct.msi /qb /l*vx %temp%\MyProductRepairFailure.log WIXFAILWHENDEFERRED=1
- Standard uninstall: msiexec.exe /x MyProduct.msi /qb /l*vx %temp%\MyProductUninstall.log
- Uninstall rollback: msiexec.exe /x MyProduct.msi /qb /l*vx %temp%\MyProductUninstallFailure.log WIXFAILWHENDEFERRED=1