Discussion:
[WiX-users] Multiple Instance Transforms Walkthrough, Proposed Simple Addition to WiX to Make Them Easier
Josh Rowe
2008-05-08 22:39:26 UTC
Permalink
Okay, I've figured out a lot about multiple instance transforms and the sorts of contortions you have to go through to make them work. I even figured out how to make a single MSI drive multiple instance transforms without the need for a bootstrapping program, relying on the fact that the InstallUISequence executes indepently from the InstallExecuteSequence. But one thing remains in WiX to make this a lot easier: automatic GUID-changing of components. In this message, I discuss how to create a multiple instance transform, how to make a single MSI drive multiple instance transforms, a common pitfall, and a feature request for WiX.

I want a user, when they double-click an MSI file, to by default install a new instance. First, obviously, we will need to define some instance transforms:

<Property Id="MYINSTANCE"
Value="DontUseThis"
Secure="yes"
/>

<InstanceTransforms Property="MYINSTANCE">
<Instance ProductCode="444e8aa6-c034-4294-b460-07c2d3262f72"
ProductName="Instance 1"
Id="Instance1"/>
<Instance ProductCode="ff0a0580-bc5d-4562-83ec-2edb3511254b"
ProductName="Instance 2"
Id="Instance2"/>
</InstanceTransforms>

So far, so good. What if the new instance ProductName should be driven by user selection items? No problem:

<CustomAction Id="SetProductName"
Property="ProductName"
Value="[[ProductNamePropertyPrefix][MYINSTANCE]]"
/>

<Property Id="ProductNamePropertyPrefix"
Value="ProductName_"
/>

<Property Id="ProductName_DefaultInstance"
Value="Multiple Instance Transforms - Default instance"
/>

<Property Id="ProductName_Instance1"
Value="Multiple Instance Transforms - Instance #1"
/>

<Property Id="ProductName_Instance2"
Value="Multiple Instance Transforms - Instance #2"

<InstallExecuteSequence>
<Custom Action="SetProductName"
Before="ValidateProductID"/>
</InstallExecuteSequence>

Obviously, use whatever algorithm you want in the SetProductName custom action. You will probably want a different install location, too:

<Directory Id="INSTALLLOCATION" Name="TestMultipleInstanceTransformUi">
<Directory Id="InstanceDirectory">
</Directory>
</Directory>

<CustomAction Id="SetInstanceDirectory"
Property="InstanceDirectory"
Value="[INSTALLLOCATION][MYINSTANCE]\"/>

<InstallExecuteSequence>
<Custom Action="SetInstanceDirectory"
Before="CostFinalize"><![CDATA[InstanceDirectory = ""]]></Custom>
</InstallExecuteSequence>

Okay, looking good. Now, the user can specify MSINEWINSTANCE=1 TRANSFORMS=Instance1 on the msiexec command line to install a new instance, right? But that seems a little in-depth for users to have to worry about. For example, how do they know which instance names are available? How can they reliably choose the next available instance id? We can obviously do better. It turns out that the MSI system works in two phases when a UI is presented. First, the InstallUISequence is executed in the user's space. Second, the InstallExecuteSequence is executed in a system process. The two sequences act completely independently; the InstallUISequence's ExecuteAction just passes a set of property names to the system msiexec service. So, we can make our parent process pass in the right transform as follows:

<CustomAction Id="SetTransforms"
Property="TRANSFORMS"
Value="{:[MYINSTANCE];}[TRANSFORMS]"
/>

<CustomAction Id="SetMsiNewInstance"
Property="MSINEWINSTANCE"
Value="1"/>

<InstallUISequence>
<Custom Action="SetTransforms"
Before="ExecuteAction"><![CDATA[ACTION = "INSTALL"]]></Custom>
<Custom Action="SetMsiNewInstance"
Before="ExecuteAction"><![CDATA[ACTION = "INSTALL"]]></Custom>
</InstallUISequence>

Now we need a reliable way of setting MYINSTANCE. That's pretty simple, too. We make our install register each instance id during the install, and look for the first not-yet-registered instance:

<Property Id="INSTANCE1INSTALLEDPRODUCTCODE">
<RegistrySearch Id="LookForInstance1InstalledProductCode"
Key="[InstancesKey]\Instance1"
Name="ProductCode"
Root="HKLM"
Type="raw"/>
</Property>

<Property Id="INSTANCE2INSTALLEDPRODUCTCODE">
<RegistrySearch Id="LookForInstance2InstalledProductCode"
Key="[InstancesKey]\Instance2"
Name="ProductCode"
Root="HKLM"
Type="raw"/>
</Property>

<Property Id="InstancesKey"
Value="Software\Manufacturer\TestMultipleInstance"
/>

Now we have information about each registered instance. (More on actually getting this registration information into the registry later.) A little more tricky custom action work sets MYINSTANCE to the first unused instance id:

<CustomAction Id="SetMyInstance_Instance1"
Property="MYINSTANCE"
Value="Instance1"
/>

<CustomAction Id="SetMyInstance_Instance2"
Property="MYINSTANCE"
Value="Instance2"
/>

<InstallUISequence>
<Custom Action="SetMyInstance_Instance1"
Before="SetTransforms"><![CDATA[ACTION = "INSTALL" AND MYINSTANCE = "DontUseThis" AND INSTANCE1INSTALLEDPRODUCTCODE = ""]]></Custom>
<Custom Action="SetMyInstance_Instance2"
After="SetMyInstance_Instance1"><![CDATA[ACTION = "INSTALL" AND MYINSTANCE = "DontUseThis" AND INSTANCE2INSTALLEDPRODUCTCODE = ""]]></Custom>
</InstallUISequence>

Now, when the ACTION = "INSTALL", just before the SetTransforms action is called that sets the TRANSFORMS property, the MYINSTANCE property will be set to the value of the first unused instance id. Assuming, that is, that we actually got the instance information into the registry. This is where it gets tricky.

Multiple Instance Transforms don't really work out-of-the-box the way most people would want to use them. MSI does not uninstall non-file-data for components that are used by multiple product codes until the final client product is uninstalled; MSI assumes that non-file data is shared. This is, of course, bunk, but that's the way it works. If your component GUID is shared by multiple product installs, then all but the last uninstall will get an Action: FileAbsent for the shared components, thus not uninstalling things other than files. So, if you perform multiple installs of your wonderful multiple-instance transform that includes the instance id in registry key names and then uninstall them, the registry keys from all but the last package to be uninstalled will continue to exist. Ditto for ServiceInstall elements, etc. So, each of your instances needs a new GUID for each component that contains non-file-data that must be uninstalled. Further, these components need to be installed only with that particular instance. One naive way of doing this is to declare multiple components with conditions on them:

<Component Id="Registry_Instance1"
Guid="54412340-1f29-44f5-a733-157efb25c8a6">
<Condition><![CDATA[MYINSTANCE = "Instance1"]]></Condition>
<RegistryKey Root="HKLM"
Key="[InstancesKey]\[MYINSTANCE]"
<RegistryValue Id="Presence_Instance1"
Action="write"
Name="ProductCode"
Value="[ProductCode]"
Type="string"
KeyPath="yes"
/>
</RegistryKey>
</Component>

<Component Id="Registry_Instance2"
Guid="ffe23417-09ba-485f-912b-c063bebbac2a">
<Condition><![CDATA[MYINSTANCE = "Instance2"]]></Condition>
<RegistryKey Root="HKLM"
Key="[InstancesKey]\[MYINSTANCE]"
<RegistryValue Id="Presence_Instance2"
Action="write"
Name="ProductCode"
Value="[ProductCode]"
Type="string"
KeyPath="yes"
/>
</RegistryKey>
</Component>

That's just for registration data. Also add any ServiceInstall, ServiceControl, other stuff, etc., and you'll see that this gets out of hand very rapidly.

Here is we get to the feature request for WiX. Wouldn't it be nice if the InstanceTransforms element interacted with the Component element to produce transformed rows for each component. For example, imagine if you could specify:

<Component Id="MyDisparateComponent"
Guid="fa6d4980-8780-4cff-b52c-5c3f57e05f48"
GenerateGuidForInstanceTransform="yes">
<RegistryKey></RegistryKey>
</Component>

Since component GUIDs are only referenced in the Component table and nowhere else (except possibly ComponentSearch, but you have that problem anyway), there isn't any reason InstanceTransforms shouldn't output rows to change the GUIDs for those components that contain non-file-data that the user would like to have uninstalled. This dramatically reduces the amount of code necessary to go into a multiple instance installer file.

Feel free to change the mechanism. You want explicit guids? How about:

<Component Id="MyDisparateComponent"
Guid="fa6d4980-8780-4cff-b52c-5c3f57e05f48">
<InstanceTransformGuid TransformId="Instance1" Guid="dd2e906b-0192-43ce-86c2-9e9cec8b1049"/>
<InstanceTransformGuid TransformId="Instance2" Guid="8a493697-8fdb-455c-a542-5bd4667e8473"/>
</Component>

I thought a nice deterministic algorithm for computing the next GUID requested by the GenerateGuidForInstanceTransform attribute would be nice so that you wouldn't need to maintain this list on each component if you didn't want to.

Thoughts?

jmr
shawny
2008-09-16 15:16:46 UTC
Permalink
This is great and exactly along the lines of what I'm trying to do. Can you
post the whole wix file? And how have you approached the instance issue in
respect to having to declare MyInstance1, MyInstance2, etc in the project?
Is there a way to dynamically generate that?

-----
Dont believe everything you think
--
View this message in context: http://n2.nabble.com/Multiple-Instance-Transforms-Walkthrough%2C-Proposed-Simple-Addition-to-WiX-to-Make-Them-Easier-tp708828p1092937.html
Sent from the wix-users mailing list archive at Nabble.com.
Josh Rowe
2008-11-24 18:13:14 UTC
Permalink
Shawny,

I apologize for not seeing this post earlier. The wix-users list is pretty high-traffic. Unfortunately, I can't follow up completely right now because the "whole file" is proprietary to my company. I am, however, slowly working on some stuff which I will be release as open source probably around March.

In the end, I built an extension against Wix 3.0.4102, which allows multiple extensions at the compile and bind stages, that implements a custom boolean attribute on a component called "instance-transforms:Private" (see usage below). The compiler extension class creates a new table called "InstanceTransforms_PrivateComponents", and adds each private component to that table. A binder extension generates Component transform rows using a deterministic guid-changing algorithm (the same used internally by WiX for generating component guids, based on the UUID generation algorithm) in each embedded transform for each row joined from the WixInstanceTransforms table to the InstanceTransforms_PrivateComponents table. This extension replaces the proposed WiX change; in WiX < 4102 I would have needed to change WiX, but at WiX 4102 it permitted multiple extensions to be loaded simultaneously, thus allowing my extension to coexist with other extensions.

I use a loop over a predefined set of instance ids while <?include?>-ing a per-instance file to generate the raw data in the .MSI file. Please note that I had to strip some things from these sources so it may not compile. Then other files specify "instance-transforms:Private" when they have private non-file data. The result is an MSI that when installed via the GUI automatically installs the next available instance id.

InstanceTransforms.wxi:

<?xml version="1.0" encoding="utf-8"?>
<Include xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:instance-transforms="http://XXX.com/wix/InstanceTransforms.xsd">

<!-- This property contains the instance id. If its value is
"Invalid", then this instance should NOT be installed! -->
<Property Id="InstanceId"
Value="Invalid"
/>

<!-- We need some instance transforms. Twent-five oughtta do it.
If you add or delete from this list, also add or delete from the
foreach loop below. -->
<InstanceTransforms Property="InstanceId">
<Instance Id="InstanceId01" ProductCode="xxx"/>
<Instance Id="InstanceId02" ProductCode="xxx"/>
<Instance Id="InstanceId03" ProductCode="xxx"/>
<Instance Id="InstanceId04" ProductCode="xxx"/>
<Instance Id="InstanceId05" ProductCode="xxx"/>
<Instance Id="InstanceId06" ProductCode="xxx"/>
<Instance Id="InstanceId07" ProductCode="xxx"/>
<Instance Id="InstanceId08" ProductCode="xxx"/>
<Instance Id="InstanceId09" ProductCode=" xxx "/>
<Instance Id="InstanceId10" ProductCode=" xxx "/>
<Instance Id="InstanceId11" ProductCode=" xxx "/>
<Instance Id="InstanceId12" ProductCode=" xxx "/>
<Instance Id="InstanceId13" ProductCode=" xxx "/>
<Instance Id="InstanceId14" ProductCode=" xxx "/>
<Instance Id="InstanceId15" ProductCode=" xxx "/>
<Instance Id="InstanceId16" ProductCode=" xxx "/>
<Instance Id="InstanceId17" ProductCode=" xxx "/>
<Instance Id="InstanceId18" ProductCode=" xxx "/>
<Instance Id="InstanceId19" ProductCode=" xxx "/>
<Instance Id="InstanceId20" ProductCode=" xxx "/>
<Instance Id="InstanceId21" ProductCode=" xxx "/>
<Instance Id="InstanceId22" ProductCode=" xxx "/>
<Instance Id="InstanceId23" ProductCode=" xxx "/>
<Instance Id="InstanceId24" ProductCode=" xxx "/>
<Instance Id="InstanceId25" ProductCode=" xxx "/>
</InstanceTransforms>


<!-- We need a component to represent the installed state of the instance.
We can later detect the installation state of this component to determine
whether or not the particular instance exists.
-->
<DirectoryRef Id="InstallDirectory">
<Component Id="InstalledStateMarker"
Guid="xxx"
instance-transforms:Private="yes" <--- This is my addition to WiX
<RegistryValue Id="Registry.InstalledStateMarker"
Root="HKLM"
Key="[InstallKey]"
Name="[InstanceId]"
Type="string"
Value="[_NAME]"
/>
</Component>
</DirectoryRef>

<!-- For each instance id, generate custom actions, properties, registry searches, etc.,
for that instance. Doing it this way cuts down on a large amount of duplication of code. -->
<?foreach InstanceId in 01;02;03;04;05;06;07;08;09;10;11;12;13;14;15;16;17;18;19;20;21;22;23;24;25?>
<?include DefineInstanceTransform.wxi?>
<?endforeach?>

<!-- This custom action sets the MSINEWINSTANCE property if we are installing. -->
<CustomAction Id="SetMsiNewInstance"
Property="MSINEWINSTANCE"
Value="1"
/>

<!-- This custom action sets the TRANSFORMS property to include the instance
transform. It must be run after the SetInstanceId01 action
to guarantee that InstanceId is already set. -->
<CustomAction Id="SetTransforms"
Property="TRANSFORMS"
Value="{:[InstanceId];}[TRANSFORMS]"
/>

<InstallUISequence>
<Custom Action="SetMsiNewInstance"
Before="LaunchConditions">ACTION="INSTALL" AND InstanceId = "Invalid"</Custom>
<Custom Action="SetTransforms"
After="SetInstanceId01">ACTION="INSTALL" AND MSINEWINSTANCE = 1</Custom>
</InstallUISequence>

<!-- This prevents the install from succeeding on the server side if
no instance transform has been provided. -->
<InstallExecuteSequence>
<LaunchConditions After="AppSearch"/>
</InstallExecuteSequence>

<Condition Message="Cannot install non-instance transform version of this product. ACTION: [ACTION] InstanceId: [InstanceId]"><![CDATA[InstanceId <> "Invalid"]]></Condition>

</Include>


DefineInstanceTransform.wxi:


<?xml version="1.0" encoding="utf-8"?>
<Include xmlns="http://schemas.microsoft.com/wix/2006/wi">
<!-- This file defines all the MSI gunk needed to support a single
instance transform.

This .wxi file requires the InstanceId variable to be defined to the
"00"-formatted value of the instance id to support. This file is
included multiple times by InstanceTransforms.wxi to support each
instance transform. -->

<!-- Search for this instance's install state. -->
<Property Id="INSTANCE$(var.InstanceId)INSTALLED">
<RegistrySearch Id="Instance$(var.InstanceId)Installed"
Key="[InstallKey]"
Name="InstanceId$(var.InstanceId)"
Root="HKLM"
Type="raw"
/>
</Property>

<!-- Choose a value of __Before. -->
<?define __Before = LaunchConditions?>
<?ifdef PreviousInstanceId?>
<?define __Before = SetInstanceId$(var.PreviousInstanceId)?>
<?endif?>

<?define PreviousInstanceId = $(var.InstanceId)?>

<!-- Create a custom action to set the InstanceId to this instance id. -->
<CustomAction Id="SetInstanceId$(var.InstanceId)"
Property="InstanceId"
Value="InstanceId$(var.InstanceId)"/>

<!-- This execute the custom action only if:
1. We are installing, as compared to uninstalling, repairing, etc., AND
2. The instance id is not installed.

This results in the InstanceId property containing the value "InstanceId"
for the smallest instance id that was not installed.
-->
<InstallUISequence>
<Custom Action="SetInstanceId$(var.InstanceId)"
Before="$(var.__Before)"><![CDATA[ACTION="INSTALL" AND NOT (INSTANCE$(var.InstanceId)INSTALLED)]]></Custom>
</InstallUISequence>

</Include>

-----Original Message-----
From: wix-users-***@lists.sourceforge.net [mailto:wix-users-***@lists.sourceforge.net] On Behalf Of shawny
Sent: Tuesday, September 16, 2008 11:17 AM
To: wix-***@lists.sourceforge.net
Subject: Re: [WiX-users] Multiple Instance Transforms Walkthrough, Proposed Simple Addition to WiX to Make Them Easier


This is great and exactly along the lines of what I'm trying to do. Can you
post the whole wix file? And how have you approached the instance issue in
respect to having to declare MyInstance1, MyInstance2, etc in the project?
Is there a way to dynamically generate that?
amrish
2009-07-01 11:24:33 UTC
Permalink
I am trying to create an MSI which will install a windows service multiple
times on the same machine with a clientname appended to the end of each
service instance (ServiceNameClientA). I take the servicename as input from
the user and create the service with that name(e.g clientname). It will be
created in its own directory named exactly the same.

So if the clientname is ClientA then the Directory its installed to is also
the same name.

I use a custom action to change the InstallLocation to
[InstallLocation]\[ClientName]

Is it possible to do this via the Multiple Instace example in your post?

I see you have to define the each instance in the file. In my requirement
there could be any number of instances. I need to be able to install any
number of times and uninstall them either by taking the clientname as input
to uninstall or other method.

Could you advise on this please?
Post by Josh Rowe
Okay, I've figured out a lot about multiple instance transforms and the
sorts of contortions you have to go through to make them work. I even
figured out how to make a single MSI drive multiple instance transforms
without the need for a bootstrapping program, relying on the fact that the
InstallUISequence executes indepently from the InstallExecuteSequence.
But one thing remains in WiX to make this a lot easier: automatic
GUID-changing of components. In this message, I discuss how to create a
multiple instance transform, how to make a single MSI drive multiple
instance transforms, a common pitfall, and a feature request for WiX.
I want a user, when they double-click an MSI file, to by default install a
new instance. First, obviously, we will need to define some instance
<Property Id="MYINSTANCE"
Value="DontUseThis"
Secure="yes"
/>
<InstanceTransforms Property="MYINSTANCE">
<Instance ProductCode="444e8aa6-c034-4294-b460-07c2d3262f72"
ProductName="Instance 1"
Id="Instance1"/>
<Instance ProductCode="ff0a0580-bc5d-4562-83ec-2edb3511254b"
ProductName="Instance 2"
Id="Instance2"/>
</InstanceTransforms>
So far, so good. What if the new instance ProductName should be driven by
<CustomAction Id="SetProductName"
Property="ProductName"
Value="[[ProductNamePropertyPrefix][MYINSTANCE]]"
/>
<Property Id="ProductNamePropertyPrefix"
Value="ProductName_"
/>
<Property Id="ProductName_DefaultInstance"
Value="Multiple Instance Transforms - Default instance"
/>
<Property Id="ProductName_Instance1"
Value="Multiple Instance Transforms - Instance #1"
/>
<Property Id="ProductName_Instance2"
Value="Multiple Instance Transforms - Instance #2"
<InstallExecuteSequence>
<Custom Action="SetProductName"
Before="ValidateProductID"/>
</InstallExecuteSequence>
Obviously, use whatever algorithm you want in the SetProductName custom
<Directory Id="INSTALLLOCATION"
Name="TestMultipleInstanceTransformUi">
<Directory Id="InstanceDirectory">
</Directory>
</Directory>
<CustomAction Id="SetInstanceDirectory"
Property="InstanceDirectory"
Value="[INSTALLLOCATION][MYINSTANCE]\"/>
<InstallExecuteSequence>
<Custom Action="SetInstanceDirectory"
Before="CostFinalize"><![CDATA[InstanceDirectory = ""]]></Custom>
</InstallExecuteSequence>
Okay, looking good. Now, the user can specify MSINEWINSTANCE=1
TRANSFORMS=Instance1 on the msiexec command line to install a new
instance, right? But that seems a little in-depth for users to have to
worry about. For example, how do they know which instance names are
available? How can they reliably choose the next available instance id?
We can obviously do better. It turns out that the MSI system works in two
phases when a UI is presented. First, the InstallUISequence is executed
in the user's space. Second, the InstallExecuteSequence is executed in a
system process. The two sequences act completely independently; the
InstallUISequence's ExecuteAction just passes a set of property names to
the system msiexec service. So, we can make our parent process pass in
<CustomAction Id="SetTransforms"
Property="TRANSFORMS"
Value="{:[MYINSTANCE];}[TRANSFORMS]"
/>
<CustomAction Id="SetMsiNewInstance"
Property="MSINEWINSTANCE"
Value="1"/>
<InstallUISequence>
<Custom Action="SetTransforms"
Before="ExecuteAction"><![CDATA[ACTION =
"INSTALL"]]></Custom>
<Custom Action="SetMsiNewInstance"
Before="ExecuteAction"><![CDATA[ACTION =
"INSTALL"]]></Custom>
</InstallUISequence>
Now we need a reliable way of setting MYINSTANCE. That's pretty simple,
too. We make our install register each instance id during the install,
<Property Id="INSTANCE1INSTALLEDPRODUCTCODE">
<RegistrySearch Id="LookForInstance1InstalledProductCode"
Key="[InstancesKey]\Instance1"
Name="ProductCode"
Root="HKLM"
Type="raw"/>
</Property>
<Property Id="INSTANCE2INSTALLEDPRODUCTCODE">
<RegistrySearch Id="LookForInstance2InstalledProductCode"
Key="[InstancesKey]\Instance2"
Name="ProductCode"
Root="HKLM"
Type="raw"/>
</Property>
<Property Id="InstancesKey"
Value="Software\Manufacturer\TestMultipleInstance"
/>
Now we have information about each registered instance. (More on actually
getting this registration information into the registry later.) A little
more tricky custom action work sets MYINSTANCE to the first unused
<CustomAction Id="SetMyInstance_Instance1"
Property="MYINSTANCE"
Value="Instance1"
/>
<CustomAction Id="SetMyInstance_Instance2"
Property="MYINSTANCE"
Value="Instance2"
/>
<InstallUISequence>
<Custom Action="SetMyInstance_Instance1"
Before="SetTransforms"><![CDATA[ACTION = "INSTALL" AND
MYINSTANCE = "DontUseThis" AND INSTANCE1INSTALLEDPRODUCTCODE =
""]]></Custom>
<Custom Action="SetMyInstance_Instance2"
After="SetMyInstance_Instance1"><![CDATA[ACTION = "INSTALL"
AND MYINSTANCE = "DontUseThis" AND INSTANCE2INSTALLEDPRODUCTCODE =
""]]></Custom>
</InstallUISequence>
Now, when the ACTION = "INSTALL", just before the SetTransforms action is
called that sets the TRANSFORMS property, the MYINSTANCE property will be
set to the value of the first unused instance id. Assuming, that is, that
we actually got the instance information into the registry. This is where
it gets tricky.
Multiple Instance Transforms don't really work out-of-the-box the way most
people would want to use them. MSI does not uninstall non-file-data for
components that are used by multiple product codes until the final client
product is uninstalled; MSI assumes that non-file data is shared. This
is, of course, bunk, but that's the way it works. If your component GUID
is shared by multiple product installs, then all but the last uninstall
will get an Action: FileAbsent for the shared components, thus not
uninstalling things other than files. So, if you perform multiple
installs of your wonderful multiple-instance transform that includes the
instance id in registry key names and then uninstall them, the registry
keys from all but the last package to be uninstalled will continue to
exist. Ditto for ServiceInstall elements, etc. So, each of your
instances needs a new GUID for each component that contains non-file-data
that must be uninstalled. Further, these components need to be installed
only with that particular instance. One naive way of doing this is to
<Component Id="Registry_Instance1"
Guid="54412340-1f29-44f5-a733-157efb25c8a6">
<Condition><![CDATA[MYINSTANCE = "Instance1"]]></Condition>
<RegistryKey Root="HKLM"
Key="[InstancesKey]\[MYINSTANCE]"
<RegistryValue Id="Presence_Instance1"
Action="write"
Name="ProductCode"
Value="[ProductCode]"
Type="string"
KeyPath="yes"
/>
</RegistryKey>
</Component>
<Component Id="Registry_Instance2"
Guid="ffe23417-09ba-485f-912b-c063bebbac2a">
<Condition><![CDATA[MYINSTANCE = "Instance2"]]></Condition>
<RegistryKey Root="HKLM"
Key="[InstancesKey]\[MYINSTANCE]"
<RegistryValue Id="Presence_Instance2"
Action="write"
Name="ProductCode"
Value="[ProductCode]"
Type="string"
KeyPath="yes"
/>
</RegistryKey>
</Component>
That's just for registration data. Also add any ServiceInstall,
ServiceControl, other stuff, etc., and you'll see that this gets out of
hand very rapidly.
Here is we get to the feature request for WiX. Wouldn't it be nice if the
InstanceTransforms element interacted with the Component element to
produce transformed rows for each component. For example, imagine if you
<Component Id="MyDisparateComponent"
Guid="fa6d4980-8780-4cff-b52c-5c3f57e05f48"
GenerateGuidForInstanceTransform="yes">
<RegistryKey></RegistryKey>
</Component>
Since component GUIDs are only referenced in the Component table and
nowhere else (except possibly ComponentSearch, but you have that problem
anyway), there isn't any reason InstanceTransforms shouldn't output rows
to change the GUIDs for those components that contain non-file-data that
the user would like to have uninstalled. This dramatically reduces the
amount of code necessary to go into a multiple instance installer file.
<Component Id="MyDisparateComponent"
Guid="fa6d4980-8780-4cff-b52c-5c3f57e05f48">
<InstanceTransformGuid TransformId="Instance1"
Guid="dd2e906b-0192-43ce-86c2-9e9cec8b1049"/>
<InstanceTransformGuid TransformId="Instance2"
Guid="8a493697-8fdb-455c-a542-5bd4667e8473"/>
</Component>
I thought a nice deterministic algorithm for computing the next GUID
requested by the GenerateGuidForInstanceTransform attribute would be nice
so that you wouldn't need to maintain this list on each component if you
didn't want to.
Thoughts?
jmr
-------------------------------------------------------------------------
This SF.net email is sponsored by the 2008 JavaOne(SM) Conference
Don't miss this year's exciting event. There's still time to save $100.
Use priority code J8TL2D2.
http://ad.doubleclick.net/clk;198757673;13503038;p?http://java.sun.com/javaone
_______________________________________________
WiX-users mailing list
https://lists.sourceforge.net/lists/listinfo/wix-users
--
View this message in context: http://n2.nabble.com/Multiple-Instance-Transforms-Walkthrough%2C-Proposed-Simple-Addition-to-WiX-to-Make-Them-Easier-tp708828p3188480.html
Sent from the wix-users mailing list archive at Nabble.com.
amrish
2009-07-01 11:24:31 UTC
Permalink
I am trying to create an MSI which will install a windows service multiple
times on the same machine with a clientname appended to the end of each
service instance (ServiceNameClientA). I take the servicename as input from
the user and create the service with that name(e.g clientname). It will be
created in its own directory named exactly the same.

So if the clientname is ClientA then the Directory its installed to is also
the same name.

I use a custom action to change the InstallLocation to
[InstallLocation]\[ClientName]

Is it possible to do this via the Multiple Instace example in your post?

I see you have to define the each instance in the file. In my requirement
there could be any number of instances. I need to be able to install any
number of times and uninstall them either by taking the clientname as input
to uninstall or other method.

Could you advise on this please?
Post by Josh Rowe
Okay, I've figured out a lot about multiple instance transforms and the
sorts of contortions you have to go through to make them work. I even
figured out how to make a single MSI drive multiple instance transforms
without the need for a bootstrapping program, relying on the fact that the
InstallUISequence executes indepently from the InstallExecuteSequence.
But one thing remains in WiX to make this a lot easier: automatic
GUID-changing of components. In this message, I discuss how to create a
multiple instance transform, how to make a single MSI drive multiple
instance transforms, a common pitfall, and a feature request for WiX.
I want a user, when they double-click an MSI file, to by default install a
new instance. First, obviously, we will need to define some instance
<Property Id="MYINSTANCE"
Value="DontUseThis"
Secure="yes"
/>
<InstanceTransforms Property="MYINSTANCE">
<Instance ProductCode="444e8aa6-c034-4294-b460-07c2d3262f72"
ProductName="Instance 1"
Id="Instance1"/>
<Instance ProductCode="ff0a0580-bc5d-4562-83ec-2edb3511254b"
ProductName="Instance 2"
Id="Instance2"/>
</InstanceTransforms>
So far, so good. What if the new instance ProductName should be driven by
<CustomAction Id="SetProductName"
Property="ProductName"
Value="[[ProductNamePropertyPrefix][MYINSTANCE]]"
/>
<Property Id="ProductNamePropertyPrefix"
Value="ProductName_"
/>
<Property Id="ProductName_DefaultInstance"
Value="Multiple Instance Transforms - Default instance"
/>
<Property Id="ProductName_Instance1"
Value="Multiple Instance Transforms - Instance #1"
/>
<Property Id="ProductName_Instance2"
Value="Multiple Instance Transforms - Instance #2"
<InstallExecuteSequence>
<Custom Action="SetProductName"
Before="ValidateProductID"/>
</InstallExecuteSequence>
Obviously, use whatever algorithm you want in the SetProductName custom
<Directory Id="INSTALLLOCATION"
Name="TestMultipleInstanceTransformUi">
<Directory Id="InstanceDirectory">
</Directory>
</Directory>
<CustomAction Id="SetInstanceDirectory"
Property="InstanceDirectory"
Value="[INSTALLLOCATION][MYINSTANCE]\"/>
<InstallExecuteSequence>
<Custom Action="SetInstanceDirectory"
Before="CostFinalize"><![CDATA[InstanceDirectory = ""]]></Custom>
</InstallExecuteSequence>
Okay, looking good. Now, the user can specify MSINEWINSTANCE=1
TRANSFORMS=Instance1 on the msiexec command line to install a new
instance, right? But that seems a little in-depth for users to have to
worry about. For example, how do they know which instance names are
available? How can they reliably choose the next available instance id?
We can obviously do better. It turns out that the MSI system works in two
phases when a UI is presented. First, the InstallUISequence is executed
in the user's space. Second, the InstallExecuteSequence is executed in a
system process. The two sequences act completely independently; the
InstallUISequence's ExecuteAction just passes a set of property names to
the system msiexec service. So, we can make our parent process pass in
<CustomAction Id="SetTransforms"
Property="TRANSFORMS"
Value="{:[MYINSTANCE];}[TRANSFORMS]"
/>
<CustomAction Id="SetMsiNewInstance"
Property="MSINEWINSTANCE"
Value="1"/>
<InstallUISequence>
<Custom Action="SetTransforms"
Before="ExecuteAction"><![CDATA[ACTION =
"INSTALL"]]></Custom>
<Custom Action="SetMsiNewInstance"
Before="ExecuteAction"><![CDATA[ACTION =
"INSTALL"]]></Custom>
</InstallUISequence>
Now we need a reliable way of setting MYINSTANCE. That's pretty simple,
too. We make our install register each instance id during the install,
<Property Id="INSTANCE1INSTALLEDPRODUCTCODE">
<RegistrySearch Id="LookForInstance1InstalledProductCode"
Key="[InstancesKey]\Instance1"
Name="ProductCode"
Root="HKLM"
Type="raw"/>
</Property>
<Property Id="INSTANCE2INSTALLEDPRODUCTCODE">
<RegistrySearch Id="LookForInstance2InstalledProductCode"
Key="[InstancesKey]\Instance2"
Name="ProductCode"
Root="HKLM"
Type="raw"/>
</Property>
<Property Id="InstancesKey"
Value="Software\Manufacturer\TestMultipleInstance"
/>
Now we have information about each registered instance. (More on actually
getting this registration information into the registry later.) A little
more tricky custom action work sets MYINSTANCE to the first unused
<CustomAction Id="SetMyInstance_Instance1"
Property="MYINSTANCE"
Value="Instance1"
/>
<CustomAction Id="SetMyInstance_Instance2"
Property="MYINSTANCE"
Value="Instance2"
/>
<InstallUISequence>
<Custom Action="SetMyInstance_Instance1"
Before="SetTransforms"><![CDATA[ACTION = "INSTALL" AND
MYINSTANCE = "DontUseThis" AND INSTANCE1INSTALLEDPRODUCTCODE =
""]]></Custom>
<Custom Action="SetMyInstance_Instance2"
After="SetMyInstance_Instance1"><![CDATA[ACTION = "INSTALL"
AND MYINSTANCE = "DontUseThis" AND INSTANCE2INSTALLEDPRODUCTCODE =
""]]></Custom>
</InstallUISequence>
Now, when the ACTION = "INSTALL", just before the SetTransforms action is
called that sets the TRANSFORMS property, the MYINSTANCE property will be
set to the value of the first unused instance id. Assuming, that is, that
we actually got the instance information into the registry. This is where
it gets tricky.
Multiple Instance Transforms don't really work out-of-the-box the way most
people would want to use them. MSI does not uninstall non-file-data for
components that are used by multiple product codes until the final client
product is uninstalled; MSI assumes that non-file data is shared. This
is, of course, bunk, but that's the way it works. If your component GUID
is shared by multiple product installs, then all but the last uninstall
will get an Action: FileAbsent for the shared components, thus not
uninstalling things other than files. So, if you perform multiple
installs of your wonderful multiple-instance transform that includes the
instance id in registry key names and then uninstall them, the registry
keys from all but the last package to be uninstalled will continue to
exist. Ditto for ServiceInstall elements, etc. So, each of your
instances needs a new GUID for each component that contains non-file-data
that must be uninstalled. Further, these components need to be installed
only with that particular instance. One naive way of doing this is to
<Component Id="Registry_Instance1"
Guid="54412340-1f29-44f5-a733-157efb25c8a6">
<Condition><![CDATA[MYINSTANCE = "Instance1"]]></Condition>
<RegistryKey Root="HKLM"
Key="[InstancesKey]\[MYINSTANCE]"
<RegistryValue Id="Presence_Instance1"
Action="write"
Name="ProductCode"
Value="[ProductCode]"
Type="string"
KeyPath="yes"
/>
</RegistryKey>
</Component>
<Component Id="Registry_Instance2"
Guid="ffe23417-09ba-485f-912b-c063bebbac2a">
<Condition><![CDATA[MYINSTANCE = "Instance2"]]></Condition>
<RegistryKey Root="HKLM"
Key="[InstancesKey]\[MYINSTANCE]"
<RegistryValue Id="Presence_Instance2"
Action="write"
Name="ProductCode"
Value="[ProductCode]"
Type="string"
KeyPath="yes"
/>
</RegistryKey>
</Component>
That's just for registration data. Also add any ServiceInstall,
ServiceControl, other stuff, etc., and you'll see that this gets out of
hand very rapidly.
Here is we get to the feature request for WiX. Wouldn't it be nice if the
InstanceTransforms element interacted with the Component element to
produce transformed rows for each component. For example, imagine if you
<Component Id="MyDisparateComponent"
Guid="fa6d4980-8780-4cff-b52c-5c3f57e05f48"
GenerateGuidForInstanceTransform="yes">
<RegistryKey></RegistryKey>
</Component>
Since component GUIDs are only referenced in the Component table and
nowhere else (except possibly ComponentSearch, but you have that problem
anyway), there isn't any reason InstanceTransforms shouldn't output rows
to change the GUIDs for those components that contain non-file-data that
the user would like to have uninstalled. This dramatically reduces the
amount of code necessary to go into a multiple instance installer file.
<Component Id="MyDisparateComponent"
Guid="fa6d4980-8780-4cff-b52c-5c3f57e05f48">
<InstanceTransformGuid TransformId="Instance1"
Guid="dd2e906b-0192-43ce-86c2-9e9cec8b1049"/>
<InstanceTransformGuid TransformId="Instance2"
Guid="8a493697-8fdb-455c-a542-5bd4667e8473"/>
</Component>
I thought a nice deterministic algorithm for computing the next GUID
requested by the GenerateGuidForInstanceTransform attribute would be nice
so that you wouldn't need to maintain this list on each component if you
didn't want to.
Thoughts?
jmr
-------------------------------------------------------------------------
This SF.net email is sponsored by the 2008 JavaOne(SM) Conference
Don't miss this year's exciting event. There's still time to save $100.
Use priority code J8TL2D2.
http://ad.doubleclick.net/clk;198757673;13503038;p?http://java.sun.com/javaone
_______________________________________________
WiX-users mailing list
https://lists.sourceforge.net/lists/listinfo/wix-users
--
View this message in context: http://n2.nabble.com/Multiple-Instance-Transforms-Walkthrough%2C-Proposed-Simple-Addition-to-WiX-to-Make-Them-Easier-tp708828p3188478.html
Sent from the wix-users mailing list archive at Nabble.com.
amrish
2009-07-01 11:24:31 UTC
Permalink
I am trying to create an MSI which will install a windows service multiple
times on the same machine with a clientname appended to the end of each
service instance (ServiceNameClientA). I take the servicename as input from
the user and create the service with that name(e.g clientname). It will be
created in its own directory named exactly the same.

So if the clientname is ClientA then the Directory its installed to is also
the same name.

I use a custom action to change the InstallLocation to
[InstallLocation]\[ClientName]

Is it possible to do this via the Multiple Instace example in your post?

I see you have to define the each instance in the file. In my requirement
there could be any number of instances. I need to be able to install any
number of times and uninstall them either by taking the clientname as input
to uninstall or other method.

Could you advise on this please?
Post by Josh Rowe
Okay, I've figured out a lot about multiple instance transforms and the
sorts of contortions you have to go through to make them work. I even
figured out how to make a single MSI drive multiple instance transforms
without the need for a bootstrapping program, relying on the fact that the
InstallUISequence executes indepently from the InstallExecuteSequence.
But one thing remains in WiX to make this a lot easier: automatic
GUID-changing of components. In this message, I discuss how to create a
multiple instance transform, how to make a single MSI drive multiple
instance transforms, a common pitfall, and a feature request for WiX.
I want a user, when they double-click an MSI file, to by default install a
new instance. First, obviously, we will need to define some instance
<Property Id="MYINSTANCE"
Value="DontUseThis"
Secure="yes"
/>
<InstanceTransforms Property="MYINSTANCE">
<Instance ProductCode="444e8aa6-c034-4294-b460-07c2d3262f72"
ProductName="Instance 1"
Id="Instance1"/>
<Instance ProductCode="ff0a0580-bc5d-4562-83ec-2edb3511254b"
ProductName="Instance 2"
Id="Instance2"/>
</InstanceTransforms>
So far, so good. What if the new instance ProductName should be driven by
<CustomAction Id="SetProductName"
Property="ProductName"
Value="[[ProductNamePropertyPrefix][MYINSTANCE]]"
/>
<Property Id="ProductNamePropertyPrefix"
Value="ProductName_"
/>
<Property Id="ProductName_DefaultInstance"
Value="Multiple Instance Transforms - Default instance"
/>
<Property Id="ProductName_Instance1"
Value="Multiple Instance Transforms - Instance #1"
/>
<Property Id="ProductName_Instance2"
Value="Multiple Instance Transforms - Instance #2"
<InstallExecuteSequence>
<Custom Action="SetProductName"
Before="ValidateProductID"/>
</InstallExecuteSequence>
Obviously, use whatever algorithm you want in the SetProductName custom
<Directory Id="INSTALLLOCATION"
Name="TestMultipleInstanceTransformUi">
<Directory Id="InstanceDirectory">
</Directory>
</Directory>
<CustomAction Id="SetInstanceDirectory"
Property="InstanceDirectory"
Value="[INSTALLLOCATION][MYINSTANCE]\"/>
<InstallExecuteSequence>
<Custom Action="SetInstanceDirectory"
Before="CostFinalize"><![CDATA[InstanceDirectory = ""]]></Custom>
</InstallExecuteSequence>
Okay, looking good. Now, the user can specify MSINEWINSTANCE=1
TRANSFORMS=Instance1 on the msiexec command line to install a new
instance, right? But that seems a little in-depth for users to have to
worry about. For example, how do they know which instance names are
available? How can they reliably choose the next available instance id?
We can obviously do better. It turns out that the MSI system works in two
phases when a UI is presented. First, the InstallUISequence is executed
in the user's space. Second, the InstallExecuteSequence is executed in a
system process. The two sequences act completely independently; the
InstallUISequence's ExecuteAction just passes a set of property names to
the system msiexec service. So, we can make our parent process pass in
<CustomAction Id="SetTransforms"
Property="TRANSFORMS"
Value="{:[MYINSTANCE];}[TRANSFORMS]"
/>
<CustomAction Id="SetMsiNewInstance"
Property="MSINEWINSTANCE"
Value="1"/>
<InstallUISequence>
<Custom Action="SetTransforms"
Before="ExecuteAction"><![CDATA[ACTION =
"INSTALL"]]></Custom>
<Custom Action="SetMsiNewInstance"
Before="ExecuteAction"><![CDATA[ACTION =
"INSTALL"]]></Custom>
</InstallUISequence>
Now we need a reliable way of setting MYINSTANCE. That's pretty simple,
too. We make our install register each instance id during the install,
<Property Id="INSTANCE1INSTALLEDPRODUCTCODE">
<RegistrySearch Id="LookForInstance1InstalledProductCode"
Key="[InstancesKey]\Instance1"
Name="ProductCode"
Root="HKLM"
Type="raw"/>
</Property>
<Property Id="INSTANCE2INSTALLEDPRODUCTCODE">
<RegistrySearch Id="LookForInstance2InstalledProductCode"
Key="[InstancesKey]\Instance2"
Name="ProductCode"
Root="HKLM"
Type="raw"/>
</Property>
<Property Id="InstancesKey"
Value="Software\Manufacturer\TestMultipleInstance"
/>
Now we have information about each registered instance. (More on actually
getting this registration information into the registry later.) A little
more tricky custom action work sets MYINSTANCE to the first unused
<CustomAction Id="SetMyInstance_Instance1"
Property="MYINSTANCE"
Value="Instance1"
/>
<CustomAction Id="SetMyInstance_Instance2"
Property="MYINSTANCE"
Value="Instance2"
/>
<InstallUISequence>
<Custom Action="SetMyInstance_Instance1"
Before="SetTransforms"><![CDATA[ACTION = "INSTALL" AND
MYINSTANCE = "DontUseThis" AND INSTANCE1INSTALLEDPRODUCTCODE =
""]]></Custom>
<Custom Action="SetMyInstance_Instance2"
After="SetMyInstance_Instance1"><![CDATA[ACTION = "INSTALL"
AND MYINSTANCE = "DontUseThis" AND INSTANCE2INSTALLEDPRODUCTCODE =
""]]></Custom>
</InstallUISequence>
Now, when the ACTION = "INSTALL", just before the SetTransforms action is
called that sets the TRANSFORMS property, the MYINSTANCE property will be
set to the value of the first unused instance id. Assuming, that is, that
we actually got the instance information into the registry. This is where
it gets tricky.
Multiple Instance Transforms don't really work out-of-the-box the way most
people would want to use them. MSI does not uninstall non-file-data for
components that are used by multiple product codes until the final client
product is uninstalled; MSI assumes that non-file data is shared. This
is, of course, bunk, but that's the way it works. If your component GUID
is shared by multiple product installs, then all but the last uninstall
will get an Action: FileAbsent for the shared components, thus not
uninstalling things other than files. So, if you perform multiple
installs of your wonderful multiple-instance transform that includes the
instance id in registry key names and then uninstall them, the registry
keys from all but the last package to be uninstalled will continue to
exist. Ditto for ServiceInstall elements, etc. So, each of your
instances needs a new GUID for each component that contains non-file-data
that must be uninstalled. Further, these components need to be installed
only with that particular instance. One naive way of doing this is to
<Component Id="Registry_Instance1"
Guid="54412340-1f29-44f5-a733-157efb25c8a6">
<Condition><![CDATA[MYINSTANCE = "Instance1"]]></Condition>
<RegistryKey Root="HKLM"
Key="[InstancesKey]\[MYINSTANCE]"
<RegistryValue Id="Presence_Instance1"
Action="write"
Name="ProductCode"
Value="[ProductCode]"
Type="string"
KeyPath="yes"
/>
</RegistryKey>
</Component>
<Component Id="Registry_Instance2"
Guid="ffe23417-09ba-485f-912b-c063bebbac2a">
<Condition><![CDATA[MYINSTANCE = "Instance2"]]></Condition>
<RegistryKey Root="HKLM"
Key="[InstancesKey]\[MYINSTANCE]"
<RegistryValue Id="Presence_Instance2"
Action="write"
Name="ProductCode"
Value="[ProductCode]"
Type="string"
KeyPath="yes"
/>
</RegistryKey>
</Component>
That's just for registration data. Also add any ServiceInstall,
ServiceControl, other stuff, etc., and you'll see that this gets out of
hand very rapidly.
Here is we get to the feature request for WiX. Wouldn't it be nice if the
InstanceTransforms element interacted with the Component element to
produce transformed rows for each component. For example, imagine if you
<Component Id="MyDisparateComponent"
Guid="fa6d4980-8780-4cff-b52c-5c3f57e05f48"
GenerateGuidForInstanceTransform="yes">
<RegistryKey></RegistryKey>
</Component>
Since component GUIDs are only referenced in the Component table and
nowhere else (except possibly ComponentSearch, but you have that problem
anyway), there isn't any reason InstanceTransforms shouldn't output rows
to change the GUIDs for those components that contain non-file-data that
the user would like to have uninstalled. This dramatically reduces the
amount of code necessary to go into a multiple instance installer file.
<Component Id="MyDisparateComponent"
Guid="fa6d4980-8780-4cff-b52c-5c3f57e05f48">
<InstanceTransformGuid TransformId="Instance1"
Guid="dd2e906b-0192-43ce-86c2-9e9cec8b1049"/>
<InstanceTransformGuid TransformId="Instance2"
Guid="8a493697-8fdb-455c-a542-5bd4667e8473"/>
</Component>
I thought a nice deterministic algorithm for computing the next GUID
requested by the GenerateGuidForInstanceTransform attribute would be nice
so that you wouldn't need to maintain this list on each component if you
didn't want to.
Thoughts?
jmr
-------------------------------------------------------------------------
This SF.net email is sponsored by the 2008 JavaOne(SM) Conference
Don't miss this year's exciting event. There's still time to save $100.
Use priority code J8TL2D2.
http://ad.doubleclick.net/clk;198757673;13503038;p?http://java.sun.com/javaone
_______________________________________________
WiX-users mailing list
https://lists.sourceforge.net/lists/listinfo/wix-users
--
View this message in context: http://n2.nabble.com/Multiple-Instance-Transforms-Walkthrough%2C-Proposed-Simple-Addition-to-WiX-to-Make-Them-Easier-tp708828p3188477.html
Sent from the wix-users mailing list archive at Nabble.com.
Crazyht
2009-07-26 15:40:49 UTC
Permalink
I tried to use your post, but I have an error on InstanceTransformGuid XML
Tag.
I probably missing something, but what ?

Best regards,
CrazyHT
Post by Josh Rowe
Okay, I've figured out a lot about multiple instance transforms and the
sorts of contortions you have to go through to make them work. I even
figured out how to make a single MSI drive multiple instance transforms
without the need for a bootstrapping program, relying on the fact that the
InstallUISequence executes indepently from the InstallExecuteSequence.
But one thing remains in WiX to make this a lot easier: automatic
GUID-changing of components. In this message, I discuss how to create a
multiple instance transform, how to make a single MSI drive multiple
instance transforms, a common pitfall, and a feature request for WiX.
I want a user, when they double-click an MSI file, to by default install a
new instance. First, obviously, we will need to define some instance
<Property Id="MYINSTANCE"
Value="DontUseThis"
Secure="yes"
/>
<InstanceTransforms Property="MYINSTANCE">
<Instance ProductCode="444e8aa6-c034-4294-b460-07c2d3262f72"
ProductName="Instance 1"
Id="Instance1"/>
<Instance ProductCode="ff0a0580-bc5d-4562-83ec-2edb3511254b"
ProductName="Instance 2"
Id="Instance2"/>
</InstanceTransforms>
So far, so good. What if the new instance ProductName should be driven by
<CustomAction Id="SetProductName"
Property="ProductName"
Value="[[ProductNamePropertyPrefix][MYINSTANCE]]"
/>
<Property Id="ProductNamePropertyPrefix"
Value="ProductName_"
/>
<Property Id="ProductName_DefaultInstance"
Value="Multiple Instance Transforms - Default instance"
/>
<Property Id="ProductName_Instance1"
Value="Multiple Instance Transforms - Instance #1"
/>
<Property Id="ProductName_Instance2"
Value="Multiple Instance Transforms - Instance #2"
<InstallExecuteSequence>
<Custom Action="SetProductName"
Before="ValidateProductID"/>
</InstallExecuteSequence>
Obviously, use whatever algorithm you want in the SetProductName custom
<Directory Id="INSTALLLOCATION"
Name="TestMultipleInstanceTransformUi">
<Directory Id="InstanceDirectory">
</Directory>
</Directory>
<CustomAction Id="SetInstanceDirectory"
Property="InstanceDirectory"
Value="[INSTALLLOCATION][MYINSTANCE]\"/>
<InstallExecuteSequence>
<Custom Action="SetInstanceDirectory"
Before="CostFinalize"><![CDATA[InstanceDirectory = ""]]></Custom>
</InstallExecuteSequence>
Okay, looking good. Now, the user can specify MSINEWINSTANCE=1
TRANSFORMS=Instance1 on the msiexec command line to install a new
instance, right? But that seems a little in-depth for users to have to
worry about. For example, how do they know which instance names are
available? How can they reliably choose the next available instance id?
We can obviously do better. It turns out that the MSI system works in two
phases when a UI is presented. First, the InstallUISequence is executed
in the user's space. Second, the InstallExecuteSequence is executed in a
system process. The two sequences act completely independently; the
InstallUISequence's ExecuteAction just passes a set of property names to
the system msiexec service. So, we can make our parent process pass in
<CustomAction Id="SetTransforms"
Property="TRANSFORMS"
Value="{:[MYINSTANCE];}[TRANSFORMS]"
/>
<CustomAction Id="SetMsiNewInstance"
Property="MSINEWINSTANCE"
Value="1"/>
<InstallUISequence>
<Custom Action="SetTransforms"
Before="ExecuteAction"><![CDATA[ACTION =
"INSTALL"]]></Custom>
<Custom Action="SetMsiNewInstance"
Before="ExecuteAction"><![CDATA[ACTION =
"INSTALL"]]></Custom>
</InstallUISequence>
Now we need a reliable way of setting MYINSTANCE. That's pretty simple,
too. We make our install register each instance id during the install,
<Property Id="INSTANCE1INSTALLEDPRODUCTCODE">
<RegistrySearch Id="LookForInstance1InstalledProductCode"
Key="[InstancesKey]\Instance1"
Name="ProductCode"
Root="HKLM"
Type="raw"/>
</Property>
<Property Id="INSTANCE2INSTALLEDPRODUCTCODE">
<RegistrySearch Id="LookForInstance2InstalledProductCode"
Key="[InstancesKey]\Instance2"
Name="ProductCode"
Root="HKLM"
Type="raw"/>
</Property>
<Property Id="InstancesKey"
Value="Software\Manufacturer\TestMultipleInstance"
/>
Now we have information about each registered instance. (More on actually
getting this registration information into the registry later.) A little
more tricky custom action work sets MYINSTANCE to the first unused
<CustomAction Id="SetMyInstance_Instance1"
Property="MYINSTANCE"
Value="Instance1"
/>
<CustomAction Id="SetMyInstance_Instance2"
Property="MYINSTANCE"
Value="Instance2"
/>
<InstallUISequence>
<Custom Action="SetMyInstance_Instance1"
Before="SetTransforms"><![CDATA[ACTION = "INSTALL" AND
MYINSTANCE = "DontUseThis" AND INSTANCE1INSTALLEDPRODUCTCODE =
""]]></Custom>
<Custom Action="SetMyInstance_Instance2"
After="SetMyInstance_Instance1"><![CDATA[ACTION = "INSTALL"
AND MYINSTANCE = "DontUseThis" AND INSTANCE2INSTALLEDPRODUCTCODE =
""]]></Custom>
</InstallUISequence>
Now, when the ACTION = "INSTALL", just before the SetTransforms action is
called that sets the TRANSFORMS property, the MYINSTANCE property will be
set to the value of the first unused instance id. Assuming, that is, that
we actually got the instance information into the registry. This is where
it gets tricky.
Multiple Instance Transforms don't really work out-of-the-box the way most
people would want to use them. MSI does not uninstall non-file-data for
components that are used by multiple product codes until the final client
product is uninstalled; MSI assumes that non-file data is shared. This
is, of course, bunk, but that's the way it works. If your component GUID
is shared by multiple product installs, then all but the last uninstall
will get an Action: FileAbsent for the shared components, thus not
uninstalling things other than files. So, if you perform multiple
installs of your wonderful multiple-instance transform that includes the
instance id in registry key names and then uninstall them, the registry
keys from all but the last package to be uninstalled will continue to
exist. Ditto for ServiceInstall elements, etc. So, each of your
instances needs a new GUID for each component that contains non-file-data
that must be uninstalled. Further, these components need to be installed
only with that particular instance. One naive way of doing this is to
<Component Id="Registry_Instance1"
Guid="54412340-1f29-44f5-a733-157efb25c8a6">
<Condition><![CDATA[MYINSTANCE = "Instance1"]]></Condition>
<RegistryKey Root="HKLM"
Key="[InstancesKey]\[MYINSTANCE]"
<RegistryValue Id="Presence_Instance1"
Action="write"
Name="ProductCode"
Value="[ProductCode]"
Type="string"
KeyPath="yes"
/>
</RegistryKey>
</Component>
<Component Id="Registry_Instance2"
Guid="ffe23417-09ba-485f-912b-c063bebbac2a">
<Condition><![CDATA[MYINSTANCE = "Instance2"]]></Condition>
<RegistryKey Root="HKLM"
Key="[InstancesKey]\[MYINSTANCE]"
<RegistryValue Id="Presence_Instance2"
Action="write"
Name="ProductCode"
Value="[ProductCode]"
Type="string"
KeyPath="yes"
/>
</RegistryKey>
</Component>
That's just for registration data. Also add any ServiceInstall,
ServiceControl, other stuff, etc., and you'll see that this gets out of
hand very rapidly.
Here is we get to the feature request for WiX. Wouldn't it be nice if the
InstanceTransforms element interacted with the Component element to
produce transformed rows for each component. For example, imagine if you
<Component Id="MyDisparateComponent"
Guid="fa6d4980-8780-4cff-b52c-5c3f57e05f48"
GenerateGuidForInstanceTransform="yes">
<RegistryKey></RegistryKey>
</Component>
Since component GUIDs are only referenced in the Component table and
nowhere else (except possibly ComponentSearch, but you have that problem
anyway), there isn't any reason InstanceTransforms shouldn't output rows
to change the GUIDs for those components that contain non-file-data that
the user would like to have uninstalled. This dramatically reduces the
amount of code necessary to go into a multiple instance installer file.
<Component Id="MyDisparateComponent"
Guid="fa6d4980-8780-4cff-b52c-5c3f57e05f48">
<InstanceTransformGuid TransformId="Instance1"
Guid="dd2e906b-0192-43ce-86c2-9e9cec8b1049"/>
<InstanceTransformGuid TransformId="Instance2"
Guid="8a493697-8fdb-455c-a542-5bd4667e8473"/>
</Component>
I thought a nice deterministic algorithm for computing the next GUID
requested by the GenerateGuidForInstanceTransform attribute would be nice
so that you wouldn't need to maintain this list on each component if you
didn't want to.
Thoughts?
jmr
-------------------------------------------------------------------------
This SF.net email is sponsored by the 2008 JavaOne(SM) Conference
Don't miss this year's exciting event. There's still time to save $100.
Use priority code J8TL2D2.
http://ad.doubleclick.net/clk;198757673;13503038;p?http://java.sun.com/javaone
_______________________________________________
WiX-users mailing list
https://lists.sourceforge.net/lists/listinfo/wix-users
--
View this message in context: http://n2.nabble.com/Multiple-Instance-Transforms-Walkthrough%2C-Proposed-Simple-Addition-to-WiX-to-Make-Them-Easier-tp708828p3329391.html
Sent from the wix-users mailing list archive at Nabble.com.
supriya_n
2011-02-03 08:16:22 UTC
Permalink
Hi Josh,

Really your article is very helpful to work with instance transforms
concept. I have created one MSI which inturn installed with different
instances on the same server. The msi get's uninstalled with same instance
and with same version only through octopus. If i change the product version
then i am able to install same instance also. But my problem is, I am not
able to uninstall any of instances through control panel. While uninstalling
i am getting this error "Could not access network location default\null".

Meanwhile i haven't used different instance locations and differnet
instance component guid's.
I am placing my piece of code here.
<Directory Id="TARGETDIR" Name="SourceDir">

<Directory Id="FSVDIRPTH" Name=".">
<Directory Id="SHAREPOINT_VIRTUAL_DIRECTORY" Name="FSSharepoint">
<Component Id="FS_Sharepoint_Vdir"
Guid="{0CF00407-6209-4EB8-81C9-48499D20D0C2}">
<CreateFolder />
</Component>
<Component Id="FSSharePoint"
Guid="{0E34D913-2511-4BD3-A895-7F4D7A38ED2D}">
<CreateFolder/>
</Component>
</Directory>
</Directory>
</Directory>

please suggest me a solution.

Thanks & Regards
priya
--
View this message in context: http://windows-installer-xml-wix-toolset.687559.n2.nabble.com/Multiple-Instance-Transforms-Walkthrough-Proposed-Simple-Addition-to-WiX-to-Make-Them-Easier-tp708828p5987913.html
Sent from the wix-users mailing list archive at Nabble.com.
Stephen Reindl
2011-02-03 12:06:07 UTC
Permalink
Hello,

As MSI doesn't remember properties, pass them on the command line (i.e. SHAREPOINT_VIRTUAL_DIRECTORY="<path>").

The problem is that the instance mapping custom action is performed after the path to be uninstalled is checked.

Regards
Stephen
Post by Josh Rowe
-----Original Message-----
Sent: Thursday, February 03, 2011 9:16 AM
Subject: Re: [WiX-users] Multiple Instance Transforms Walkthrough,
Proposed Simple Addition to WiX to Make Them Easier
Hi Josh,
Really your article is very helpful to work with instance transforms concept.
I have created one MSI which inturn installed with different instances on the
same server. The msi get's uninstalled with same instance and with same
version only through octopus. If i change the product version then i am able
to install same instance also. But my problem is, I am not able to uninstall any
of instances through control panel. While uninstalling i am getting this error
"Could not access network location default\null".
Meanwhile i haven't used different instance locations and differnet
instance component guid's.
I am placing my piece of code here.
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="FSVDIRPTH" Name="."> <Directory
Id="SHAREPOINT_VIRTUAL_DIRECTORY" Name="FSSharepoint">
<Component Id="FS_Sharepoint_Vdir"
Guid="{0CF00407-6209-4EB8-81C9-48499D20D0C2}">
<CreateFolder />
</Component>
<Component Id="FSSharePoint"
Guid="{0E34D913-2511-4BD3-A895-7F4D7A38ED2D}">
<CreateFolder/>
</Component>
</Directory>
</Directory>
</Directory>
please suggest me a solution.
Thanks & Regards
priya
--
View this message in context: http://windows-installer-xml-wix-
toolset.687559.n2.nabble.com/Multiple-Instance-Transforms-Walkthrough-
Proposed-Simple-Addition-to-WiX-to-Make-Them-Easier-
tp708828p5987913.html
Sent from the wix-users mailing list archive at Nabble.com.
------------------------------------------------------------------------------
Special Offer-- Download ArcSight Logger for FREE (a $49 USD value)!
Finally, a world-class log management solution at an even better price-free!
Download using promo code Free_Logger_4_Dev2Dev. Offer expires
February 28th, so secure your free ArcSight Logger TODAY!
http://p.sf.net/sfu/arcsight-sfd2d
_______________________________________________
WiX-users mailing list
https://lists.sourceforge.net/lists/listinfo/wix-users
properto
2011-12-21 12:15:05 UTC
Permalink
If you need to generate multiple components (each for a different instance)
with a specific GUID, you don't need to have an additional feature request.
You can use pre-processor extension. I put it together:

define specific GUIDS for each instance

<?define GUID_0001.INSTANCE01 = '*** real guid ***' ?>
<?define GUID_0001.INSTANCE02 = '*** real guid ***' ?>
<?define GUID_0001.INSTANCE03 = '*** real guid ***' ?>

<?define GUID_0002.INSTANCE01 = '*** real guid ***' ?>
<?define GUID_0002.INSTANCE02 = '*** real guid ***' ?>
<?define GUID_0002.INSTANCE03 = '*** real guid ***' ?>

define instance list

<?define InstanceIDList=01;02;03 ?>

define components using foreach loop - with condition
<?foreach INST in $(var.InstanceIDList)?>
<Component Id='cmpId$(var.INST)'
Guid='$(instance_tools.GetGUIDVariable(0001, $(var.INST)))'>
<File Id='fileId$(var.INST)_01' Name='....' DiskId='1' Source='...'
KeyPath='yes'>
</File>
<Condition></Condition>
</Component>
<?endforeach?>

Please note there is a special "instance_tools.GetGUIDVariable"
pre-processor call used. This is written in a separate DLL (c#) as a
pre-processor extension. Purpose of this is to compute the name of GUID
variable to be used. This cannot be done in WIX directly (as of 3.5). The
code looks like this:

public class CIPPWIXExtension : PreprocessorExtension
{

private static string[] prefixes = { "instance_tools" };
public override string[] Prefixes { get { return prefixes; } }

public override string EvaluateFunction(string prefix, string
function, string[] args)
{
string result = null;
switch (prefix)
{
case "instance_tools":
switch (function)
{
case "GetGUIDVariable":
if (args.Length == 2)
{
//0 - GUID ID
//1 - Instance ID
string var_name = ("GUID_" + args[0] +
".INSTANCE" + args[1]);
string res =
this.Core.GetVariableValue(null, "var", var_name);
if (String.IsNullOrEmpty(res))
return "[No such variable found: " +
var_name + "]";
else
return res;
}
else
{
result = String.Empty;
}
break;
}
break;
}
return result;
}
}

--
View this message in context: http://windows-installer-xml-wix-toolset.687559.n2.nabble.com/Multiple-Instance-Transforms-Walkthrough-Proposed-Simple-Addition-to-WiX-to-Make-Them-Easier-tp708828p7114938.html
Sent from the wix-users mailing list archive at Nabble.com.

Loading...