Let's suppose we want to export one custom action method and make it multipurposed. In other words, if we call into while deferred execution we should do one thing but if we call into it for rollback execution we should do another.
How would we do that? The answer is in the BOOL MsiGetMode( __in MSIHANDLE hInstall, __in MSIRUNMODE iRunMode) Windows Installer function exposed by the Session.GetMode( InstallRunMode installRunMode ) DTF method.
Consider the following WiX Code:
You'll notice we have 2 custom actions pointing to the same exported function in the binary table. Also notice that one custom action is scheduled as deferred and the other rollback. The deferred is scheduled right after the rollback. This results in Windows Installer scheduling RollbackCA after InstallInitialize and DeferredCA after RollbackCA.
Now let's look at the code inside of that Custom Action.
public class CustomActions
public static ActionResult CustomAction1(Session session)
ActionResult result = ActionResult.Success;
MessageBox.Show("I'm not in rollback, let's cause one to occur!");
result = ActionResult.Failure;
MessageBox.Show("We are now in a rollback." );
At runtime, RollbackCA is skipped and DeferredCA is executed. The CustomAction1 method will be executed by the DeferredCA and it will detect that it's scheduled in deferred mode and display a message saying that it's not in rollback and that it'll cause one. It does so by returning a failure to Windows Installer. Now MSI starts walking the script backwards executing any rollback CA it finds. This causes it to run RollbackCA and once again we are inside of the CustomAction1 method.
This time we will evaluate that we are in rollback, display a message stating so and quit gracefully. At this point the product is not installed.
This pattern can be extended so that CustomAction1 also handles immediate execution, implicit scheduling of the deferred custom actions and CustomActionData serialization/deserialization. But I'll leave that for another blog.
Thanks for this it helps a bit but I was really trying to see how I could use the same CA for install and uninstall. For your other reply I think the only way to do this is to pass in the REMOVE property.
I'll show how to do that tonight when I get some more free time.
Thanks, this is helpful. As far as best practices go, if I need to track some information or backup files so that, should a rollback occur, I can undo whatever I just did, is there a common place to put those? For example, if I store values in my C# custom action class, I probably can't bank on those values being present whenever the rollback invokes my class. Should I put those in a temporary MSI table perhaps? If my custom action installs widgets from a Widgets table, maybe I should just create and populate a WidgetsRollback table on-the-fly...
Post a Comment