Creating a setup package with multiple optional custom actions#

I've recently needed to create an installer with custom actions that only need to be run when the user selects certain options in the installation wizard. It's pretty easy to do once you know the pitfalls, but it took me some time to figure out so I figured I might as well post it for future reference...

One option is to create a separate assembly for each custom action that can be selected, and then setting its Condition property so that it is only executed if the user chose the corresponding option.

However, this could result in quite a lot of extra assemblies (currently three in my case, but it might become more), which I try to avoid to make it easier to deploy and use these custom actions. So I wanted one assembly containing all the installer classes that could need to be run, and then decide inside that assembly which ones to execute based on the user's choice. This is what worked for me:

  • Pass in the user's choices (e.g. the property values of the checkboxes) through the custom action's CustomActionData property, e.g. "/OPTION1=[OPTION1] /OPTION2=[OPTION2]" in case there are two checkboxes with OPTION1 and OPTION2 properties. Also pass in all other data that needs to be available for the individual installers (note that these may conflict, in which case you'll have to create different assemblies anyway).
  • Mark each individual installer with the [RunInstaller(false)] attribute. Otherwise, the installer will be executed no matter what. Side note: my first attempt at solving this problem was to check inside each installer's Install method if it needed to be executed based on the context parameters, but this caused all sorts of problems with the savedState dictionary.
  • Create one main installer class with [RunInstaller(true)]. This is the one we will add the decision-making logic in, and which will keep the master savedState dictionary.
  • In this main installer, override the Install method and, before calling base.Install, add the individual installers to its Installers property depending on the parameters that were passed in. Also, remember which installers were added (which options were selected) by putting them in the stateSaver object. Example:
    if (this.Context.Parameters["OPTION1"] == "1")
    {
      this.Installers.Add(new Option1Installer());
      stateSaver["OPTION1"] = "1";
    }
    It's important to only call base.Install after this, because the base implementation will then install all the child installers that were just added.
  • Override the Commit and Rollback methods, and before calling the base class implementation, add the installers again depending on the values you put in the savedState object. This is necessary because these methods will be called on new instances of your installer class, so you need to re-add them to the Installers property. Example:
    if (savedState["OPTION1"] == "1")
    {
      this.Installers.Add(new Option1Installer());
    }
  • Do the same thing for the Uninstall method, this way you are sure that you're only uninstalling the parts that were actually installed. This is important, because when uninstalling, the parameters that are passed in do not remember the user's initial choices (i.e. OPTION1 might be set to "1" when uninstalling, even if the user didn't select that choice at installation time).

This is what worked for me, if there are simpler ways of achieving the same thing, don't hesitate to make suggestions :-)

Blog | Programming | .NET | VS.NET
Comments are closed.
All content © 2012, Jelle Druyts
On this page

Recent Photos
www.flickr.com
This is a Flickr badge showing public photos from Jelle Druyts. Make your own badge here.
Advertising
Top Picks
Statistics
Total Posts: 350
This Year: 0
This Month: 0
This Week: 0
Comments: 530
Archives
Sitemap
Disclaimer
This is my personal website, not my boss', not my mother's, and certainly not the pope's. My personal opinions may be irrelevant, inaccurate, boring or even plain wrong, I'm sorry if that makes you feel uncomfortable. But then again, you don't have to read them, I just hope you'll find something interesting here now and then. I'll certainly do my best. But if you don't like it, go read the pope's blog. I'm sure it's fascinating.

Powered by:
newtelligence dasBlog 2.0.7226.0

Sign In