Instance Transform incorrect on update

2019-06-26 03:49发布

问题:

I'm using instance transforms to install the same product multiple times on the same machine. When starting the .msi installation file, I pass TRANSFORMS=":X" (and several other variables needed for the installation) on the command line, where X is the version of the Program to use.

In the Product.wxs file I then assign a new ProductCode using the InstanceTransforms tag, e.g.:

<Property Id="INSTANCEID" Value="Default"/>
<InstanceTransforms Property="INSTANCEID">
  <Instance Id="I1" ProductCode="$(guid.NewGuid())"/>
  <Instance Id="I2" ProductCode="$(guid.NewGuid())"/>
  <Instance Id="I4" ProductCode="$(guid.NewGuid())"/>
  <Instance Id="I3" ProductCode="$(guid.NewGuid())"/>
  <Instance Id="I5" ProductCode="$(guid.NewGuid())"/>
  <Instance Id="I6" ProductCode="$(guid.NewGuid())"/>
  <Instance Id="I7" ProductCode="$(guid.NewGuid())"/>
  <Instance Id="I8" ProductCode="$(guid.NewGuid())"/>
  <Instance Id="I9" ProductCode="$(guid.NewGuid())"/>
</InstanceTransforms>

Additionally, the UpgradeCode is added to the UpgradeTable in a custom action, the relevant code is:

Database db = session.Database;
session.Log("Get DB: {0}", db.FilePath ?? string.Empty);
string sqlInsertSring = db.Tables["Upgrade"].SqlInsertString + " TEMPORARY";
session.Log("DB Tables, querying with SQL: {0}", sqlInsertSring);
View view = db.OpenView(sqlInsertSring);
session.Log("OpenView, adding two new records to the UpgradeView.");
session.Log("Inserting line: {0}, null, {1}, null, 512, null, \"UPDATE\"",     
session["UpgradeCode"], session["ProductVersion"]);
view.Execute(new Record(new object[] { session["UpgradeCode"], null,  
session["ProductVersion"], null, 512, null, "UPDATE"}));
session.Log("Inserting line: {0}, {1}, null, null, null, 0, \"NEWERVERSIONINSTALLED\"", 
session["UpgradeCode"], session["ProductVersion"]);
view.Execute(new Record(new object[] { session["UpgradeCode"], 
session["ProductVersion"], null, null, 0, null, "NEWERVERSIONINSTALLED" }));
view.Close();

Ok, so far so good, this works for installations, allowing me to successfully install two instances of the same program. However, Updates don't seem to work. I can upgrade one of the instances, but when upgrading the second instance the transformation doesn't work correctly and the other instance is upgraded again.

Example: Two instances (I1 & I2). Upgrading I2 works fine. However, when I subsequently try to update I1, it just Updates I2 again (although the correct Transformation is passed).

The log of the MSI shows that the Transformation is first reverted to I1 (as it should), and then set back again to I2 (for reasons I can't fathom):

 - MSI (s) (B0:F8) [09:06:48:745]: Running product '{C33371A0-C32A-4120-BD8F-ACDC79E13458}' with elevated privileges: Product is assigned.
 - MSI (s) (B0:F8) [09:06:48:776]: PROPERTY CHANGE: Modifying TRANSFORMS property. Its current value is ':I2'. Its new value: ':I1'.
 - MSI (s) (B0:F8) [09:06:48:823]: PROPERTY CHANGE: Adding DATADIR property. Its value is 
MSI (s) (B0:F8) [09:06:49:463]: PROPERTY CHANGE: Adding CURRENTDIRECTORY property. Its value is 'C:\Users\Administrator\Desktop'.
 - [Further arguments passed]
 - MSI (s) (B0:F8) [09:06:49:494]: PROPERTY CHANGE: Adding CLIENTUILEVEL property. Its value is '2'.
 - MSI (s) (B0:F8) [09:06:49:572]: PROPERTY CHANGE: Adding CLIENTPROCESSID property. Its value is '6424'.
 - MSI (s) (B0:F8) [09:06:49:587]: Machine policy value 'DisableAutomaticApplicationShutdown' is 0
 - MSI (s) (B0:F8) [09:06:49:634]: RESTART MANAGER: Disabled by MSIRESTARTMANAGERCONTROL property; Windows Installer will use the built-in FilesInUse functionality.
 - MSI (s) (B0:F8) [09:06:49:681]: PROPERTY CHANGE: Adding MsiSystemRebootPending property. Its value is '1'.
 - MSI (s) (B0:F8) [09:06:49:728]: PROPERTY CHANGE: Modifying TRANSFORMS property. Its current value is ':I1'. Its new value: ':I2'.
 - MSI (s) (B0:F8) [09:06:49:821]: TRANSFORMS property is now: :I2

The last lines are significant: The TRANSFORMS property is set to I2 again, meaning that the installation will Update instance I2 instead of Instance I1.

Why could this be happening? The TRANSFORMS is passed correctly, I can't see why the property would be set back again.

(If more code or explenations are needed I'm happy to provide).

EDIT: BTW I'm using WiX toolset 3.7.

EDIT 2: For installation, I'm calling the .msi with following parameters:

MSINEWINSTANCE=1 TRANSFORMS=":I[N]" [Further parameters] (where N is the Instance)

For updates, I'm calling the .msi without MSINEWINSTANCE, starting directly with the TRANSFORMS part

As a dirty fix I'm now Uninstalling/Installing on update, which isn't exactly what I had planned.

回答1:

You don't mention the MSINEWINSTANCE property so perhaps you aren't calling the installation correctly. This property should be set to one. For more details see: Installing Multiple Instances with Instance Transforms.

I've also explained the process on my blog: (Wow, was it really 8 years ago?)

Multiple Instance MSI's and InstallShield 12

Update:

I was able to correctly implement this using WiX and IsWiX.

Step 1: Use the IsWiX multiproject solution accelerator template to create an MSI/MSM project.

Step 2: Add a file to the merge module so that the installer has a component with a file as a keypath to install. (This is required because a) you need to register at least one component to have a feature/product considered installed and b) components with keypaths that are not files have to be mutated with different guids and conditional installation based on instance id. To get around this and key it simple just create a component with a file as a keypath )

Step 3: Add the following XML to the primary wxs file Product.wxs:

<SetProperty Id="INSTALLLOCATION" Value="[ProgramFilesFolder][Manufacturer]\[ProductName]" Before="AppSearch" Sequence="first">Not INSTALLLOCATION and Not Installed</SetProperty>
<Property Id="InstanceId" Value="0"/> 
<InstanceTransforms Property="InstanceId">
  <Instance Id="I1" ProductCode="*" UpgradeCode="{10E90C30-F117-4EE8-A084-25E4D0076CE4}" ProductName="ProductName-1" />
  <Instance Id="I2" ProductCode="*" UpgradeCode="{919F5399-4E7A-4D8A-9484-A85D0F5E2C77}" ProductName="ProductName-2" />
  <Instance Id="I3" ProductCode="*" UpgradeCode="{0BEAEF92-1821-4909-A83A-9B2AE2194AAC}" ProductName="ProductName-3" />
</InstanceTransforms>

This code mutates the installation directory on first install if a value wasn't passed at the command line and if the product wasn't previously installed (in which case directories are immutable) I also use * to get a random ProductCode and pass in an UpgradeCode value since the project template uses MajorUpgrades out of the box and each instance is a unique product family for upgrade purposes. Finally I transform ProductName to be unique in Add/Remove programs and to drive the installation location transformation.

Install the primary instance:

msiexec /I ProductName.msi

Install the secondary instance:

msiexec /I ProductName.msi MSINEWINSTANCE=1 TRANSFORMS=":I1"
msiexec /I ProductName.msi MSINEWINSTANCE=1 TRANSFORMS=":I2"
msiexec /I ProductName.msi MSINEWINSTANCE=1 TRANSFORMS=":I3"

Note the use of ":" in the TRANSFORMS property. This specifies an embedded transform in the Storages table.

Both installations appear side by side in Program Files and Programs and Features.