Spinner Software Home About
KB Contact

Packaging and repackaging 201

Published: 23 March 2015 - Last update: 12 Dec 2015 (c) Nicolai Kjaer / Spinner Software B.V.

This document is meant for advanced tips and tricks for application scripting/packaging.

Chapters:

1. Property repair

2. Non-advertised shortcuts

3. Installing features by commandline

4. Removing empty directories

5. Custom Actions

6. User Interface

7. Prerequisites and Merge Modules

8. Analysing Logfiles

9. Finished

This is an example section.

Tips are shown in these sections.

1. Property repair

By default, property values that were changed via commandline are not remembered during a repair. This section explains how to fix it.

The basic idea is this: To remember the value of a property during installation, you must save it to somewhere (e.g. the registry) during installation and have Windows Installer look it up before repairing. Windows Installer uses the AppSearch table in combination with a locator table in order to find the value the property must be set to before repair.

To remember a property via the registry:

  • Create a Registry entry for HKLM\Software\MyVendor\MyApp\ named MyProperty, and have the value be [MYPROPERTY].
  • Create an AppSearch entry for MYPROPERTY with a signature of MYSignature1.
  • Create a RegLocator entry for MYSignature1. The Type must be 2. The registry data must point to the same registry key that was created in the first step (MyProperty).
  • Do NOT create a Signature table record - it needs to be absent for a registry key.

Windows Installer will now set the value of MYPROPERTY to the value stored in the registry before actually repairing.

Use CompLocator for component keypaths, IniLocator for ini entries, and DrLocator for folders.

Example:

  • The msi has MYPROPERTY defaulting to "A".
  • The installation script sets MYPROPERTY to "B" on the commandline.
  • During installation, HKLM\Software\MyVendor\MyApp\MyProperty is set to "B".
  • The user now repairs the application manually via Add/Remove Programs.
  • The default in the MSI is still "A", so the value of MYPROPERTY is "A".
  • Windows Installer sees the AppSearch entries, and reaches RegLocator to find a match.
  • RegLocator specifies it should load the value from the registry.
  • Windows Installer sets the value of MYPROPERTY to "B", as read from the registry.
  • Windows Installer finally repairs the application.

2. Non-advertised shortcuts

Older shortcuts usually points straight to an executable. This prevents the auto-repair functionality (self-heal) of Windows Installer, and should be fixed.

To fix this, we must find the target component and feature, and make sure it only contains the executable file, and that the component KeyPath points to it. Once this is done, you can change the shortcut to point to the target feature and component - and Windows Installer will repair if the executable is not found.

3. Installing features by commandline

You can use ADDLOCAL to specify which features to install via the commandline. This can be useful for some deployments where a large number of plugins are present, and you use the basic vendor installation with a different commandline depending on the target.

ADDLOCAL=feature1,feature2,etc.
Keep in mind that you may need to specify subfeatures, depending on their state / attributes. Always try and keep the vendor documentation handy if they support deployments like this.

Only use ADDLOCAL on commandline, never in a transform ! When Windows Installer removes a product, REMOVE will be "ALL" and ADDLOCAL should be empty. Otherwise those features specified in ADDLOCAL will never be removed.

4. Removing empty directories

It happens regularly that a vendor msi creates a folder (perhaps via the CreateFolder table), but never removes it. To fix this, we can add entries to the RemoveFile table to remove the folder if it was empty after the component was removed.

  • Create a new record in the RemoveFile table.
  • Use your own new identifier for FileKey (e.g. MyFileKey1).
  • Choose the component that is linked to the folder (we want to remove) in the CreateFolder table, if applicable. This also fixes the ICE Validation error, as they are now matched (created during installation, removed during uninstall).
  • Otherwise find another suitable component, for example the component that contains the main executable (assuming we remove the entire application always).
  • The FileName entry must be blank (null) for it to only remove empty directories.
  • The DirProperty is the name of the entry in the Directory table that points to the folder that we want to remove.
  • The InstallMode is 2 (OnRemove).
If the folder is empty after files are removed, it will now be deleted during uninstall.

Note that this will not work for removing files/folders from the users profile in normal cases of deployments (e.g. SCCM), as the user used to install or remove application is a system account and not the actual user.

5. Custom Actions

I wrote an example for VbScript some 10 years ago - see Custom Actions.

You can disable a custom action in the InstallExecuteSequence by putting the condition to 0 (false). This can be needed in order to fix certain installations, or to maintain guidelines. For example, Adobe sets the ARPREADME property to the locally installed readme file with a custom action during the installation ; if your guidelines specify that this entry should be left blank, you can disable that custom action.

5.1 Deferred vs immediate

During an installation or removal, Windows Installer essentially executes 2 phases:

  • 1. Build up a list of things to do, and resolve all values (properties, directories, etc).
  • 2. Execute the list of things to do.

Custom Actions that are marked as Immediate, are executed in phase 1.

Custom Actions that are marked as Deferred, are executed in phase 2.

Custom Actions marked as Deferred System Context, are executed within 
a limited part of phase 2, in a system context (not user).

This is also why there is such a strict limit to what you can do with values (properties etc) in phase 2. Everything was already resolved in the previous phase.

The way to pass data to a deferred custom action is to create a property with the exact same name as the custom action. The data in the property can then be referenced in the script using MsiGetProperty("CustomActionData").

6. User Interfaces

There are two parts to what the user sees during the installation:

  • 1. The dialog window, containing controls such as text fields and buttons
  • 2. The InstallUISequence table determining what happens when.

The Dialog table contains the definition of the various windows (also called Dialogs or Forms) that the user sees during the installation. It has entries to define width, height, title etc. You can consider this the canvas on which Controls are painted, as defined in the Controls table. What happens during the user interaction with the window is defined in the related ControlEvents and ControlCondition tables.

You would normally use an editor to change the layout and content of the window, instead of changing entries in these tables directly.

The tables are handy though - if you are not sure in which property a value is stored, you can check to see what happens when you would enter it during an installation with a user interface. Find the dialog that matches the window, then find the control where you enter the value. The Property field controls the value for the control. It could also have a control event set to [PROPERTY], with the argument being the new value.

InstallUISequence

This is somewhat identical to the InstallExecuteSequence, except it controls the user interface, and therefore mostly just spawns Dialogs.

You will sometimes see smaller vendors using a lot of custom actions in the InstallUISequence, mostly in order to provide feedback to the user (such as popup message boxes). Keep in mind that this will not happen during a deployment, as there will be either none, or a very limited user interface (e.g. progress bar only). Any UI Custom Actions that installs or configures items should be matched during an unattended installation.

7. Prerequisites and Merge Modules

You will often see an application either downloading or installing a prerequisite such as Microsoft Visual C++. You can handle this in many ways:

1. Deploy the prerequisite to all machines. Some prerequsites are required by so many different software packages that it will make sense to do so ; a good example is Visual C++ and the .NET frameworks.

2. Include the prerequisite as a separate installer, to be run before the installation itself. For example, you can specify this in SCCM.

3. Include the prerequisite as a Merge Module in your package. This is mostly useful for old requirements that only include one or two DLLs, such as Visual C.

When using Microsoft deployments (e.g. Office 365), it will download any missing components. Always check the installation log file for download tasks. You can also disable the network temporarily in order to ensure the installation fails instead of downloading any missing prerequisites.

8. Analysing Logfiles

To generate a logfile, add this to your normal msiexec commandline

/L*v "%TEMP%\My.log"

Since the exitcode from MsiExec is the main error code, it's nice to have it shown directly when running installation test scripts. You can do so by adding

@ECHO Result: %ERRORLEVEL%
straight after the MsiExec line in your script.

Example install.cmd test script with log and result:

@ECHO OFF
MsiExec /I My.msi /L*v %temp%\My.log /Qn
ECHO Result: %ERRORLEVEL%
PAUSE

A lot of the errors you will see along the way are standard, and you'll learn to fix them without looking up the actual error in the logfile. Some standard errors and their typical unofficial meaning:

1602 You cancelled the installation before it was finished.
1603 Generic fatal error, usually in a custom action script.
1605 You cannot remove something you haven't installed.
1618 Another installation is in progress.
1619 You used a wrong path\filename (not found).
1620 That's not an msi file.
1622 Logging path is invalid.
1624 Transform path is incorrect.
3010 (warning) Reboot is required to complete the installation. 
     If this should not be needed, change the REBOOT property to ReallySuppress.

After the installation or removal fails, open %TEMP%\My.log in Notepad. The logfile can generally be viewed as 3 sections:

Start/buildup
Execution until Failure
Rollback and exit
You need to find the Error: sentence that describes what went wrong, at the end of the execution phase. There will probbaly be a lot of FileCopy and RegAddValue etc until something goes wrong, and then it notes the error and starts the rollback (resulting in RemoveFile, RegRemoveValue, etc).

At the end of the log it will also state the resulting exitcode, which you can also search for (e.g. if the exitcode was 1710, search for 1710 and you should find the Error: 1710 after a few hits). Once you find the precise failure, you should have 1-2 lines of code documenting exactly what went wrong. Fix it and retest.

If you try and install Java 8 as a non-admin, the installation fails.

  • Open the log file.
  • Almost at the end of the logfile, is a single (long) line surround by empty lines. This is the main result information.
  • At the end of the long line, we see: Installation success or error status: 1603.
  • Unfortunately 1603 is generic, and we cannot find the error by searching for 1603.
  • Instead, we browse backwards (up) through the rollback actions (RegRemoveKey etc) until we find the error.
  • We spot the error line: Error 1721. There is a problem with this Windows Installer package. A program required for this install to complete could not be run.
  • We note that it's from Action: installexe, which tries to do something in C:\Program Files\
  • We remember we did not run it as Admin, and therefore do not have write access to that location.
  • We summarize that the installation perhaps needs to be run as Admin.
  • We confirm by retesting the installation using Run As Administrator that this fixes the problem.

9. Finished

For further reading, please check MSDN, Msi.chm, or your tool documentation.

I hope you enjoyed this guide !

(c) 2015 Nicolai Kjaer, Spinner Software B.V.