Edit:
This article was years ahead of it's time. InstallShield 2009 will natively support this pattern as described
here.
At a previous job, they wanted the ability to install multiple instances of their product but I never gave it a whole lot of attention because the application wasn't really isolated in such a way to really support the requirement in the first place. I've since moved on and the requirement has resurfaced.
Fortunately this time I couldn't really see any reason why they couldn't run multiple isolated instances so I started wondering what would be involved.
Fortunately Stefan Krueger was kind enough to point me to a topic in the
MSI SDK that had escaped my attention. `
Authoring Multiple Instances with Instance Transforms`.
The concept is you create
ProductCode changing transforms and then embed them into the
_Storages table of your
MSI. At install time invoking will simply install your default instance. However if you add the property
MSINEWINSTANCE=1 and
specify your embedded transform with
TRANSFORMS=:Embedded.
mst then suddenly your install takes on a new identity and it can be installed side by side with your default instance. There is a bit more too it then that to make sure everything is isolated correctly so jump into the
SDK and have some fun.
I had also been stuck on
DevStudio 9 for awhile and low and behold
InstallShield 12 has a partial implementation of this pattern. You simply add the property
InstanceId and default it to 0. Then in the direct editor you populate data in the
ISProductConfigurationInstance table. A simple record like:
ISProductConfiguration_1 1
ProductCode {A5F2A675-9BAB-4A36-BE7A-C4E412230404}
This tells the build process to automatically generate
InstanceId1.
mst when building
ISProdutConfiguation_1 and to author a
ProductCode property change then embed it into _
Storages when complete.
Cool! But wait..... they didn't finish! You don't REALLY want a user to have to know how to invoke the instance do you? That's what those pretty little
bootstrappers are for. So I started digging in and I wrote a utility that:
1) Opens the
MSI in read/only mode, grabs the
ProductCode out of the Property table and then iterates through the _
Storages table and applies each transform to retrieve the additional
ProductCode definitions.
2) Get the
PackageCode of the
MSI.
3) Invoke the
MSI API to find out information of installed instances. Which
ProductCodes are installed, what are the
ProductNames and
InstallLocations as well as which
PackageCode installed each product.
4) If no products are installed, then proceed to installing the default instance. If products are detected then show a dialog asking the user if he'd like to install a new instance ( if any more are available ) or service an existing instance.
5) Launch the
MSI with the correct options to handle a new instance, maintenance of an existing instance or upgrading of an existing install ( REINSTALL=ALL
REINSTALLMODE=
vomus ).
Ok were cooking with gas but wait! What about patching?
Well it seems that again
InstallShield isn't quite there. The patch generation process only populates the default instances
ProductCode into the patch's
SummaryInformationStream Targets attribute.
Tis means the patch only detects the default instance as a valid product to be upgraded. This can be fixed by again iterating through _
Storages to get the
ProductCodes and populating the Targets in a ; delimited manner.
Except of course now will need a better update.
exe because we really don't want to have to use the /n switch to specify the
ProductCode of the instance to be patched. So now we create a
bootstrapper that
1) Opens the patch and grab the Targets property.
2) Call the
MSI API to see what matching products are installed
3) Throw a dialog asking the user which instance he would like upgraded.
4) Call the patch.
If you do this all correctly you end up with a fairly scalable servicing mechanism for supporting multiple instance of your product. Now that's Hard Core Setup Engineering.