cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
chgruber
Level 4

Multiple Instances and Chained MSI

Hi -
I am trying to install multiple instances with chained msi's with IS Basic MSI.

Everything works on the first instance install, but the 2nd installation's msi's don't appear to be working (because they think they are already installed).

What do I need to do to get this working?

EDIT: I am going to start going down the path where all of the chained MSI's themselves allow multiple instances and then sending those InstanceId from the command-line.

-grubersauce
Labels (1)
0 Kudos
(11) Replies
chgruber
Level 4

Another problem I am having is overwriting the Product Name so that the user can specify what shows up in Add/Remove programs. ex:

User specifies "Install 1" for first install
User specifies "Install 2" for 2nd instance

Add/Remove Programs would have

MyProduct - Install 1
MyProduct - Install 2

That way they know which instance they are installing - this has to be dynamic/user specified.

Is there a way to do this?
0 Kudos
chgruber
Level 4

Hopefully I'm not the only person that will ever have to do this, but here is a solution to get a user-specified instance name into Add/Remove Programs. If there is a simpler way, please let me know. I may have well hung myself by the time i get this completed.

When the user specifies a destination for the install, we also have a textbox with the property INSTANCENAME. This way the user can specify a name for this particular instance of the product.

First, add as many instances in the Media/Releases/[Release] Multiple Instances tab. It will create a ProductCode property. Add another property, I tried ProductID, however this doesn't get overwritten on subsequent instance installs. :mad:

Now you're gonna have to install all the instances of your product. (Unless there is a way to look at the Instance#.mst files which get embedded into the parent msi - someone holla at me if you found a way to do this) I have 10, so this took the better part of day.

After each installation, go to HKLM\SOFTWARE\Classes\Installer\Products and look through each guid-named folder until you find the latest install. Write down or keep a listing of the GUIDs with the corresponding instance number.

After all that is done, go back into the instances and set that ProductID property to be what that GUID was. Almost done. Hang with me.

Should look something like this:
http://i483.photobucket.com/albums/rr195/Grubersauce/InstallShield/instances.jpg

I added a InstallScript Custom Action to the OK button on the SetupCompleteSuccess dialog to be called before the installer closes. It basically overwrites the registry key that Add/Remove Programs looks at to get the name of the program. Here is that method:

function WriteInstanceNameToRegistry(hMSI)     
NUMBER nvType, nvSize;
STRING szInstanceName, szInstanceRegPath, szProductID;
begin
//set initial value(s)
nvType = REGDB_STRING;
nvSize = 256;

RegDBSetDefaultRoot(HKEY_LOCAL_MACHINE);

MsiGetProperty(hMSI, "ProductID", szProductID, nvSize);
MsiGetProperty(hMSI, "INSTANCENAME", szInstanceName, nvSize);
// Lets not let them leave that blank
if( szInstanceName = "" ) then
szInstanceName = "Grubesauce";
endif;
szInstanceRegPath = "Software\\Classes\\Installer\\Products\\" + szProductID;

if( RegDBSetKeyValueEx (szInstanceRegPath, "ProductName", nvType, szInstanceName, nvSize) < 0 ) then
//MessageBox("Fail" , WARNING);
else
// MessageBox("W00t! ", INFORMATION);
endif;

end;


When you're done, you should be able to go into Add/Remove Programs and see something similar to this:

http://i483.photobucket.com/albums/rr195/Grubersauce/InstallShield/arp.jpg

We are trying to do this with chained msi packages which are selectively installed based on features selected and I haven't moved on to try to tackle that yet (only the first instance of the chained msi packages is being installed).

I'm guessing I will have to do something like this:
TRANSFORMS="transformname[INSTANCEID].mst" MSINEWINSTANCE=1
At least with the chained msi's I can manipulate the ProductID in the .mst using Orca and not have to install 10 instances of each of the 3 chained msi's
If I come up with a solution, I will post it here.
0 Kudos
chgruber
Level 4

Another update:

So I was able to get multiple instances with chained MSIs to install as I mentioned in my last post. Here is the basics.

First, create as many transforms for each chained MSI as you have instances available of your installation. Here is how we named:
If the MSI file was named First.msi then the transforms were named First1.mst, First2.mst, First3.mst, etc.

Place them in the same folder as your chained MSIs (probably, Packages/chained1.msi like in the whitepaper).

Sometime in your install, call a custom action similar to this:

function CreateInstanceName(hMSI)     
NUMBER nvSize;
STRING szInstanceName, szInstanceId;
STRING szThird, szSecond, szFirst;
begin
//set initial value(s)
nvSize = 256;

MsiGetProperty(hMSI, "INSTANCENAME", szInstanceName, nvSize);
MsiGetProperty(hMSI, "InstanceId", szInstanceId, nvSize);

if (szInstanceId = "0" ) then
else
MsiSetProperty(hMSI, "INSTANCENAME", szInstanceName + szInstanceId);

// Build the Transforms strings for child MSIs
szFirst = "TRANSFORMS=FirstMSI" + szInstanceId + ".mst MSINEWINSTANCE=1 ";
szSecond = "TRANSFORMS=SecondMSI" + szInstanceId + ".mst MSINEWINSTANCE=1 ";
szThird = "TRANSFORMS=ThirdMSI" + szInstanceId + ".mst MSINEWINSTANCE=1 ";
//MessageBox(szUMS, INFORMATION);
MsiSetProperty(hMSI, "FIRSTINSTANCESTRING", szFirst );
MsiSetProperty(hMSI, "SECONDINSTANCESTRING", szSecond );
MsiSetProperty(hMSI, "THIRDINSTANCESTRING", szThird );

endif;
end;


Then in your chained msi install properties, for the first msi, you put [FIRSTINSTANCESTRING] before your other properties. Make sure the default for this property is an empty string so on the first install nothing is there.

Oh yea, there is one catch: I haven't figured out how to get them to uninstall when the parent uninstalls, even though ARPSYSTEMCOMPONENT=1 is sent to the chained msi and REMOVE="ALL" is in the removal condition.
0 Kudos
MichaelU
Level 12 Flexeran
Level 12 Flexeran

This is a very interesting approach. One more approach you can try (particularly for the removal case) is to schedule a custom action in the execute sequence before ISChainPackagePrepare which would make modifications to the ISChainPackage table. While the table isn't exactly documented, most of it just follows the fields exposed in the IDE. This may allow you to override the product code, etc., that our chaining support looks for.

I forget if you can apply temporary modifications to existing records, though. Worst case you may need to condition the real rows off (if you're using streamed data files), or even remove them entirely and add the custom actions directly (if you're using just external files), and create all the rows that will be used as new temporary records. It's definitely messy, but it might get you where you want to be.
0 Kudos
chgruber
Level 4

Thanks Michael -
I'm already ahead of you. I tried the overwriting the ProductCode in the ISChainPackage table.... but I couldn't get it working so I gave up on it since I didn't even know if it would do what I was trying to make it do :confused: Maybe someone else can get it working: This should give them a good head start:

[CODE]
prototype AssignNewProductCodesToChainedMSIs(HWND);
prototype OLE32.CoCreateGuid(POINTER);
prototype OLE32.StringFromGUID2(POINTER, BYREF STRING, LONG);
prototype CreateStringGUID(HWND);

typedef GUID
begin
LONG Data1;
SHORT Data2;
SHORT Data3;
STRING Data4[8];
end;

GUID guid;
GUID POINTER pGuid;
WSTRING wszGuid[40];
STRING szGuid[40];

INT i;
NUMBER n;


function AssignNewProductCodesToChainedMSIs(hMSI)
HWND hDB, hView, hRecord, hViewprop, hRecordProp;
NUMBER result;
begin

hDB = MsiGetActiveDatabase(hMSI);
MsiDatabaseOpenView(hDB, "SELECT * FROM `ISChainPackage`", hView);
MsiViewExecute(hView, NULL);
MessageBox("ExecutedView", INFORMATION);
result = MsiViewFetch(hView, hRecord);
while( result == 0)

guid.Data1 = 0;
guid.Data2 = 0;
guid.Data3 = 0;
pGuid = &guid;

CoCreateGuid(pGuid);
n = StringFromGUID2(pGuid, wszGuid, 40);

for i = 0 to (n - 1)
szGuid = wszGuid[i * 2];
endfor;
MessageBox("New Guid: " +szGuid, INFORMATION);

// Column 3 in ISChainPackage is ProductCode
MsiRecordSetString(hRecord, 3, szGuid);

MsiViewModify(hView, MSIMODIFY_UPDATE, hRecord);
//MsiViewClose(hRecord);
MsiCloseHandle(hRecord);
result = MsiViewFetch(hView, hRecord);
endwhile;
MsiCloseHandle(hView);
MsiDatabaseCommit(hDB);

end; [/CODE]
0 Kudos
MichaelU
Level 12 Flexeran
Level 12 Flexeran

Yeah, I hear you. I think ideally we'd need to add a flag and a build step which made these changes in the instance transforms such that your outer and chained MSIs applied instance transforms in lock-step. Perhaps you could stream out, regenerate with updated information, and stream in the embedded instance transforms? It would require delaying your digital signature (if you're using one) which introduces another set of annoyances...
0 Kudos
EJSchuiteman
Level 4

Hi,

an easier way to change the name in Add/Remove Programs is to set property ProductName with the values you want. Make sure it executes in the Execute Sequence.

In Custom Actions choose "New Set Property".

Property Name: ProductName
Property Value: [YOURNEWPROPERTY]

Value can be more than just one property.
0 Kudos
chgruber
Level 4

EJSchuiteman wrote:
Hi,

an easier way to change the name in Add/Remove Programs is to set property ProductName with the values you want. Make sure it executes in the Execute Sequence.

In Custom Actions choose "New Set Property".

Property Name: ProductName
Property Value: [YOURNEWPROPERTY]

Value can be more than just one property.


Thanks for the heads up EJ. I just assumed a variable/property in there would not work as it doesn't when setting through the IS UI.
0 Kudos
EJSchuiteman
Level 4

When installer goes from UI to Execute Sequence it is setting the private properties to its default values.

Setting private properties in the execute sequence fixes this.
0 Kudos
chgruber
Level 4

Thanks EJ. It helps a lot to understand what is going on behind the scenes.

As far as multiple instances with chained msi's - I've been changing the product codes for the chained msi's with MsiViewModify scripts, but they never get installed on multiple instances. The msi log always shows :

InstallShield : Skipping chained package CHAIN1 installation because it is already installed

I assume the ISChainPackagePrepare method is looking to see if they are installed - having a hard time 'fooling' it into thinking that they are not.

Any ideas?
0 Kudos
chgruber
Level 4

Alright - I have no idea why this would make a difference....

In the Install Condition, I put back in "Not Installed" and all of a sudden it lays down the chained msi. What is going on here?

OK - Apparently that was a fluke. Can't get it to work again.:mad:

I think from here I'm going to try to merge msi's with their transforms into the original MSI instead of trying to call them with transforms. I'll let you know know how that goes. :confused:
0 Kudos