The installation sequence is divided into two main stages; immediate and deferred. The main reason of dividing an installation into two phases (UI and Execution) is supplying roll-back if an error occurs. All actions in the execution phase, between InstallInitialize
and InstallFinalize
are included in the roll-back, which is called the deferred stage. The initial phase, where the roll-back script is being prepared but the roll-back protection has not started yet is called immediate stage.
The UI sequence does not have any roll-back feature, therefore actions that alter the system should never take place there.
Custom actions that make system changes should be marked as deferred
and should be scheduled to run between InstallInitialize
and InstallFinalize
in the execution phase. Roll-back actions should be supplied by the developer for custom actions:
…
<CustomAction Id=”myaction” Execute=”deferred” Return=”check” />
…
<InstallUISequence>
<Custom Action=”myaction” After=”CostFinalize” />
<Custom Action=”myaction2” After=”myaction” />
</InstallUISequence>
<InstallExecuteSequence>
<Custom Action=”myaction3” After=”InstallInitialize” />
<Custom Action=”myaction4” After=”myaction3” />
</InstallExecuteSequence>
Custom actions that are deferred execute during the Execute sequence’s rollback-protected phase. To give those actions rollback capabilities, separate custom actions that undo the work should be authored. Rollback actions are scheduled before the action they are meant to revert in case of an error:
<CustomAction Id="systemChangingCA" Execute="deferred" Script="vbscript">
msgbox "Your system has been changed"
</CustomAction>
<CustomAction Id="rollbackSystemChangingCA" Execute="rollback" Script="vbscript">
msgbox "System changes are undone"
</CustomAction>
<CustomAction Id="causeError" Execute="deferred" Script="vbscript">
Err.Raise 507
</CustomAction>
These are scheduled as:
<InstallExecuteSequence>
<Custom Action="rollbackSystemChangingCA" Before="systemChangingCA" />
<Custom Action="systemChangingCA" After="InstallInitialize" />
<Custom Action="causeError" After="systemChangingCA" />
</InstallExecuteSequence>
In this example, systemChangingCA
will run during deferred phase of InstallExecuteSequence
. When causeError
runs afterwards, it causes an exception to be thrown, which triggers a rollback. Then rollbackSystemChangingCA
runs.
Wix toolset also provides its own custom action for stimulating a rollback called WixFailWhenDeferred
. It is a part of WixUtilExtension
and you could use it to test your rollback methods.
The folders created by WiX itself should be removed automatically during a rollback.
You can learn more about the overall concept of Microsoft Installer and WiX from the book of Nick Ramirez: "A Developer's Guide to Windows Installer XML". It has many examples and cases including the ones above.