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
.
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]
- Create reg value AU in Local Machine to remember All Users / Just Me
- Create reg value PC in Local Machine to remember last installed upgraded
[ProductCode]
- Create Registry Search to read value TF form registry into variable
[TARGETDIR]
- Create Registry Search to read value AU form registry into variable
[REMALLUSERS]
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]
- 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.
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]
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:
- Insert into table
CustomAction
action that can set the parameter to its upgrade value:
- Insert into table
InstallUISequence
call to the custom action which when upgrading ( condition ISOLDPRODUCTCODEINSTALLED<>""
) will actually do change the parameter to its upgrade value:
- 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:
{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.
- Insert into table
InstallUISequence
call to this custom action which when upgrading ( condition ISOLDPRODUCTCODEINSTALLED<>""
) will actually do the [WelcomeForm_NextArgs]
to be ConfirmInstall
form.
- 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
.
- Insert into table
InstallUISequence
call to this custom action which when upgrading ( condition ISOLDPRODUCTCODEINSTALLED<>""
) will actually do the [ConfirmInstallForm_PrevArgs]
to WelcomeForm
.
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).