Multiple Instance Transforms Walkthrough, Proposed Simple Addition to WiX to Make Them Easier

classic Classic list List threaded Threaded
10 messages Options
Reply | Threaded
Open this post in threaded view
|

Multiple Instance Transforms Walkthrough, Proposed Simple Addition to WiX to Make Them Easier

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 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

 


-------------------------------------------------------------------------
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
[hidden email]
https://lists.sourceforge.net/lists/listinfo/wix-users
Reply | Threaded
Open this post in threaded view
|

Re: Multiple Instance Transforms Walkthrough, Proposed Simple Addition to WiX to Make Them Easier

shawny
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
Reply | Threaded
Open this post in threaded view
|

Re: Multiple Instance Transforms Walkthrough, Proposed Simple Addition to WiX to Make Them Easier

Josh Rowe
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: [hidden email] [mailto:[hidden email]] On Behalf Of shawny
Sent: Tuesday, September 16, 2008 11:17 AM
To: [hidden email]
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?


-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
WiX-users mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/wix-users
Reply | Threaded
Open this post in threaded view
|

Re: Multiple Instance Transforms Walkthrough, Proposed Simple Addition to WiX to Make Them Easier

amrish
In reply to this post by Josh Rowe
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?

Josh Rowe wrote
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


-------------------------------------------------------------------------
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
WiX-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/wix-users
Reply | Threaded
Open this post in threaded view
|

Re: Multiple Instance Transforms Walkthrough, Proposed Simple Addition to WiX to Make Them Easier

amrish
In reply to this post by Josh Rowe

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?


Josh Rowe wrote:

>
> 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
>
>
> -------------------------------------------------------------------------
> 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
> [hidden email]
> 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.


------------------------------------------------------------------------------
_______________________________________________
WiX-users mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/wix-users
Reply | Threaded
Open this post in threaded view
|

Re: Multiple Instance Transforms Walkthrough, Proposed Simple Addition to WiX to Make Them Easier

supriya_n
In reply to this post by Josh Rowe
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
Reply | Threaded
Open this post in threaded view
|

Re: Multiple Instance Transforms Walkthrough, Proposed Simple Addition to WiX to Make Them Easier

Stephen Reindl
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


> -----Original Message-----
> From: supriya_n [mailto:[hidden email]]
> Sent: Thursday, February 03, 2011 9:16 AM
> To: [hidden email]
> 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
> [hidden email]
> https://lists.sourceforge.net/lists/listinfo/wix-users

------------------------------------------------------------------------------
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
[hidden email]
https://lists.sourceforge.net/lists/listinfo/wix-users
Reply | Threaded
Open this post in threaded view
|

Re: Multiple Instance Transforms Walkthrough, Proposed Simple Addition to WiX to Make Them Easier

properto
In reply to this post by Josh Rowe
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>
<![CDATA[PRODUCT_INSTANCE = "Instance$(var.INST)"]]>
</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;
        }
}
Reply | Threaded
Open this post in threaded view
|

Re: Multiple Instance Transforms Walkthrough, Proposed Simple Addition to WiX to Make Them Easier

Anthony Dewhirst
This post has NOT been accepted by the mailing list yet.
Hi All,

I am looking to find a solution to the following problem:

We currently have 3 msi for our solution which include a database, web and service installation.
We upgrade these installs on a regular basis.
The client has asked for the ability to have side by side installs. Obviously, each will need to be updated as well.

I came across this post and thought that this looked like it might help.
We currently use Setup projects from within VS 2010 and as you already know, these don't support side by side out of the box.

I am completely green wrt to WiX and I am trying to get my head around all of this.
Does someone have an example or a walk though of how to put this solution together and also, will this work for updates as well?

Regards
Anthony
Reply | Threaded
Open this post in threaded view
|

Re: Multiple Instance Transforms Walkthrough, Proposed Simple Addition to WiX to Make Them Easier

dhaneshbiradavada
This post has NOT been accepted by the mailing list yet.
In reply to this post by amrish
Hello Amrish, Apparently i have the same challenge right now which u posted ages back...it would be great if u can share how did u overcome this task. Any help much appreciated.