Here's a screen shot:
I wanted to take a moment to respectfully point out that this implementation is flawed, a better way to accomplish the task and finally an opportunity for InstallShield to improve their product with a new feature.
The problem comes from the condition and execution scheduling choices. First, let's talk about the condition ISReleaseFlags (contains) X64. This condition should also be checking that INSTALLDIR doesn't already have a value. Second the execution scheduling of Always Execute should really be Execute Only Once.
Why? Because failing to do some causes two defects in the resultant installer. First, attempting to pass INSTALLDIR at the command line will fail. The installer will continue to default the [ProgramFilesxx]MyCompanyName\MyAppName. Second, because the custom action is being called again in the InstallExecuteSequence the Browse capability in the CustomSetup dialog is effectively overridden. There are also other concerns such as trying to set a directory property in subsequent installer transactions such as repairs and upgrades/patches.
This goes to show how careful one must be with custom actions. Even seemingly innocent Type 51 custom actions can cause undesired behavior. If you don't understand this, I really suggest reading Stefan Krueger's immensely helpful Installation Phases and In-Script Execution Options for Custom Actions in Windows Installer article. ( The irony is this article was originally published by InstallShield back in 2001 so you'd think they already get it. )
My entire architecture at my day job is built around Build Automation and ProductConfigurations. All of our installers bring together reusable components with unique branding which includes the value of INSTALLDIR. I've long known the complications of using custom actions so instead I use the ISWiProduct object's INSTALLDIR property to build up the directory table before I build the project. This works really nicely ( including better looking Administrative Installation Extracts and Uncompressed Media ) and eliminates the need for the custom action. I find it ironic that InstallShield just posted six articles on how to use the automation interface in various obscure languages but missed this excellent opportunity to show how it can really be useful in a practical way.
Finally, there is a situation today where I'd have to use the Type 51 custom action: Multiple Instance Installers. InstallShield currently has the ability to define the ProductCode for each instance transform but it would be really cool if it also had the ability to define the INSTALLDIR at the ProductConfiguration level and the Instance level. The idea would be able to do something like:
ProductConfig A (default instance) INSTALLDIR [ProgramFiles]Company\ProductA
ProductConfig A (instance 1 ) INSTALLDIR [ProgramFiles]Company\ProductA-1
ProductConfig A (instance 2 ) INSTALLDIR [ProgramFiles]Company\ProductA-2
ProductConfig B (default instance) INSTALLDIR [ProgramFiles]Company\ProductB
ProductConfig B (instance 1 ) INSTALLDIR [ProgramFiles]Company\ProductB-1
ProductConfig B (instance 2 ) INSTALLDIR [ProgramFiles]Company\ProductB-2
Finally, for my WiX brothers, here is the equivilant code in WiX:
<SetProperty Id="INSTALLDIR"
Value="[ProgramFiles64Folder]MyCompanyName\MyAppName"
Sequence="both"
Before="AppSearch">
ISReleaseFlags><"X64"
</SetProperty>
Great post! I am also a great fan of automation interfaces, but I have another question about the type 51 CA.
ReplyDeleteA problem that can occur when setting INSTALLDIR during an install is that the user can get the error message "Invalid Drive" and cannot continue the installation. This seem to be a really common problem.
A common reason for this is that the installation is trying to use a directory path found in the registry as INSTALLDIR. But the directory path/drive specified in the registry no longer exists (because someone removed an external hard drive for instance).
I was wondering if you have any tip on how to avoid getting the "Invalid Drive" message? I was thinking that I could let the CA set the destination directory to a faulty directory (with a drive that does not exist), but let the user use the Browse capability in the CustomSetup dialog and change it (or let the user mount the drive and run the install again). How can I achieve that?