Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Setup Project - Install / Upgrade sequence

4.60/5 (4 votes)
28 Oct 2010CPOL7 min read 51.2K   776  
Have a different dialog sequence and text when installing / upgrading with Setup Project

Introduction

When distributing .NET applications Setup Project is a very helpful tool because it is simple to use and it tracks all the library dependencies needed for the application to install and work properly.

There was a simple task that we wanted to achieve. Our install sequence of dialogs was Welcome Form, Folder Form, Confirm Install Form, Finish Install Form. All we wanted was when installing a new version of the product, the installation needed to detect if there was a previous version installed, and if so, to skip the Folder Form because the user already chose (during the initial install) where he wants to install, and if he wants for AllUsers or Just Me. We also wanted to change all the text in the dialogs to state that the installer is upgrading rather than installing.

How To Use It

You have to manually do the "Changes to setup project" steps 1 to 7 in the beginning of "Step By Step" section and add as postbuild event to the Setup Project the supplied with the article javascript file which will do automatically the ORCA demonstrated MSI changes
PostBuildEvent="$(ProjectDir)PBE.js" "$(BuiltOuputPath)" and set the RemovePreviousVersions=True.

Image 1

How It Was Done

To achieve what we wanted, we used functionality offered by the Setup Project itself, as well as Postbuild JavaScript that automates the necessary MSI changes.

One of the challenges was to detect if we are installing or upgrading - there is no simple parameter that can tell us that. There is [PREVIOUSVERSIONSINSTALLED] but as it was said somewhere it is actually meaningful (has proper value) in the previous version uninstallation process and not when you are installing or upgrading. So what we did was add a registry value remembering the last installed/upgraded [ProductCode], and then when the upgrade runs (the upgrade is install with the same [UpgradeCode] but different [ProductCode]), reading that registry value into parameter [OLDPRODUCTCODE] and checking if there is registry key with that value under SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\[OLDPRODUCTCODE] which will mean that there was a previous installation of the same product or with other words we are upgrading.

Skipping the FolderForm meant that we have to remember the users choice of TargetFolder and AllUsers/JustMe selection. This was done by creating registry values under HKLM\Software\[Manufacturer]\[UpgradeCode] (we use the [UpgradeCode] because it is unique and consistent for the installation but you can use a simple string) to store these choices.
AU - holding the choice of AllUsers/JustMe (if [ALLUSERS] = "" then JustMe else AllUsers ifend).

Initially, we tried to store simply the parameter [FolderForm_AllUsers] which holds the value ALL or ME depending on the radio button selection in FolderForm, but for some strange reason, it was losing its value prior to the stage of writing the registry values TF - holding the [TARGETDIR]. To read back this values on consecutive upgrades, registry searches were created - one that retrieves the registry value TF into [TARGETDIR], and another that retrieves the registry value AU into [REMALLUSERS].

We are not setting directly [ALLUSERS] or [FolderForm_AllUsers] because they are being manipulated somehow by the MSI, so even set with the remembered value in the beginning, they were being reset somewhere after that, and lost their value. When looking into the MSI with ORCA normally in table Parameters parameter [FolderForm_AllUsers] is set to ME, and there is a VSDCA_FolderForm_AllUsers action called into table InstallUISequence to change its value to ALL, so now on upgrade, we added the condition [REMALLUSERS]<>"ME" so that if the user had selected ME previously, this action would be skipped, ensuring that [FolderForm_AllUsers] stays set to ME.

Skipping the FormFolder Dialog meant that we need a different behavior of the NextButton of the form leading to that dialog - in our case WelcomeForm - and also different behaviour of the PreviousButton of the form after FormFolder - in our case ConfirmInstall form.

The behavior of WelcomeForm NextButton was dictated by parameter [WelcomeForm_NextArgs], and the behavior of ConfirmInstall PreviousButton was dictated by parameter [ConfirmInstallForm_PrevArgs]. Custom actions were defined to enable us to change them to the upgrade values [WelcomeForm_NextArgs]=ConfirmInstallForm [ConfirmInstallForm_PrevArgs]="", and these custom actions were called on condition that we are upgrading ( ISOLDPRODUCTCODEINSTALLED<>"") in InstallUISequence.

Also, we want to change all the Text of the controls to state whether we are installing or upgrading, and that means that the words we want to substitute should be a parameters, and the value of the parameter is set accordingly when installing or upgrading. So we created a parameter for every word we want to be different on install/upgrade in Parameter table, and set their values to indicate "install". Create procedures that allow to change this parameters to their upgrade values in table CustomAction. Call these custom actions when upgrading to actually change the parameter values in table InstallUISequence on condition ISOLDPRODUCTCODEINSTALLED<>"". Replace all the words in the Control table Text field with the parameter names so depending on install/upgrade, we will see the corresponding text.

Step By Step

Changes to setup project

  • Create reg value TF in Local Machine to remember last installed [TARGETDIR]

    Image 2

  • Create reg value AU in Local Machine to remember All Users / Just Me

    Image 3

    Image 4

  • Create reg value PC in Local Machine to remember last installed upgraded [ProductCode]

    Image 5

  • Create Registry Search to read value TF form registry into variable [TARGETDIR]

    Image 6

  • Create Registry Search to read value AU form registry into variable [REMALLUSERS]

    Image 7

    It is very important that Step 6 is performed before Step 7 because Step 7 depends on the value retrieved on Step 6

  • Create Registry Search to read value PC form registry into variable [OLDPRODUCTCODE]

    Image 8

  • Create Registry Search to check if reg key (installation) SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\[OLDPRODUCTCODE] exists and write it into variable [ISOLDPRODUCTCODEINSTALLED]. This will determine if we are installing or upgrading.

    Image 9

    Postbuild event changes to MSI - (Done automatically by the attached with the article JavaScript when set as postbuild event as described earlier.)

  • Make the action VSDCA_FolderForm_AllUsers which sets [FolderForm_AllUsers] to its default value to happen only when we are installing on upgrade the value will be set depending on the picked up from the registry [REMALLUSERS]

    Image 10

    Change all the text for the controls depending on if we install or upgrade {for every string - substitute pair we want to change}

  • Insert into Property table a parameter with default (install) value:

    Image 11

  • Insert into table CustomAction action that can set the parameter to its upgrade value:

    Image 12

  • Insert into table InstallUISequence call to the custom action which when upgrading ( condition ISOLDPRODUCTCODEINSTALLED<>"" ) will actually do change the parameter to its upgrade value:

    Image 13

  • For every row in Control table replace into the Text column value the string we want to be install/upgrade dependant with the parameter name:

    Image 14

    {for end}

    Remove the FolderForm when upgrading.

  • Insert into table CustomAction action which can set the value of the [WelcomeForm_NextArgs] (which dictates which form will be shown when clicking next button in WelcomeForm (we want to skip Folder form) ) to be the name of the form after FolderForm in our case ConfirmInstall form.

    Image 15

  • Insert into table InstallUISequence call to this custom action which when upgrading ( condition ISOLDPRODUCTCODEINSTALLED<>"" ) will actually do the [WelcomeForm_NextArgs] to be ConfirmInstall form.

    Image 16

  • Insert into table CustomAction action which can set the value of the [ConfirmInstallForm_PrevArgs] (which dictates which form will be shown when clicking prev button in ConfirmInstall form (we want to skip FolderForm) ) to be the name of the form before FolderForm in our case WelcomeForm.

    Image 17

  • Insert into table InstallUISequence call to this custom action which when upgrading ( condition ISOLDPRODUCTCODEINSTALLED<>"" ) will actually do the [ConfirmInstallForm_PrevArgs] to WelcomeForm.

    Image 18 Image 19

    Because we are removing the folder form on upgrade we still want all the actions that happens when FolderForm next is being clicked to be transferred to the WelcomeForm next when upgrading because the rest of the install process depends on them one of them is setting the remembered All users/Just Me selection {for each row in table ControlEvent if Dialog='FolderForm' and Control='NextButton'}.

  • Copy the same values but for Dialog=WelcomeForm Control=NextButton and Condition=ISOLDPRODUCTCODEINSTALLED="" - now WelcomeForm on upgrade will execute all the stuff that FolderForm was doing on install.

Be Aware Of

The JavaScript changes work only for the described install sequence Welcome Form, Folder Form, Confirm Install Form but can be changed to work for other install sequences. Also there is the assumption that the parameter [FolderForm_AllUsers] is set by default to ME in the Property table of the MSI which seems to be the case (a check can be added that sets this parameter if not present for consistency).

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)