Using Active Setup to Deploy User Based Settings via an MSI
This article will build upon the concepts discussed in the previous post. Our aim is to modify an existing MSI to deploy an additional set of user-based settings (for example, adding user-specific registry entries). The challenge is to ensure these user-based settings are applied to all users of the computer, not just the user that happens to be logged on when the MSI is installed. We'll do this by using a concept called Active Setup.
The Active Setup process runs when a user logs onto a computer. It iterates through the sub-keys under:
HKLM\Software\Microsoft\Active Setup\Installed Components\
And compares each key to the sub-keys under:
HKCU\Software\Microsoft\Active Setup\Installed Components\
If the referenced sub-key does not exist under HKCU, or if the Version value under the key is less than that of the value under HKLM, Active Setup will execute the command assigned to the StubPath variable. That all sounds a little confusing, but hopefully the following two images will help to clarify the process.
The first image shows the key created by Windows Media Player. The key is named after the Media Player ProductCode {6BF52A52-394A-11d3-B153-00C04F79FAA6}. The Version number is 12,0,7600,16385 and the StubPath is %SystemRoot%\system32\unregmp2.exe /FirstLogon /Shortcuts /RegBrowsers /ResetMUI.

The second image shows the same registry key under HKCU. This entry was created when Active Setup executed the StubPath command when I logged onto the computer. As the the key now exists, and the Version of the two keys is identical, the command will not execute again when I next log on.

As you can probably tell, by registering Version and StubPath values, an MSI is able to ensure that any code necessary for creating user-based settings is invoked when a user logs on.
A significant number of the steps outlined below are similar to those discussed in the previous post. They have been reproduced in this article to ensure the following procedure stands alone. However, for background information on Orca and GuidGen, please refer to the previous post.
For the purposes of this tutorial, let's assume you have a folder named C:\Package that includes a single file named setup.msi that has been provided by a vendor. The default behaviour is for the MSI to install into a folder named C:\Program Files\ProductABC. In addition to the files typically installed as part of the package, you want to include and execute two additional scripts. The first is called ConfigMachine.vbs. It only includes machine-specific settings, and need only be executed during the initial installation process. The second script is called ConfigUser.vbs. This script includes user-specific settings that need to be applied for every user that logs onto the computer (and for which we will use Active Setup).
Create a new folder under C:\Package called Cabinet.
Copy the files to be included in the MSI (in this example, ConfigMachine.vbs and ConfigUser.vbs) into the Cabinet folder. For the purpose of our demonstration, we'll keep the files really simple. Add the following text to ConfigMachine.vbs:
Set oShell = CreateObject("WScript.Shell") oShell.RegWrite "HKLM\Software\Microsoft\Test", "TestCompData", "REG_SZ"And add the following text to ConfigUser.vbs:
Set oShell = CreateObject("WScript.Shell") oShell.RegWrite "HKCU\Software\Microsoft\Test", "TestUserData", "REG_SZ"The first script creates a value in the registry under HKLM named Test with the data TestCompData. The second script creates a value in the registry under HKCU named Test with the data TestUserData. The first entry will be created at the time of installation, whilst the second entry will be created for every user of the computer when each user first logs on.
Create a text file inside the Cabinet folder named cabinet.ddf. Add the text shown below and save the file.
.OPTION EXPLICIT .Set CabinetNameTemplate=cabinet.cab .set DiskDirectoryTemplate=CDROM .Set CompressionType=MSZIP .Set UniqueFiles="OFF" .Set Cabinet=on .Set DiskDirectory1=. ConfigMachine.vbs ConfigUser.vbs
In this example, cabinet.cab is the name of the cabinet file that will be created to hold ConfigMachine.vbs and ConfigUser.vbs. A cabinet file is a file archive format developed by Microsoft (kind of like zip).
In the Cabinet folder, create a batch file named MakeCab.cmd with the following commands:
makecab.exe /f cabinet.ddf del setup.inf del setup.rpt move /Y cabinet.cab ..\cabinet.cab
This script will create and copy a cab file named cabinet.cab into the directory containing the MSI. Note: If makecab.exe is not in the path, you can copy it to the Cabinet folder.
Open a command prompt, navigate to the Cabinet directory, and run MakeCab.cmd.
Open Orca.
From the File menu select Open.
Navigate to the MSI and click OK.
From the Transform menu select New Transform.
Navigate to the Media table.
Right click in the right window and from the context menu select Add Row.
Provide a DiskId of one greater than the last DiskId in the table. Provide a LastSequence number equal to the previous LastSequence number plus the number of files inside the cabinet (e.g. if the last sequence number was 1202, as there are only two files inside cabinet.cab, set LastSequence to 1204). Set Cabinet to cabinet.cab. Click OK.
Navigate to the Component table.
Right click in the right window and from the context menu select Add Row.
Set Component to cabinet.cab. Set ComponentID to the value of a random GUID generated via GuidGen.exe (run the tool, select Registry Format, click New GUID and then Copy, return to Orca, and paste the result into the ComponentID field). Set Directory_ to TARGETDIR (assumes the files in cabinet.cab will be deployed to the ProductABC folder under Program Files). Set Attributes to 2.
Navigate to the File table.
Right click in the right window and from the context menu select Add Row.
Set File and FileName to ConfigMachine.vbs. Set Component_ to cabinet.cab. Set FileSize to the size in bytes of ConfigMachine.vbs. This value can be obtained by getting the properties of the file and noting the size in bytes (do not confuse this value with either the size in KB, or the size on disk). Set Attributes to 16384 (indicates the file is compressed inside cabinet.cab). Set Sequence to be 1 greater than the highest sequence number in the table (sort the table by sequence number to determine the highest number).
Repeat steps 17 and 18 for the file named ConfigUser.vbs.
Navigate to the FeatureComponents table.
Right click in the right window and from the context menu select Add Row.
Set Feature to the name of an existing feature in the table. Set Component_ to cabinet.cab. This steps is required to link the new component containing the cabinet, to an existing feature. When the feature is deployed by the MSI, so will the linked component, and hence the files within the referenced cabinet.
Navigate to the CustomAction table.
Right click in the right window and from the context menu select Add Row.
Set Action to RunConfigMachine.vbs (the name of the script with a prefix of Run). Set Type to 22 (this indicates the script is a vbscript). Refer to the Microsoft web site for the complete list of custom action types. Set Source to RunConfigMachine.vbs.
Navigate to the InstallExecuteSequence table.
Right click in the right window and from the context menu select Add Row.
Set Action to RunConfigMachine.vbs (please note, this is the name of the custom action, not the name of script). Set Condition to NOT REMOVE AND ADMINUSER (this indicates the script will execute unless the product is to be uninstalled). Set Sequence to a number after the sequence number of the InstallFinalize action (typically defined as 6600). For example, set the value to 6650. Any action executed prior to InstallFinalize will not change the system (i.e. the script will execute in the graphical section of the installation routine where system modification is not permitted).
Repeat steps 24 to 28, replacing all instances of RunConfigMachine.vbs with RunConfigUser.vbs. And when reeating step 28, set the Condition to NOT REMOVE.
Navigate to the Registry table.
Right click in the right window and from the context menu select Add Row.
Set Registry to ACTIVESETUP.STUBPATH. Set Root to 2 (indicates the registry values will be created under HKLM). Set Key to Software\Microsoft\Active Setup\Installed Components\[ProductCode]\StubPath. [ProductCode] is a variable that gets set at runtime to the ProductCode of the MSI (as stored in the ProductCode field of the Property table). Set Name to +. This indicates the registry key will be created if it does not exist. Set Component to cabinet.cab. Set Value to:
msiexec.exe /fpu [ProductCode]
The f requests a repair of the application, p indicates the repair should only occur if content is missing, and the u indicates user-specific registry settings should be applied.
Add another registry item. Right click in the right window and from the context menu select Add Row.
Set Registry to ACTIVESETUP.VERSION. Set Root to 2. Set Key to Software\Microsoft\Active Setup\Installed Components\[ProductCode]\Version. Set Name to +. Set Component to cabinet.cab. Set Value to the version of the application. The version number can often be obtained by selecting Summary Information from the View menu in Orca and inspecting the Comments field. Alternatively, just set it to 1.
Save the transform (Generate Transform from the Transform menu) to the same directory as the MSI, with the same name as the MSI, but with an .mst extension. In our example, this step exports the changes made above into a transform file called setup.mst that can be applied to the original MSI.
From the File menu select Close.
Use the following command to deploy the customised MSI:
msiexec.exe /i setup.msi TRANSFORMS=setup.mst /qb ALLUSERS=1
The command assumes that the MST, and the cabinet file, are located in the same directory as the MSI. In addition to installing the application, the command will copy the two scripts inside cabinet.cab to C:\Program Files\ProductABC, after which, they will be executed. The /qb arguments ensure the software is installed without the requirement for user interaction (q) and with only a basic (b) user interface. The ALLUSERS property of 1 forces Windows Installer to attempt a machine-based deployment (as opposed to a user-based deployment). You could also use ALLUSERS=2, which will result in the same outcome if the user initiating the installation has administrative rights on the local computer.
It is important to note that any vbscript (i.e. ConfigMachine.vbs and ConfigUser.vbs) executed during the installation process does not have access to the WScript object. As such, any code that relies on WScript will fail. For example, WScript.Echo could not be used in the script. Therefore, commands such as:
Set oNetwork = WScript.CreateObject("WScript.Network")
Should be replaced with commands such as:
Set oNetwork = CreateObject("WScript.Network")
Good luck.