In my previous post about creating Custom Actions with WiX, I showed how to create a custom action project and invoke the custom action from a setup project.  In my particular case, the custom action was making several edits to an existing web.config file to incorporate a new HTTP module that was being installed.  Since these edits to the web.config are not automatically removed during uninstall of the HTTP module, I need a compensating custom action to perform this task.

Since I already have a custom action project and the associated CustomActions class from the previous post, the easiest thing to do is to incorporate the custom action for the uninstall into the same class (Remember, a custom action is represented as a static method annotated with the CustomAction attribute that ultimately gets generated into a DLL entry point that is callable by the MSI engine).  So to add the new custom action, I simply need to add another method to my CustomActions class.  The original custom action to make the web.config edits and the new custom action to remove the edits on uninstall are shown below.

 1: public class CustomActions
 2: {
 3:     [CustomAction]
 4:     public static ActionResult ConfigurEwsFilter(Session session)
 5:     {
 6:         try
 7:         {
 8:             session.Log("Begin Configure EWS Filter Custom Action");
 9:  
 10:             // TODO: Make changes to config file
 11:  
 12:             session.Log("End Configure EWS Filter Custom Action");
 13:         }
 14:         catch (Exception ex)
 15:         {
 16:             session.Log("ERROR in custom action ConfigureEwsFilter {0}", 
 17:                         ex.ToString());
 18:             return ActionResult.Failure;
 19:         }
 20:  
 21:         return ActionResult.Success;
 22:     }
 23:  
 24:     [CustomAction]
 25:     public static ActionResult RemoveEwsFilterConfig(Session session)
 26:     {
 27:         try
 28:         {
 29:             session.Log("Begin Remove EWS Filter Configuration Custom Action");
 30:  
 31:             // TODO: Remove changes to config file
 32:  
 33:             session.Log("End Remove EWS Filter Configuration Custom Action");
 34:         }
 35:         catch (Exception ex)
 36:         {
 37:             session.Log("ERROR in custom action RemoveEwsFilterConfig {0}", 
 38:                         ex.ToString());
 39:             return ActionResult.Failure;
 40:         }
 41:  
 42:         return ActionResult.Success;
 43:     }
 44: }

As with the original custom action, I need a reference to the new custom action in the *.wxs file.  The original Binary element and CustomAction element are shown along with the new CustomAction element (lines 8-12) to remove the configuration changes.

 1: <Binary Id="EwsAction.CA.dll" 
 2:         src="..\EwsAction\bin\$(var.Configuration)\EwsAction.CA.dll" />
 3: <CustomAction Id="ConfigurEwsFilter" 
 4:               Return="check" 
 5:               Execute="immediate" 
 6:               BinaryKey="EwsAction.CA.dll" 
 7:               DllEntry="ConfigurEwsFilter" />
 8: <CustomAction Id="RemoveEwsFilterConfig" 
 9:               Return="check" 
 10:               Execute="immediate" 
 11:               BinaryKey="EwsAction.CA.dll" 
 12:               DllEntry="RemoveEwsFilterConfig" />

The only differences between the two CustomAction elements are the Id and DllEntry attributes, where the DllEntry attribute indicates the CustomAction method to invoke.

Up to this point, the process of adding the compensating CustomAction is exactly the same as adding the original CustomAction.  However, I only want the new custom action to run during uninstall of the HTTP module.  I can control that with a condition on the custom action’s entry in the InstallExecuteSequence.

 1: <InstallExecuteSequence>
 2:   <Custom Action="AssignConfigFile" After="CostFinalize" />
 3:   <Custom Action="ConfigurEwsFilter" Before="InstallFinalize">
 4:     NOT Installed
 5:   </Custom>
 6:   <Custom Action="RemoveEwsFilterConfig" Before="InstallFinalize">
 7:     Installed AND NOT UPGRADINGPRODUCTCODE
 8:   </Custom>
 9: </InstallExecuteSequence>

The entry for the new custom action is shown on lines 6-8 above.  Line 7 provides the condition when the custom action should be run.  In this case it references the Installed and UPGRADINGPRODUCTCODE properties, indicating that the product should already be installed and an upgrade to the product is not being performed (i.e. during uninstall). 

You’ll also notice the condition NOT Installed added to the original custom action performing the configuration edits.  That’s because I want it to only run during install, not during uninstall, otherwise I’ll get my edits added back into the web.config file.