Wix Lyrical

Words about setup, build, development and localization

MSI Writing Guidelines

Basic Guidelines

Rule 0 – Never deploy modifiable resources

Separate the product’s set-up requirements into installation and configuration. Generally, installers should install things that remain static throughout the time the product is installed. Modifiable data should be generated by the application when missing (not just on first run).

Deployment and Operation [of an application] need to be separate code without code dependencies between them, because during deployment transactions you can’t depend on your code being in a useful state. [Blair]

Think about which resources are installer specific, major version specific and which should appear the same throughout versions. This includes file names, registry key names and shortcut and shortcut folder names.

Plan for and test upgrades before you release version 1.0 if at all possible.

Shipping someone else’s content to a shared location is a very dangerous thing to do. Other people doing the same thing will conflict with you.

Avoid custom actions, especially those that make changes to the system. They are very hard to get right. If you need a custom action that will write to the file system or the registry or some area already catered for by MSI, have the custom action write temporary rows to the appropriate tables instead of doing the work itself.

One product per MSI installer. Too many combinations will complicate it beyond what it can handle. Defining what is a product and what is a feature may be tricky.

Make sure repairs work. Patches and repairs are very similar.

Use the WiX documentation and the MSI documentation. The WiX-users mailing list, the WiX tutorial and the installshield.com forums and knowledgebase are also very useful.

Be careful what you put together in a component. There are several points to consider in these guidelines. Ideally, have 1 file per component for easier updating.

Use MSI 3.1v2 or above. It has bug fixes and patching support.

Choose between per-user and per-machine carefully. Per-user can sometimes be a hassle (except perhaps in MSI5+). Don’t give the user a choice in a dialog. It’s not worth it and the users don’t understand the full consequences. It’s difficult to write a single installer that can handle either case. Licensing may affect this decision, if your application architecture isn’t flexible.

Installation, Configuration And User Data

There are 3 kinds of resources: installation data, configuration data and user data such as a document file.

Only installation is performed by the installer. You should install the minimum to get the system up and running. Installation data includes program code and sometimes default settings without which the program will not run. These defaults could be called static configuration as these things should be immutable once installed.

Configuration should be performed by the installed program at first run and/or from an options dialog and when the data is missing. This is to prevent repair, upgrade, patch from overwriting the options. It makes it easier to change after first time installation too. Sometimes you’ll have to install a default and the program will allow it to be copied and the copy changed; sometimes the program will create it at first run or create it if it detects there’s no existing configuration. One method is to have defaults in HKLM/ProgramFilesFolder and the user’s choices in HKCU/[Local]AppDataFolder. The app looks to the default locations only if there isn’t a value in the user’s locations. Examples include details of servers used by the application, the location of the user’s data files, database connection strings and other user settings that are modified away from the defaults.

The application itself should support a silent mode configuration method for mass deployment; this could be by reading a file, transforming the installers defaults or some other method.

Configuration data can be subdivided into application or per-machine configuration data, which is similar to installed resources and per-user configuration data, which is similar to user data.

User data is created by the user with the aid of the program. An example would be a word processor document or a digital photo.

Trying to work out what is installation and what is configuration can be difficult, so ask yourself the questions below. All the questions must be considered and there will sometimes be conflicts so you must weigh each according to the product requirements. By default, prefer configuration as putting too much work in the installer is more difficult and less flexible.

  • Would you want to change this at any time after installation ?
    If so – configuration.
  • Would you want this to be restored to a default state, losing any changes if the application is repaired ?
    If so – installation.
  • Are there resources that the user wouldn’t want to lose during an upgrade, patch, uninstallation or repair ?
    If so – they are either user or configuration resources.
  • Is it user created, such as documents and saved option preferences ?
    If so – they are not installation. They may not be configuration either but user data.
  • Is this needed before the user can even configure the application ?
    If so – installation.
  • Do you create or remove system resources ? System resources are program code, registry entries, web sites, assemblies, COM+ applications, directories, etc.
    If so – probably installation. Some databases, text files and registry settings (for example) may be configuration.
  • Is the data never changed or only changed in a new version of the program ? Beware something that looks like it’s read-only but might become configurable in a later version of the product.
    If it never changes – installation.

You could put configuration into .Net install classes. This might be a valid use of them. Never use them for installation tasks. They are intended for xcopy deployment but this is a simplified deployment model for trivial .Net applications and shouldn’t be used in serious deployments.

Configuration data created by the application can be deleted at uninstallation with RemoveFile(s) and RemoveRegistry to ensure clean uninstallation.

Alternatively, do not remove configuration so that the user can reinstall and the configuration is already set as they prefer it. Offer a tool or uninstall option that will clean up the configuration.

Try and work out configuration requirements as early as possible as it has to go into the application and development time needs to be allocated.

A proper repair needs the cooperation of the application (like Word does) so it can reset the configuration data too.

User Data

User data is any resource that is not installed and is not required for the application to run. Examples include documents, favourites and some kinds of database data. User settings are treated as configuration because a default set of options are required for the user to be able to run the program and the settings have little meaning outside the context of the program.

User data is created by the user when he runs the program and should not be deleted by the installer at uninstallation time or during an upgrade. Usually it is left in place. If the user wants it removed, use RemoveFile and RemoveRegistry. If your installation is per-machine but includes per-user data, it’s best off being written by the application at start-up.

When upgrading to a later version, if the user data format must also be updated, this should be modified by the application which can recognise the old data format and upgrade it to the new one. This could be called at the end of the upgrade process (in a similar way to first-time configuration) or better, whenever old format data is loaded. An example would be old versions of Visual Studio projects. This implies that user data formats should incorporate some versioning scheme. The user should be asked if they want to convert.

Don’t install to a user profile location in an all-user installation. If a user, who didn’t install the program, runs it then the installer won’t have access to the user profile directory and will try to repair the installation. This would be OK in a per-user installation however.

Installation Scenarios

The basic installation scenarios are:

  • First-time installation
  • Removal
  • Repair
  • Upgrade
  • Change – feature addition/removal.
  • Patch application
  • Patch removal

You may want to detect these and perform particular actions specially. In a simple installer however, they are handled automatically.

The ProductState property is useful to detect per-user- or per-machine installation as it distinguishes between installations for this and for other users. It is less useful for major upgrades as it is tied to the product code being installed.

Useful wix code given here: Detecting deployment scenarios [DBLOCK].

The following shows how to detect various scenarios. Warning: These are not fully tested.

Scenario Condition used to detect Notes
First time installation NOT Installed This may be the new product being installed in a major upgrade.
Uninstallation, including during major upgrade REMOVE=”ALL” REMOVE is valid only after the InstallValidate action in the InstallExecuteSequence table.
Uninstallation but not during major upgrade REMOVE=”ALL” AND NOT UPGRADINGPRODUCTCODE UPGRADINGPRODUCTCODE  is set in the product being removed during RemoveExistingProducts
Repair REINSTALL=”ALL”
Modify Installed AND ????
Patch installation REINSTALL=”ALL” AND PATCH AND NOT MsiPatchRemovalList
Patch removal REINSTALL=”ALL” AND PATCH AND MsiPatchRemovalList
Major upgrade UPGRADINGPRODUCTCODE Set in the product being removed during RemoveExistingProducts
Feature is being installed !F < 3 and &F >=3 installation only
Feature is being installed &F >= 3 includes reinstallation
Feature is being reinstalled !F >= 3 and &F >= 3 includes repair and patch
Feature is being removed !F >=3 and &F < 3
Feature is being left alone &F = -1
Local component is being changed ?C=3 and ($C=2 or $C=4) 4 means run from source
Component reinstallation $C=!C
When a particular patch is applied PATCH AND PATCH >< MEDIASRCPROPNAME 

When run from Add/Remove programs:

Scenario UILevel Properties  
Change 5, full
Remove 3, no custom UI REMOVE=”ALL”Preselected=1
Repair 3, no custom UI REINSTALL=“ALL”REINSTALLMODE=ocmus

Preselected=1

Only installed features are repaired.

It is more likely you would want to detect and take actions based on feature or component install states and action states, so prefer those by default.

Install and action states

Action state is a misleading name. The action state does not refer to what action will be taken; rather it refers to the item’s final state. This is a subtle distinction.

If a feature is already installed and will not be installed, its action state will still be installed as that is its final state, not the action taken. (If action state meant action, not installing and already installed item would be a null action state).

Because of this, it is sometimes better to think in terms of transitions from one state to another. For example, installation is a transition from not installed to installed.

Feature States In Component Conditions

Features

Features should represent the users’ view of the installation; not a way of partitioning the installation like a program module. Users will add and remove features of the program that they want at installation and during maintenance.

Feature IDs are visible to the user as values of the ADDLOCAL and REMOVE properties in silent installations, so make them meaningful and not too long.

Features are sometimes chosen automatically i.e. not by the UI. In these cases, the Preselected property is set to 1. Don’t show a feature selection dialog in this instance.

You can enable and disable features by setting their install levels to zero or non-zero. Be very wary of disabling features by setting the install level to 0: if an installed feature is set to 0 at uninstall, it won’t be removed. Instead, prefer to set the features level higher than the INSTALLLEVEL.

Really you should avoid using INSTALLLEVEL to add or remove features from the installation. Use AddLocal/AddSource/Remove control events instead. INSTALLLEVEL is more for choosing minimal, typical, full installs. It probably doesn’t work simply with custom installations either.

Feature conditions

Features can have conditions. The concept of feature conditions is not for turning features on and off via user interface. The ADDLOCAL etc. If you want the UI to control feature selection, you need to use the AddLocal and/or Remove control events in your feature-selection dialog.

Feature conditions can be used to hide a feature that is inappropriate on a certain operating system, or to change the default selection state of a feature depending on some other software being installed (as detected with AppSearch) and the like.

Installed and action states ( &, $, ! and ? ) are available after CostFinalize.

Feature conditions are evaluated by the CostFinalize action. Feature states are available after this has finished. This occurs before the UI is shown. After CostFinalize, any changes in the conditional expression evaluation do not affect the install level of the Features. Any feature selection dialog would come after the feature conditions are evaluated and therefore before feature and component states are known and so cannot refer to installed and action states or properties set in the UI.

Feature conditions are evaluated only if no feature-selection properties are set. And, as the UI sequence converts SetInstallLevel control events, feature-selection control events, and SelectionTree control settings into the corresponding feature-selection properties, feature conditions that include properties set during the UI sequence won’t work as expected
[MSI TEAM].

Mutually exclusive features can be implemented with radio buttons on a custom dialog instead of the usual feature tree. In the execute sequence, just perform a check that mutually exclusive features aren’t set to be both installed by checking feature action and install states. Abort if they are. This covers silent installations where features are selected on the command line. Don’t forget features could have been Preselected in such scenarios.

For building feature selection UIs, If you wanted to explicitly list  features, the easiest way to do so is to use an AddLocal control event with an argument of ALL to install all features locally, then use individual Remove control events to remove the features that don’t apply. Doing so covers lets the user both install and uninstall features. For example:

<Control Id=”Next” Type=”PushButton” X=”235″ Y=”243″ Width=”57″ Height=”18″ Default=”yes” Text=”&Next >”>
<Publish Event=”AddLocal” Value=”ALL”>1</Publish>
<Publish Event=”Remove” Value=”FeatureX”>NOT FEATUREX_CHECKBOX</Publish>
<Publish Event=”Remove” Value=”FeatureY”>NOT FEATUREY_CHECKBOX</Publish>
<Publish Event=”Remove” Value=”FeatureZ”>NOT FEATUREZ_CHECKBOX</Publish>
</Control>

A difference between features and components is that a feature can only have 1 parent. A component can be contained in several features.

Components

Definition

The component is the atomic unit of installation. It consists of resources—files, registry keys, shortcuts or anything else—that should always be installed as a single unit. Installing a component should never influence other components: removing one should never damage another component or leave any orphaned resource on the target machine. As a consequence, components cannot share resources. The same resource installed to the same location must never be included in more than one component. [reworded from WIXFAQ]

A component has a GUID (the component code) that identifies a particular [key] file, directory or registry path. It may be installed from multiple features and multiple installers but can only get installed once per product. Therefore you cannot include the same component in an installer twice, not even in different directories. You can install the same component to different directories in different products, (runtime path). You can also write it so that it installs to different directories in the same or different products (authored path, which would make it a different component and require a new GUID).

Creating components

Obviously, before creating a component for a global resource, check to see if someone documented one first. This applies to third party products too. Find a third party installer and add it into a bootstrapper or, failing that, find a merge module that they publish.

While this is a guideline rather than a rule, you ought to store one resource (file, registry key etc.) in a component. Each OCX, HLP, EXE and DLL file should go in its own component and be the key path. Each file that is a target of a shortcut should go into its own component and must be the key path. You might put lots of related registry keys into the same one. Bear in mind that components are installed and removed as a unit, are patched as a unit and share the same key path for repair purposes.

In general, if the Resource is important and stands all by itself, I put it in its own Component.
[ROBMEN].

Mr Rob also suggests one file per component unless it must go together, such as a multifile assembly.

If you’re installing components that contain global resources, such as public or shared registry keys, COM components, GACed assemblies etc. then make sure that everyone in the world uses the same component code (and therefore the same component) to install that shared resource. If you don’t, they won’t be reference counted correctly and when you uninstall any product containing the resource, any other products using it will be broken because the resource has been removed.

Any registry key that points to a file should be included in that file’s component. Other registry keys should be logically grouped with the files that require them.

When adding registry values to a component, consider if they will be shared with future, incompatible versions of a component. If they will be, they should go in their own component; else the new component version will have to use a different registry key, which may not be desired. This is required to satisfy the component rule, “the component code of a new version of a component must not be changed when it would result in two components sharing resources, such as registry values, files, or shortcuts.” i.e. That rule would prevent us creating new, incompatible versions of a file when that file had its registry key (or even shortcut) installed with it. Of course, you can store registry values in the component, under a version subkey, if they need to exist in parallel to a different version’s registry data.

Component rules

Make sure you understand component codes and key paths, or rather, key resources.

A component is immutable once it has shipped.

Don’t change component GUIDs or key paths – this is likely to break installations that involve patches or where old and new installers interact. See Organizing Applications into Components (Windows). Adding or removing resources from a component violates the component rules. The only changes allowed to a component are to [change the content and] increase the version of versioned files and change the contents of non-versioned files.

If you need to add resources to or remove resources from components, then you must change the component GUID and change the location of the [resources within the] component, in order to prevent 2 different components referencing the same file.

To change a component’s composition, you have to create a major upgrade that completely removes the old product, and remove any other products containing the component, before installing the new product.

If you break backwards compatibility, create a new component. You can get away with not doing this only if the component is never installed twice on the same machine (including installations of different versions of the same product). Otherwise, make the new component by changing the resources names (e.g. file or registry path) in the component so that the same resource doesn’t occur in two different components.

If you change a file name in a component, you have to change the component ID because you’re changing the composition of the component.
[boB]

Changing the install location of a component is not a violation of the
Component Rules alone. Changing the directory of a Component is a special
case in the Component Rule handling.

You can install the same Component’s files to as many different directories as you want and the Windows Installer will track them all correctly. This is a deviation from all of the other Component Rules and is fundamentally necessary if you want to allow users to ever select where things get installed… There is just special logic to handle the case when the same Component gets installed to two different directories. The Windows Installer keeps extra information around such that uninstall removes the right thing.
[ROBMEN]

So you can change the location of a component without violating component rules, but it’s a bad idea as if you had something pointing at it (like a registry key), you can only point at one of them and would have to keep the key in sync if you changed to the other component. Beware of this if you use the product name as a directory name.

A component’s key path provides the version for the whole component:
The Windows Installer first determines whether the component’s key path is already installed before attempting to install any of the files of the component. If the installer finds a file with the same name as the component’s key path installed in the target location, it compares the version, date, and language of the two key paths and uses file versioning rules to determine whether to install the component provided by the package. If the installer determines it needs to replace the component, based upon the key path, then it uses the file versioning rules on each installed file to determine whether to replace the file.
[MSDN]

[You might make] a change in the name or target location of any file, registry key, shortcut, or other resource in the component. In this case, you must also change the name or target location of every resource in the component.

My simplified (!) component rules are:
– If you didn’t write it, check the author’s rules for installing it. If
they suggest including it in your own MSI, get the right component GUIDs and
composition of those components from whoever did (this might be a merge
module); you might instead need to launch a separate installer.
– If you can’t get the right component GUIDs (no merge module, Fragment, or
official installer), generate your own but make sure SharedDllRefCount is
set to ‘yes’.
– If installing a resource to a shared directory, you must ensure that the
component GUIDs and component contents are identical;
– Change the component GUID if you have to add or remove a file, registry
key or other resource, or if the new version of the file is no longer
compatible with the old version (because Windows Installer will keep the
newest file version); …
– … If you need to change the component GUID you must change the directory it
installs to, to ensure that you don’t end up with miscounted references (you
can ignore this rule if this is a private component and you’re uninstalling
the previous version before installing the new one).

It is OK to install the same component to different directories in different
packages – Windows Installer tracks where each package put each component –
but not to install the same resource to the same directory using different
components in different packages.

[MIKEDIMMICK]

Component conditions

Component conditions can be used to determine if a component will be installed or not. The component conditions cannot be used until after the CostFinalize action. After this stage, any changes in the conditional expression evaluation do not affect the result.

Component conditions are evaluated before feature and component states and therefore cannot refer to installed and action states. Component conditions are not re-evaluated in maintenance mode unless a component is marked transitive, which is something you should only do if absolutely necessary.

Installed and action states ( &, $, ! and ? ) are available after CostFinalize, so you can’t really install a component, using only a condition, based on feature states.

Component catalogue

If you feel it is worth the effort, you could keep a master list of shared components and their GUIDs, key path, location and contents. Store importable versions if possible. Use Fragment elements or wixlibs of code, merge modules or separate installers. If you use a merge module or snippet, be aware of the problems merge modules introduce, namely the inability to update the component without updating your whole installer.

Per-user and Per-machine

We prefer per-machine installations over per-user for several reasons. Per-user installs waste disk space (rarely important), are easier to tamper with, can only be seen and maintained by that user (unless managed by policy) and have to be installed once for each user. Per-user installs should be used for roaming users and managed rollouts.

I would suggest not giving users the choice in a dialog like InstallShield does – most users, can’t make an informed choice here and it doubles the number of cases you have to account for. If you do have such a dialog, you should provide more user guidance on what is recommended and what are the consequences.

A product is installed for the current user or all users, but never both. It would be weird when a user has two installations of the same product, with different features. It is actually possible to get this to happen but don’t. If necessary, prevent ALLUSERS being set in such a way as to cause this by detecting other installations.

The FindRelatedProducts action only works for the same value of ALLUSERS as the package it is running in. In other words, if the product is installed per-machine, it won’t be detected in a per-user install and vice versa. If you need to detect this, write a custom action using MsiEnumRelatedProducts() or MsiQueryProductState(). InstallShield uses its ISSetAllUsers custom action to work around this.

The ProductState property might be usable. I’m not sure why it isn’t widely used. Some experiments might be informative.

You should install per-user applications to a per-user location; otherwise you will run into problems when multiple users work on the same machine. A per-user installation is tracked in HKCU. If two people install the same application to the same place, such as Program Files, they have separate reference counts. There are reasons for this but if one person uninstalls, the other will lose the files.

In Windows 7 and onwards, ProgramFilesFolder in per-user applications is set to %LocalAppData%\Programs\. You should install to %LocalAppData%\Programs\<company name>\<program name>\x86 or %LocalAppData%\Programs\<company name>\<program name>\x64. In earlier versions of Windows, you should create this location and set suitable permissions on your own subdirectory and ignore ProgramFilesFolder.

Installation Context (Windows)

Set ALLUSERS if you have to in the property table. In Wix, this is the Package/@InstallScope attribute. Advanced users can override the value of ALLUSERS on the command line if they need to. Once installed, a per-machine and a per-user installer can’t interact, nor can upgrades. You must always maintain and upgrade and detect from other versions with the same context.

Don’t mix per-user and per-machine content in the same component. This is covered by some ICE validation checks.

The only safe way to get a per-user installation on any OS version is to set ALLUSERS to “” i.e. unset it. [This may not be true anymore. Check it.]

Note that to enable standard user patching on Vista, the installation must be per-machine.

Admin user installs and standard user installs

First, the official recommendation from the MSI team is that one should use multiple packages as this maps into the majority of the scenarios that were designed and tested for in MSI 4.0 development. We’ve found dual mode packages are difficult for ISVs to get right for all scenarios.
[RobertFlaming]

In MSI 5.0, you can create dual-purpose packages.

Patches And Upgrades

Versions

Use a version number of the form: Major.Minor.Build. The 4th field is ignored for determining if product versions match in MSI 3 and below although it can be used for revisions. However some API calls return 0 for this field so it’s simpler to avoid it.

Update types

Upgrades can be small, minor or major. Upgrades can be packaged either as a patch or as a full installer (or both).

The only difference between small updates and minor upgrades is if you must differentiate between product versions.

  • For small, leave the product version and code the same.
  • For minor, change the product version but keep the product code.
  • For major, change both product version and product code.
  • Always change the package code.
  • Always keep the upgrade code, unless you want to be able to install 2 products in parallel.

Updates impose some restrictions on what you can do. Violating these restrictions will cause strange effects like previously installed features appearing as advertised after the update, or components being advertised when removed in a minor update etc.

We recommend you use patches for small and minor updates. Ship major upgrades as full MSIs, not patches.

Major Upgrades

Major upgrades are applied as a normal installer and will cause the old version to be removed before it is installed. This is optional however, depending on how you write the upgrade table.

Upgrading works in the same way as the installation of a new package, except that the RemoveExistingProducts action runs the installExecute sequence of the old package during its own installation.

Use major upgrade for ease of development. You can get away with some component rule violations but you should consider this an extra level of safety, not an excuse to slack.

Don’t use major upgrade patches; they are deprecated. Major upgrade patches cannot be removed. You can’t create a sequenced major upgrade patch either. Major upgrades via patches in MSI 3.0 are not allowed. They don’t work with patch streamlining in MSI 3.0 either.

You keep the upgrade code the same if you want an upgrade, rather than a parallel installation of the product. If you have related versions that can be upgraded from such as a free and professional edition or a version 1 and 2 or an English and Japanese version, give them the same upgrade code. The upgrade code usually groups all versions and languages together into a family.

You must perform a major upgrade if:

  • Coexisting installations of both original and updated products on the same system must be possible.
  • The name of the .MSI file has been changed.
  • The component code of an existing component has changed. Check the component rules before doing this.
  • A component is removed from an existing feature.
  • An existing feature has been made into a child of an existing feature.
  • An existing child feature has been removed from its parent feature.

– From Changing the Product Code [MSDN].

If you want a custom action to run only during a major upgrade, in the removed MSI, use this condition: UPGRADINGPRODUCTCODE

If you want a custom action to run during an uninstallation of the product, but not during the removal of the product by a major upgrade, in the removed MSI, use this condition: REMOVE=”ALL” AND NOT UPGRADINGPRODUCTCODE.

UPGRADINGPRODUCTCODE is not available in the upgrading product, but you can test the property set by the upgrade table (WIX_UPGRADE_DETECTED in Wix). The property is created only when RemoveExistingProducts runs.

Preventing downgrades

You can also use the Upgrade table to prevent a lower version of a product from installing over a higher version. When you implement the technique described in this section, you can prevent the installation of the older version on any system that has a newer version of the product.

Always include an upgrade code in the product element. Then you can add an upgrade table entry to check for higher versions of the product and abort with an error message if detected.

<Property Id="DOWNGRADE" Secure="yes" />

<Upgrade Id="product family upgrade code here">
    <UpgradeVersion Property="DOWNGRADE" OnlyDetect="yes" 
    Minimum="installer product version here" IncludeMinimum="no" />
</Upgrade>

<!-- Requires LaunchConditions action to be later in the sequence than FindRelatedProducts action. -->
<Condition Message="A newer version of this product is already installed.">NOT DOWNGRADE</Condition>

Note that in the product version setting, the present version is set as the minimum version that is to be found and that this value is exclusive to the search criteria. N.B. There is a bug with MSI 2.0 and UpgradeCode/@minimum attribute.

The FindRelatedProducts action will find entries in the upgrade table and set properties if matching products are found (mixing per-user and all user installations can mess this up though). Use an error message type custom action (or a launch condition if you sequence FindRelatedProducts before LaunchConditions) which has the condition of the property being defined to abort the installation.

The MajorUpgrade element handles this for you.

An alternative, that returns success and leaves the current product in place, is to use WixExitEarlyWithSuccess.

Creating a major upgrade

  • Update the product name. Optional.
  • You may need to update the description or keywords or other strings in the product and package elements.
  • Change the product code.
  • Update the product version. We recommend using three fields only.

If not using MajorUpgrade, also perform these steps:

  • Update the minimum version in the downgrade detection in the upgrade table.
  • Update the maximum version in the UpgradeVersion that does the upgrade, so that the previous (most recently released) version will be upgraded by this version.
  • Ensure MigrateFeatureStates flag is set in the upgrade table.
  • Ensure MigrateFeatureStates is scheduled immediately after CostFinalize in both UI and execute sequences.
  • Schedule RemoveExistingProducts in the execute sequence only. (Added by Wix if MigrateFeatureStates is found).
  • Schedule FindRelatedProducts in both UI and execute sequences. Wix will do this automatically.

Packaging changes:

  • Update the Title in the package.
  • Update the MSI package reference if necessary.
  • Update the SFX file’s title, licence, path and description.
  • Add the product to any automatic updater.

Minor Upgrades

Although you can use minor upgrades to make an update that is also an installer, it’s rare that you’ll need to package this as an MSI. Use this type to make patches.

Small and minor upgrades are applied to an existing installation as though you were repairing it, only with the new package.

msiexec /i “<path to MSI>” REINSTALL=ALL REINSTALLMODE=vomus

Use REINSTALLMODE=vomus, not vamus, to avoid downgrading files and replacing modified data (configuration) files.

You can use a small or minor update as follows [MSDN]:

  • The update can enlarge or reduce the feature-component tree but it must not reorganize the existing hierarchy of features and components described by the Feature and FeatureComponents tables. If it removes a parent feature, it must also remove all the child features of the removed feature. It can add a new feature to the existing feature-component tree. You can synchronize the installation state of a new child feature with its parent feature by setting its Remote Installation property to “Favor Parent” and its Required property to yes.
  • The update can add a new component to a new or an existing feature.
  • Do not move components to other features.
  • The update must not change the component code of any component. Consequently, a small update or minor upgrade must never change the name of a component’s key path because this would require changing the component code.
  • The update must not change the name of the .msi file of the installation package.
  • The update can add, remove, or modify the files, registry keys, or shortcuts of components that are not shared by multiple features [including features in other installers]. If the update modifies a versioned file, that file’s version must be incremented in the File table. If the update removes resources, it should also update the RemoveFile and RemoveRegistry tables to remove any unused files, registry keys, or shortcuts that have already been installed. [I wouldn’t recommend this. You should abide by the rule that a component is immutable. Make new components for new resources.]
  • Otherwise, don’t delete files, shortcuts or registry keys until the next major upgrade you release.
  • The update of a component that is shared by multiple features must be backward compatible with all applications and features that use the component. The update can modify the resource of a shared component, such as files, registry entries, and shortcuts, as long as the changes are backward compatible. It is not recommended that the update add or remove files, registry entries, or shortcuts from a shared component.
  • Don’t remove any components from existing features. They will make the updated feature appear to be advertised and any updates on its other components will have no effect without issuing any error. MSI 4 removes components in patches if you set Component/@ UninstallWhenSuperseded.

Small Updates

These are similar to minor upgrades but the version number is not changed. Use these to make changes to a small number of existing files, e.g. for bug fixes.

This type is useful when you don’t want to change the version number, such as with a hot fix. That way, the next superseding patch in the release cycle doesn’t need to target the patched version as well. Otherwise they shouldn’t be used – the version number is important for identifying what’s installed.

For small and minor upgrades, you’re really just supposed to be installing some newer versions of some files, so adding or removing resources or moving them around isn’t a good idea. You can add new components, but if there are a significant number, you might be better off with a major upgrade.

Patches

A patch is a way of packaging an update. It consists of only the changes between the two versions. A patch is more complex to produce and cannot be used to install a product for the first time but is a smaller package.

Patches can be full file patches, where the whole of any modified file is distributed, or delta patches where only the differences between versions of each file are stored. It is recommended to always use full file patches to reduce prompts for source. Also there are some reports of problems with delta patching. It is also possible to produce a mixed patch by making a delta patch and then specifying the appropriate attribute on a file to make it a full file.

You might want to add MSIENFORCEUPGRADECOMPONENTRULES property, set to 1 too. This checks component rules and fails the installation if they are broken.

Patch sequencing and supersedence

Patch obsolescence was the way of superseding patches in MSI before v3.0. Patching using the newer supersedence method excludes using the obsolescence method. Use supersedence: it’s superior.

Supersedence uses patch families and patch sequence numbers to define an order in which patches should be applied to products.

When choosing family names, don’t use the same names in different products except for creating updates that are intended to apply to multiple products at once, because they contain some shared features.

If a minor upgrade does not supersede a small update, the small update is still applied even if it’s an earlier version.

How Updates Work

Components are repaired based on the key path only. If you want a component updated, the key path must change.

If the installer determines it needs to replace a component based upon the key path, then it uses the file versioning rules on each installed file in the component to determine whether to replace the file.

MSI sets the creation and modify dates to be identical when unversioned files are installed, the reason being that if these dates are later found to be different then the file’s been changed and will not be upgraded. “Newer” and “Older” aren’t factors in replacing files, just versions, file hashes and create/modify dates being different or not. The file hash acts like a version for non-versioned files: it acts as a way of telling if a file is changed or not.

Normally you don’t want to upgrade modifiable files but if you do:

  • Use companion file versioning for your unversioned file: If your unversioned file always goes with another file that does (like an assembly), it may be a companion file.
  • Use the RemoveFile table to always remove the unversioned file before you install it: This ensures that there won’t be a modified file there to prevent the copy. You should only do this in cases where you don’t expect to preserve user data since the consideration made by the Installer is that a modified unversioned file contains user-specific changes.

Registry key paths are not versioned so the REINSTALLMODE property determines if the components are reinstalled or not.

To remove a file in a minor upgrade

When removing components in a small or minor upgrade, you need to write RemoveINIValues, RemoveFile or RemoveRegistry elements which go in the upgrade MSI that you create the patch from. This complicates the removal and the recommendation is just to leave the files there until the next major upgrade or use the “puncture pattern” as follows:

What you do is keep the components in the MSI file and mark them transitive with a condition that evaluates to false. That preserves all the components in the MSI file as required, but evaluates them with a condition that means that they will be absent after the update.
[PHILWILSON]

Change the new versions to be 0 length files so they don’t take up space in the patch. Some people automate the creation of a 0-byte replacement file. This is called “puncturing” the file.

To create a double-clickable patch

Patches are double-clickable from MSI v3.0 onwards. Otherwise use the following to avoid having to set the REINSTALL* properties.

In the updated MSI, you create 2 custom actions to set the necessary properties (REINSTALLMODE and REINSTALL) before creating the patch. The actions can be removed after the patch is created if you need to have a full updated MSI as well.

<SetProperty Id=”REINSTALL” Value=”ALL” Before=”CostFinalize”>
Installed and PATCH and not (REMOVE~=”ALL”) and not REINSTALL
</SetProperty>

<SetProperty Id=”REINSTALLMODE” Value=”omus” Before=”CostFinalize”>
Installed and PATCH and not (REMOVE~=”ALL”)
</SetProperty>

You can put the features into REINSTALL instead of ALL if you know which features must be updated and which won’t be changed.

Preventing the need for source media

When you patch an installation that has one or more features that were installed to run from source, the patching of the components in these features can take place only if these features are changed so they are installed to run locally. For this to happen, the original installation source must be available so the files can be installed locally and then patched to upgrade them to the newer version. This scenario can be avoided by not allowing features to be installed as run-from-source or to create your patch package so that only whole-file patches are used.

Add a file hash for unversioned files that aren’t companion files. Wix and InstallShield do this for you. This helps determine if repair or patching is needed without recourse to the source disk.

Version all files that have the capability so that files can be compared by version to see if an upgrade is necessary.

Use whole file patches. A delta patch requires the original file to work out the new version.

Windows 7 caches the entire MSI to avoid invalidating any digital certificate but I haven’t checked to see if this is used for source resolution. You’d hope it would.

Custom uninstall actions

The important thing to remember is that superseded, obsoleted, or uninstalled patches are simply removed from the view. Any data provided in those patches — if not also provided in obsolescing or superseding patches — is gone and cannot be used. Custom actions defined by the removed patch are not run because they are also gone from the view.

In order to run custom actions during patch uninstall, you must define a patch uninstall custom action in the original product installation package or in a patch that is not removable and will always be installed.
[HeathStewart]

Commands to make a patch with Wix3

You can make a patch without the WXS or PDB files if you have the installers. This took a while to work out so I give the commands for reference. This uses two administrative installations.

torch -t patch -p -xo -ax “tmp” first\noarp.msi second\noarp.msi -out diff.wixmst

candle patch.wxs

light  -pedantic patch.wixobj -out patch.wixmsp

pyro -v patch.wixmsp -out patch.msp -t RTM diff.wixmst

Where tmp is a temporary directory, first and second are directories where admin installs of the MSIs were made, patch.wxs is your patch’s source code and RTM is the patch base line ID.

How to create a Wix patch that applies to 2 products

Assume you have 2 products with different product codes. In this example they are different language variants. The patches are being built with the admin installs method.

<!-- English -->

<PatchFamily ProductCode="12A345D6-7890-45AB-9327-C49189A8D13E" Id='SOME1' Version='1.0.3' Supersede='no' />

<!-- Spanish -->

<PatchFamily ProductCode="9F876E54-6792-4F89-A7CA-2F889FFE4DE7" Id='SOME2' Version='1.0.3' Supersede='no' />

The safest way requires that you prepare both admin images and call torch twice, once for the English old against the English new and the other time for the Spanish old and Spanish new. You then need to supply both wixmst files to pyro by supplying the -t argument twice [select baseline]. This means you will be using two different baselines (one for each wixmst file), both of which need to be represented under the Media element and on the command-line (one with each instance of the -t argument).

The biggest disadvantage with above is that the patch is roughly twice as big as with just one baseline, since you have basically the same transforms in there twice.

There is an alternative way of using just one baseline: pass “-val gstu” as an argument to torch to change the validation flags (and don’t use the “-t” argument [validation type]). However, any changes to the text in the admin image you use will be applied to both languages without regard to the language itself (meaning you will bleed those text changes across the translations, losing your localization).
[BLAIR]

Digital certificate expiry and UAC patching

To enable continuous use of UAC patching, before the digital certificates expire, you need to produce an update that adds the new certificate to the MsiDigitalCertificate table and sign the update with the old certificate.

When the old certificate expires, new updates must be signed with the new certificate:
You can’t sign with an expired certificate. See UAC patching.

The user must apply the update to update their products before the next update. If they use a later cumulative update, the update will be signed with the new certificate which is not in the installed products PatchCertificates table and they will get a UAC prompt.

Patches that were signed certificates that have since expired still qualify for UAC patching because the certificate is in the original installer. Only revoked certificates will be rejected.

If the certificate expires before a certificate update is produced, the user would have to apply one update with admin privileges to get back to doing UAC patching.

If this update (to the certificate tables) is non-cumulative and self-contained, and if the sequencing is correct, it can be applied just before or at the same time as any later patches signed with the later certificate to avoid a UAC prompt.

If you have a major update due a short while before the certificate expires, you can just create a major update with the new certificate. If you want to continue supporting the previous version with patches, you’ll still need to consider the above.

Properties

Properties are public or private.

Public properties must be all uppercase. They can be set on the msiexec command line. Only public properties can be passed from the command line or UI to execute sequence by marking them as secure custom properties. AppSearch properties must be public properties.

“Restricted public properties” are always passed to the execute sequence so these need not be included in the SecureCustomProperties property e.g. REINSTALLMODE, ALLUSERS.

Private properties include 1 or more lowercase characters. Private properties are only seen inside the sequence where they were set.

Property values are written to the MSI log unless they are marked as hidden. Mark properties used to store passwords as hidden.

A value of “” (an empty string) is the same as a property not existing or being undefined. Thus, the following are the same condition:

  • not PROPERTY
  • PROPERTY = “”

Directory IDs are represented as properties. You can’t modify a directory property in maintenance mode but you can during first time installation. You can install resources under a directory located with an AppSearch by including, for example, <Directory Id=”DIRPROPERTY”> and searching for the directory and assigning it to that property. Directory properties are resolved during CostFinalize so any references (or changes ?) to them must be made after that.

Properties provided to you are only available at certain parts of the installation. OS properties are always set; AppSearch defined properties are available after AppSearch [obviously]; TARGETDIR and others may be changed by the user in the UI sequence; others are set by various standard actions.

Some properties should always be set. These include the ARP* properties. Wix will set some of these for you but not all. ARP* properties define what appears in the add/remove programs control panel applet (ARP).

A windows bug means you must set ARPPRODUCTICON on Windows prior to XP:

<Component Id="bodgeARPPRODUCTICONonWin2k" Guid="some guid"
DiskId="1">
    <!—- Can’t write to HKLM if not privileged. -->
    <Condition>VersionNT &lt; 501 and Privileged</Condition>
    <Registry Id="Win2KARPProductIcon" Root="HKLM" Key=
  "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\[ProductCode]" 
  Action="write" KeyPath="yes" Type="string" Value="[#IconFileID]" 
  Name="DisplayIcon" />
    <File Id="IconFileID" Name="SDLIcon.ICO" Source="SDLIcon.ico" />
</Component>

Surprisingly, ARPINSTALLLOCATION is not set for you, although it will be saved if the property is set. You must set it and retrieve it as described here. See also the Persisting Properties section.

On first time installation, use a custom action to set the property to [INSTALLDIR] after CostFinalize. On subsequent runs, read it from the ARP registry entry into INSTALLDIR.

<Property Id="ARPINSTALLLOCATION">
      <RegistrySearch Id="GetINSTALLDIRFromRegistry" Root="HKLM" 
      Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\
      [ProductCode]" Name="InstallLocation" Type="raw" />
</Property>

<SetProperty Id="ARPINSTALLLOCATION" Value="[INSTALLDIR]" After="CostFinalize" Sequence="execute"> 
not Installed
</SetProperty>
<SetDirectory Id="INSTALLDIR" Value="[ARPINSTALLLOCATION]"> 
ARPINSTALLLOCATION 
</SetDirectory>

Persisting INSTALLDIR is the same as for a normal property except:

  • The X_PERSISTED property is named ARPINSTALLLOCATION
  • The registry location is predefined by MS.
  • The registry is set by assigning to the MS-defined ARPINSTALLLOCATION property in a CA after CostFinalize during first-time installation. You don’t use a component or registry value.
  • The SetDirectory should go in both sequences but never allow ARPINSTALLLOCATION to be changed in the UI sequence. The installation location cannot be changed after first-time installation.

Persisting properties

Property values are not stored between installs or between installation, maintenance and removal. Property values that cannot be derived, such as user input, typically need to be saved and restored, otherwise the default value in the property table will be used. Think of properties as local variables whose values are lost when the MSI is finished.

For example, a value entered by the user and stored in a custom property, which is written to the registry, must be reloaded in order to repair that registry value. Sometimes properties are needed during removal.

The usual approach is to save properties into the registry during installation and read them back out during maintenance using an registry app search.

Using the guidelines given elsewhere, decide whether the property is version independent, version dependent at some level (major, minor, build) or MSI dependent (usually same as build, sometimes minor version dependent).

Hard code the company name for the unlikely event you rebrand your company as something else. You can change installer properties without breaking code.

To persist a property:

  1. You may, but aren’t required to create your property X in the property table and possibly give it a default value. I would. It documents the property.
  2. Create another public, secure property called X_PERSISTED. Wrap it in a registry search that checks the registry value where you’ll store your property.
  3. Create a persisted property component in the root feature so it’s always installed. Put the registry value for the property you want to persist into it.
  4. Add a custom action that sets X to X_PERSISTED if X_PERSISTED is defined. Set the action to run after AppSearch in both sequences.

Notes

During first-time installation, this won’t find the registry key. Property may be set on command line or in UI (non-silent installation).

During first-time installation, properties are persisted to the registry.

Repair mode MUST use the same values that are persisted to the registry.

Uninstall mode MUST use the same values that are persisted to the registry.

The command line could not be used to override persisted properties in maintenance mode. To allow this you’d have to check to see if the property was already defined and not overwrite it.

The Change button from ARP uses the UI and will get the persisted values as defaults BUT won’t save new values ! Properties changed in maintenance mode won’t be persisted again because the components are already installed. To allow this you need to reinstall the component (which requires a feature reinstallation ? – if so, you need to put the component in its own hidden feature, which is a child of the root).

Keeping the values under a version-specific key means that upgrades that result in a different version number won’t read the values, if they need to.

Wix Example

To persist a property called TEST:

Make a property

<Property Id="TEST" Value="Default" Secure="yes" />

The property value must be secure so that the value can be read when it is saved to the registry.

Make the registry search

This property must be secure (and public) because AppSearch won’t be run in both sequences.

<Property Id="TEST_PERSISTED" Secure="yes">
    <RegistrySearch Id="GetTESTFromRegistry" Root="HKCU" 
    Key="Software\BigCompany\SuperProduct1\
    Installer" Name="TEST" Type="raw" />
</Property>

Set the root to HKLM, HKMU or HKCU as needed.

Add to a component

<Component Id="PersistTEST" Guid="*">
    <Registry KeyPath="yes" Action="write" Root="HKCU" 
    Key="Software\BigCompany\SuperProduct1\Installer" Name="TEST" Value="[TEST]" Type="string" />
</Component>

The root, key and value name should be the same as in the registry search. Type is always string as AppSearch can’t return other types without some decoding. This means you can’t store nulls. You could base-64 encode to get around this but that requires writing code custom actions.

Make a custom action and add it to the sequences

<SetProperty Id="TEST" Value="[TEST_PERSISTED]" After="AppSearch">TEST_PERSISTED</SetProperty>

Launch conditions

Add “Installed or” to most launch conditions to prevent them blocking maintenance operations. Using “Installed or” means that LaunchConditions will never stop the user from repairing, patching, or uninstalling the package once it is installed. During development, you don’t want to accidentally block removal because of a bug in a condition. In a few cases it might not be appropriate to use “Installed or”, e.g. for testing if the user is admin; you’ll still need to be admin to uninstall.

Use launch conditions to check for the existence of prerequisites that are needed to run the installation. Don’t use them to check for the prerequisites needed to run the application – it makes more sense for those to be done by the application. Otherwise, someone could remove a prerequisite after installation and the application would fail.

If you put validation to restrict the values of properties in your UI sequence, repeat those checks as installation conditions in the UI execute sequence so that silent installs can’t bypass the checks, e.g.:

UI dialog disables next button until PORT is in range 1-65535.

Execute sequence should have a launch condition (or error custom action) of “PORT>=1 AND PORT<=65535”, Message: “Port must be in range 1-65535.”

As usual, PORT would need to be persisted to allow uninstallation.

If you are unsure how a launch condition will behave in the future, add an override property, such as:

SKIPCHECKS or <some checking condition>

COM

Being a system-wide resource, COM requires careful handling:

  • Use registry-free COM to reduce the scope to the application and avoid sharing problems. If multiple instances of the same COM component may run simultaneously, check the code to ensure that it will function correctly.
  • COM objects with the same class or interface GUID (i.e. same identity) must always be installed to the same place on disk, namely your own directory under [CommonFilesFolder].
  • Create an application-specific build with new GUIDs for each component (in the same way that you would create a new component that breaks backwards compatibility). If multiple instances of the same COM component may run simultaneously, check the code to ensure that it will function correctly.

COM objects are (usually) visible globally on the system and it is much more important to follow the component rules. Follow all the guidelines for installing shared resources.

XP SP2 and above allow registry-free COM that simplifies matters greatly. SP1 and Windows 2000 had some problems. If your COM component(s) is not shared among different applications, don’t register anything and make your application use side-by-side assemblies. Isolated Applications and Side-by-side Assemblies

Keep your COM registration in the same component as the COM DLL or COM-callable assembly. This prevents separate installation, removal and makes repairs simpler – you can delete the file to rewrite the registration.

Set SharedDllRefCount on your component if its DLL key path could be installed or removed by a non-MSI installer. If you do not, it may be removed or fail to be removed at the wrong time by one of the installers.

COM DLLs that have a SharedDllRefCount <> 0 after removal will be left behindto avoid breaking other applications. It never hurts to set it, regardless so apply it to any DLL that might have been put in a past product that didn’t obey component rules or didn’t use an MSI for installation.

Ignore ICE33.
ICE33 is very contentious – please note that WiX suppresses this ICE by default when running validation in WiX 3.0. The WiX team suggests using the Registry table for all COM registration to avoid unwanted advertised COM repair and demand installation behavior. You won’t see too many examples of developers complaining about this issue because it really only affects very large programs like Office because they use the COM advertising behavior to load spell checkers and additional feature on-demand. Smaller applications usually install everything locally, so the impact is lessened.

Advertised COM registration was created for Office. However, the feature is no longer considered robust by Office setup given many years of user complaints. As a former Office setup developer, I can tell you that we made every attempt to eliminate the Typelib table from all Office installations (due to its myriad problems) and additionally asked developers to stop using the other COM tables.
[ROBMEN].

Wix generates Registry table entries if you use <TypeLib> <ProgID> etc.

Never use regsvr32. Use Tallow with -s or Heat to extract registration information. Regsvr32 bypasses the registration reference counts used by MSI and can result in broken COM registration.

Never use regasm, for the same reasons. Use Tallow with -c on your .net DLLs that need to be called by COM or use Heat.

When creating registry entries for COM components by capturing with tallow or heat, replace any hard-coded paths with file ID references, using the [#xxxx] syntax, so that you register the component in whichever path it is installed to. Help directories are replaced with the directory ID syntax (that takes the form [$xxx]).

Heat can cause duplicate registry element keys though when used with .net com DLLs if you generate higher level Wix (with typelibs and progIDs and so on) so switch it to generate registry entries. If necessary, add IDs to duplicate registry entries. This problem may not occur in newer versions of Wix.

Update: TypeLib and ProgId elements now generate registry entries. You should retain these elements: Wix compiles them into registry table rows.

Registering .Net assemblies for COM

Regasm…

1. Registers the class entries and sets the InprocServer32 to mscoree.dll.

2. Registers a type library.

Regasm /regfile only performs (1).

Regasm /tlb only performs (2). Don’t use this option.

Create the type lib with tlbexp then register it with <TypeLib> elements. Run Regasm /regfile and convert that into registry entries.

COM+

If you can guarantee your COM DLL will never be used outside COM+ then put them together. Otherwise make COM+ applications their own component separate from the COM DLL(s) they run as, so the COM DLL can be used outside of COM+.

Don’t use regsvcs. That is only a developer tool. Use <ComPlusApplication> etc. You can create a TLB and view the settings you need with regsvcs.

Without the TLB it seems to fail in weird ways in WiX 2. Install the TLB with a native (as opposed to .Net) COM+ DLL. This may have been fixed since I used it. .Net needs the TLB installing anyway, I think.

Use tlbexp to make the typelib for .Net assemblies. Don’t use regasm with /tlb. That’s both creates and registers the typelib anyway. [I’m not sure why I came to this conclusion – might be worth rechecking]. To make a typelib for a native assembly, drop the DLL into visual Studio and select the typelib under the typelib branch then right click export and rename to .TLB.

To get the information needed for the (.Net) COM+ elements, either install the components with regsvcs and view the result in the component services control panel or use OLEView’s Bind To Typelib menu item and grab the UUIDs for the coclasses.

If your COM+ isn’t installing, ensure you’ve got all dependencies installed too. Use Dependency Walker and something like dotNetDependency viewer. You can also run an exe or host a DLL in dependency walker to include run time loading of DLLs.

There’s no programmatic access to COM+ for creating COM+ proxies so there cannot be a Wix CA for it. Fredrick Grohn suggests using the COM+ proxy as a prerequisite. The proxies seem to be operating system sensitive (between 2000 and XP and maybe 2003 server at the least).

Also try (with .net assemblies) installing to GAC and not to the GAC to see if you get better error messages. Or try running regsvcs to see what the error might be.

Register a native COM+ DLL, to see if it registers successfully, and then try importing it into a created application.

I think the typelib registry entries will be messed up if you use regsvcs to register your COM+.

If you can’t work out a dependency, install the COM+ app without the implementing assembly and then manually try to register the implementing DLL after installation and run it.

In Wix 2 [haven’t tried 3], you seem to need to specify the application ID for installing .Net assemblies in the GAC.

.Net Assemblies

The definitive rules are in the MSI SDK under Updating Assemblies.

Installing to the GAC changes the version rules. The assembly version is used when updating and the file version is not so important. Outside the GAC, the reverse is true.

Updating private assemblies tests the file version, as normal: the file version must be updated when you make a change, regardless of if the assembly version stays the same, otherwise patching won’t install the file. This would happen when you fix a bug.

When you install to the GAC:

  • If you only change the file version, the file is updated in-place. All applications will use the new one. Use this for backwardly compatible updates. However, this is not recommended. You should install a new version and use a policy to redirect to the new version.
  • If you change the assembly version (set the file version to the same), but use the same component, the application will install the new version but other products will use the old one. The old one will be removed if no other products are keeping references to it.
  • If you change the assembly version (set the file version to the same), but use a different component, the application will install old and new versions.

At least this is what the docs say. The Phil Wilson book suggests that things may not work like this. Additionally, things have changed since this was written.

Updating assemblies

In place updates

If the assembly file version is updated and the assembly version is unchanged, you can major upgrade the files.

If we want to patch the DLLs in place we need to make a change to the Wix linker settings to allow it to work: use the –fv switch.

Side By side updates

If you do a major upgrade, this will work fine; the old assembly is removed and new one installed.

If you patch, the old assembly is left behind and the new one installed. This may or may not cause problems, depending on how applications bind to files in the GAC.

How to write the source code

When .Net calls COM: Use interop. The COM DLL needs registering or a manifest writing if using side by side.

When COM calls .Net: Use heat or tallow instead of regasm. You could use regasm to generate a .REG file for heat or tallow to process. Regasm doesn’t perform typelib registration unless /tlb is specified.

If Windows calls your assembly (such as from mmc.exe) you’ll need to install to the GAC so Windows can find them.

Don’t use gacutil. It’s not redistributable. Use File/@Assembly=”.net” and omit File/@AssemblyManifest attribute.

Assembly attribute:

  • Missing or “no” – regular file.
  • “win32” – side-by-side win32 assembly.
  • “.net” – .Net assembly:
    • AssemblyManifest attribute is set to Id of the manifest file (normally assembly itself. This is the default and can be omitted).
    • AssemblyApplication attribute:
      • Missing or blank – assembly is installed in GAC.
      • Id of some file (which could be of the assembly itself) – assembly installed in the same directory as referenced file.

Fusion will reference count assemblies installed to the GAC independently of MSI. Even if the same assembly is installed with different component codes, it will only let it be deleted when the last reference is gone. You should still try to obey component rules to ensure repairs and upgrades work but if 3rd parties also install the same assembly you have some protection.

Installing As A Standard User

Note that some kinds of GPO deployment are similar to running as a standard user.

  • The UI client runs as Standard User
  • The execute script generation runs as Standard User
  • For the execute script:
    • Standard Actions run as with the needed permissions
    • Custom Actions run as Standard User unless the NoImpersonate bit is set.

Don’t modify the system in the UISequence, as always, and don’t query the system in a way that requires elevation there, either. There are some IIS features that do require elevation to query it. The only alternative there is to request elevation at the start.

Don’t modify the system in the execute sequence in the script generation phase; only do it in the script execution phase.

MSIUSERREALADMINDETECTION property to return the historical behaviour of AdminUser on Vista. The MSIUSERREALADMINDETECTION property is expected to be set when a package has designed different UI behaviour if the user is an admin. Unless you have a specific reason to need to test that the user is an administrator only, you should ignore this and use the Privileged property.

Don’t check for Privileged (or AdminUser) in the launch conditions of the UISequence, check with a type 19 Error CA in the execute sequence only because Windows wont elevate you with a credential prompt until then.

If you include the word, “setup” in the file name, your bootstrapper will be elevated by Windows installer detection. This is for legacy compatibility and should not be relied upon.

All MSIs are elevated by default on Vista unless the flag is set in the MSI that says it’s not necessary. [What about Win7 onwards ?]

In general, Windows Installer will prompt for elevation every time a user installs an MSI unless the MSI author specifically opts out.  There are a few key exceptions to this statement:

  • If the MSI is run in silent mode (msiexec.exe /qn).
  • If the product has already been installed and is managed by Group Policy.
  • If the user launched setup from a process that is already elevated (such as an elevated command prompt).
  • If the MSI package is already approved for elevation (such as via Group Policy deployment).
  • If the AlwaysInstallElevated policy is set on the system.

[ASTEBNER]

The UISequence only runs elevated if the MSI is called from an elevated process, such as an administrative command prompt. This means that launch conditions and app searches that need admin access won’t work without elevating first.

For multiple MSIs in one chainer, not installed by Administrator, you’ll get multiple elevation dialogs. To avoid it, use a chainer that elevates itself to install all packages. Burn automatically detects when it needs to elevate its engine by detecting if a package is per-user or per-machine. The author can specify ForcePerMachine to provide a hint too.

If you’re going to have an application that needs administrator’s permission to run (not to install), don’t put it in a per-user location. An application that is going to be elevated should install to a location that cannot be tampered with by Standard Users.

Make sure CAs modifying the system run without impersonation. Usually your CA must run impersonated (the default) so that per-user resources (like HKCU and special folder CSIDLs) are resolved correctly.

If you run as system, ensure per-user resources are always accessed in the right way. If you can avoid, per user data in the installer and make the application handle it, as it should. If one were following this guidance, there should be fewer (and possibly no) instances of the deferred, NoImpersonate custom action writing the wrong users profile. Otherwise use WTSQueryUserToken() to try and get the identity of the person that launched the program so that you can write to their profile. This technique requires code to then use the token to impersonate a user. This technique is not guaranteed to work in 100% of the cases.

Even if Privileged is true, if the user is running a blessed package, his impersonated CAs will still run with that user’s normal privileges. If the impersonated CAs need admin rights, you’ll need to test if the user really is an administrator. Non-impersonated CAs may suffice if user specific locations aren’t being accessed.

It is difficult to write per-user state from a deferred, NoImpersonate CA because the user is local system. Likewise a per-machine install is running as an administrator, not the launching user and so cant access HKCU either.

MS recommends writing separate packages for per-machine and per-user installations, except in later versions of Windows Installer.

Some authors suggest it’s easiest to write a package that will work with “machine assignment” as that requires least effort.

GPO may need advertised shortcuts.

For GPO testing, see the Testing and debugging section.

See zap files.

Add PatchCertificate elements to client software to enable standard users on Vista onwards to install patches without elevating.

Registry Access as a Standard User

Registry access by a standard user is restricted in a similar fashion as the file system. A standard user is permitted read access to all keys in the registry; however, write access is only granted to the HKEY_CURRENT_USER (HKCU) subtree, which is mapped internally to the user-specific subkey HKEY_USERS\Security ID (SID) of current user. An application that needs to write to any registry location other than HKEY_CURRENT_USER requires administrative privileges.

If the 32-bit application’s manifest does not contain a requested execution level, then all writes to HKEY_LOCAL_MACHINE\Software will be virtualized, while writes to other locations outside of HKEY_CURRENT_USER will fail.

Storing persistent data in the registry, like a user’s configuration, is not recommended. The preferred method of storing persistent data from your application is to write the data to the file system in a location derived by calling SHGetFolderPath (pre-Vista) or SHGetKnownFolderPath (Vista and onwards) and to get the path corresponding to a CSIDL constant.

Detecting pending reboot.

For Vista/MSI 4+ we could add a launch condition of:

<Condition Message=”You must reboot to complete installation of Windows Updates before installing this product.”>
    not MsiSystemRebootPending 
</Condition>

This is backwardly compatible with earlier OSes and MSI < v4.

The Burn equivalent is:

<bal:Condition Message=”You must reboot to complete installation of Windows Updates before installing this product.”>
<![CDATA[ RebootPending <> 0 ]]>
</bal:Condition>

Guidelines

Don’t make major upgrades as patches. They are deprecated. Once a patch containing sequencing information has been applied to a product, major upgrade patches can no longer be applied to that product.

Avoid custom actions wherever possible. If they manipulate resources that Windows Installer already handles, such as files and registry keys, write “semi-custom actions” that change the appropriate tables and let Windows handle the rest: it simplifies development and improves reliability. Write any other custom actions to be data driven and define a table to hold the data.

Add a key path to all components; if you don’t you’ll get ICE18 errors. Make it on the component element if you want the key path to be the parent directory itself but this should be a last resort:

Unless your Component only creates a Directory then I would always recommend using something else as the KeyPath.
[ROBMEN].

Always validate and check your validation errors and warnings. Fix what you need to fix. Document what can be ignored and why.

Treat registry values that you install as being immutable, changeable only by the next version of your installer. I believe there’s a risk that, if installed registry values are edited, the changes could be overwritten by repairs or upgrades. Stick to the bare minimum required to allow the components to be located. Do the rest in initial program start-up.

Use public properties for configuration information. Administrators can provide this information on the command-line for silent installations. This should be configuration that doesn’t change throughout the installation of the product: what I like to call “static configuration”, such as a user-chosen name for an generated account to run a service.

If you use the product name for the installation directory, don’t put minor version number information in it. Otherwise patching “Product 1.0” to v1.1 will leave v1.1 in that directory. With major upgrades you can change the directory name. If your minor versions can be installed in parallel, then the above applies to the build version instead.

Put RemoveExistingProducts after InstallValidate if possible: putting it after InstallFinalize prevents rollback and also any removal CAs in the old product would be operating on the newly installed one. This is the default for the MajorUpgrade element.

One file per component is a very good idea. It makes updates and modifications much easier. Exceptions include multi-file assemblies and manifest files that go with DLLs.

Do not automatically restart the user’s computer during a silent installation. [MSDN]

Try to use versioned resources as key paths so that unnecessary replacements can be avoided during upgrades. For example, use a versioned file rather than a registry key, all other considerations being equal.

Don’t use .REG files or scripted custom actions. Some anti-virus programs prevent .REG files and VBScript from being launched by another process.

“Concurrent Installations, also called Nested Installations, is a deprecated feature of the Windows Installer.” Wix won’t even support them. Avoid them. Use a bootstrapper.

Split large wixlibs into fragments. Light will only link in the fragments that are referred to.

Don’t mark components shared between products as transitive because reference counts are ignored if the condition evaluates to false and the component is removed. See Wix help.

Testing And Debugging

Test each of the installation scenarios (see above).

You can divide your testing process into the following parts. [MSDN]

  • Installation Testing – Test the installation with all possible combinations of application features. Test all types of installation, including Administrative Installation, Rollback Installation, and Installation-On-Demand. Try all possible methods of installation, including clicking on the MSI file, command line options, and installing from the control panel. Test that the package can be installed by users in all possible privilege contexts. Try installing the package after it has been deployed by all possible methods. Enable Windows Installer Logging for each test and resolve all errors found in the installer log and event log. The command for an admin install is msiexec /a file.msi TARGETDIR=”absolute path to install dir”. The directory must exist for some versions of the OS.
  • User Interface Testing – Test the package when installed with all possible user interface levels. Test the package installed with no user interface and with all information provided through the user interface. Ensure the Accessibility of the user interface and that the user interface functions as expected for different screen resolutions and font sizes.
  • Servicing and Repair Testing – Test that the package can handle Patching and Upgrades delivered by a Small Update, Minor Upgrade, and Major Upgrades. Before deploying the package, write a trial update of each type and try applying it to the original package.
  • Uninstall Testing – Verify that when the package is removed it leaves no useless parts of itself behind on the user’s computer and that only information belonging to the package has been removed. Reboot the test computer after uninstalling the package and verify the integrity of common system tools and other standard applications. Test that the package can be removed by users in all possible privilege contexts. Test all methods to remove the package, click the MSI file, try the command line options, and try removing the package from the control panel. Enable Windows Installer Logging for each test and resolve all errors found in the installer log and event log.
  • Product Functionality Testing – Ensure that the application functions as expected after the installation, repair, or removal of the package.

Can your application be repaired back to a working state ? This will be its initial state. If the application wasn’t designed properly, this might reset some configuration data. The application itself should reset configuration data.

To Log Removals Run From ARP

To debug installers where you aren’t using the command line, set:

HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\Installer

Reg_SZ: Logging

Value: voicewarmup

See MS KB223300

Using the MsiBreak Environment Variable

This is the “official” method suggested by Microsoft: To enable the debugging of a custom action, set the environment variable MsiBreak to the name of the designated action, which is case-sensitive, the same as it appears in the CustomAction and sequence tables. Debugging custom actions that are executing in the service requires the debugger to be attached to the service process (MsiExec.exe) ahead of time. For more details see topic “Debugging Custom Actions” in the Windows Installer SDK Help (msi.chm). Be sure to register the MsiBreak environment-variable as a SYSTEM-environment-variable. On pre-2.0 Windows Installer it doesn’t work when it is defined as a USER-variable. A reboot may be necessary to make sure all programs use the changed system environment.

Call DebugBreak()

Note that this won’t work in deferred custom action code.

MessageBox()

If you need the program to halt while you do something, such as read the log or start the debugger, or if you need to display a value, MessageBox() is perfectly acceptable for debugging.

Checking for .Net DLL loading failures

If a DLL is missing you may get a crash. Try this to get a log of DLL loading failures. I’ve noted that some loading errors don’t actually get reported unless you turn on “log successes” as well.

Testing for GPO installation

Corporate deployment systems (GPO, SMS etc.) may invoke the package from a client running as Local System on the client node.

Per-machine advertisement and subsequent installation under a standard user account best simulates the process of application distribution used in MS SMS and Active Directory™. This is the easiest and most comprehensive test for installations designed for the locked environment systems.

In order to advertise installation per-machine we have to log in as administrator and run the following command line:

msiexec.exe /jm my_package_path.msi ALLUSERS=1 /qb

As a result, no component will actually be installed. MSI will just install the advertised shortcuts and icons, file types and extensions, COM class registration, and package installation information.

After advertising the package we need to log in under an ordinary user, activate the installation either by running an advertised shortcut or by loading an application file type. The only feature containing the advertised item will be installed on demand. The setup can be considered successful if the application starts immediately following the installation and runs without any problems.
Packaging Applications for Locked-Down Environments

Errors

To find an error in the log, you can usually find it by searching for “value 3”

“Failed to generate hash for file” errors in the MSI log: This is normal and harmless for companion files. They use their parents’ versions to determine their version, not a file hash.

Reasons for autorepair

If Windows runs repairs unexpectedly, check the event log for entries with ID 1001 and 1004. These will name the GUID of the component being repaired and the source that triggered it. Sources of autorepair include: using an advertised shortcut, causing a ShellExecute() via file extension that you registered and the application creating an instance of an advertised COM component.

There is a bug on server 2003 that can cause an auto-repair http://support.microsoft.com/?kbid=915030 .

Note on SMS

SMS can push support files like bootstrappers out with the MSI to the distribution points and invoke them as part of their package definition. In fact, SMS doesn’t really even require MSI’s although MSIs generally behave nicer. GPO can only invoke MSI’s.

When using SMS, always distribute the MSI outside of the EXE (fully exposed) as this is the only way the SMS Advanced Client can be aware of the package’s ProductCode and have access to the MSI for assisting in source resolution. It should also be noticed that SMS (at least the last time I used it) only correlates the distribution point to the MSI source if the DP is considered ‘local’. It does not do this for remote DP’s. I wrote about this back in 2004

http://blog.deploymentengineering.com/2004/12/msi-patching-with-sms.html

I rarely used setup chainers because SMS has its own chaining tool. You end up consuming less space on your distribution points, pull less bits (pun intended for people who know what BITS is) and you get better SMS status messages from the client when you let SMS own the chain.

Another reason for me insisting on extracting the MSI is for source resiliency.  SMS can understand a relationship between an installed application (ProductCode) and an MSI package sitting on a distribution point and automatically make it available to MSI if the DP is considered `local` to the subnet.  I’ve also written utilities that query the SMS database and add source points that are also considered ‘remote’ to the subnet.
[ChrisPainter]

User Interface

Troubleshooting

If your publish event isn’t firing, remember the inner text condition is mandatory and should be set to 1, if it should always run.

Only edit controls, not text, can be linked to a property (in fact only active, not static controls can be)

MSI dialogs do not refresh after the value of a property is changed programmatically. For example, if you run a custom action which updates a property which is displayed in a control on the calling dialog. The property is not updated when the action returns. This is a limitation of the MSI. All kinds of solutions don’t work. Crap as it is, the best solution is to silently execute a newDialog to an identical dialog. This would go back to the previous one if it needed to run the CA again. i.e. DialogA goes to DialogB to refresh and DialogB goes to DialogA to refresh.

Dialog Template

This is based on the dialogs in the Wix 2 UI libraries.

<Dialog Id="@@@Dlg" Width="370" Height="270" Title="$(loc.@@@Dlg_Title)">
    <Control Id="Title" Type="Text" X="15" Y="6" Width="200" Height="15" 
    Transparent="yes" NoPrefix="yes" Text="$(loc.@@@DlgTitle)" />

    <Control Id="Description" Type="Text" X="25" Y="23" Width="280" 
    Height="15" Transparent="yes" NoPrefix="yes" 
    Text="$(loc.@@@DlgDescription)" />

    <Control Id="BannerBitmap" Type="Bitmap" X="0" Y="0" Width="370" 
    Height="44" TabSkip="no" Text="$(loc.@@@DlgBannerBitmap)" />

    <Control Id="BannerLine" Type="Line" X="0" Y="44" Width="370" 
    Height="0" />

    <Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" 
    Height="0" />

    ....

    <Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" 
    Height="17" Default="yes" Text="$(loc.WixUINext)">
      <Publish Event="NewDialog" Value="[WixUI_@@@Dlg_Next]">1</Publish>
    </Control>

    <Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" 
    Height="17" Text="$(loc.WixUIBack)">
      <Publish Event="NewDialog" Value="[WixUI_@@@Dlg_Back]">1</Publish>
    </Control>

    <Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" 
    Height="17" Cancel="yes" Text="$(loc.WixUICancel)">
      <Publish Event="SpawnDialog" Value="CancelDlg">1</Publish>
    </Control>    
</Dialog>

Shortcuts

In theory, shortcuts are easy but this is MSI. The two types of shortcut, the various locations for the menus depending on whether its per-user or per-machine and accounting for unknown roaming user cases make the whole thing tricky. What follows is a distillation of the various practices.

Shortcuts are either “advertised” (MSI) shortcuts or non-advertised (LNK files). There are also URL shortcuts, created by a Wix custom action.

Advertised

Advertised shortcuts advertise a feature. This allows installation on demand. A better name for advertised shortcuts would be “Windows Installer shortcuts”. Advertised shortcuts are always present on the system but the feature containing the resource they advertise may not be. Advertised shortcuts are entry points into the MSI process that may run autorepair and install on demand before launching your application. Autorepair and install on demand are applied to the whole feature that the shortcut is part of. Autorepair is used to install the application for users other than the installing user, where necessary, i.e. for resources installed in user-specific locations.

Advertised shortcuts installed for a particular user using group policy will appear on whichever machine a user logs in to and will trigger installation if the application is not present. If you license per machine you may have to consider this in the agreement.

Advertised shortcuts are required for installers that will be advertised with GPO using user assignment. The shortcuts are installed when the advertise action is run to install the package. A non-advertised shortcut cannot launch the advertised application. If using machine assignment however, advertising is not required.

Advertised shortcuts cannot be placed in their own component. The shortcut target must be the component’s key path and the target must contain an icon. If this can’t be done, use a non-advertised shortcut.

This kind of shortcut is specific to a product code. You cannot share shortcuts, and hence their components, between MSIs.

Advertised shortcuts trigger MSI repair on first use of the application. If you installed as one user and run as another, you’ll need access to the original MSI.

Advertised shortcuts are sometimes preferred for per-user setups as they don’t suffer the same per-user complications of normal shortcuts.

Problems with advertised shortcuts

The target file is the component’s key path and this can cause problems since you may want to create different versions of advertised shortcuts without installing the file multiple times.

The target file is run via its default action. If you want a different action, use non-advertised shortcuts.

The created shortcut is not editable. The user cannot change the program’s arguments, icon or set it to run as administrator.

Advertised shortcuts that point to files can only point to executables installed with the MSI.

Non-advertised

Non-advertised shortcuts are the more familiar .LNK files that contain the information needed to start a program. They do not have the behaviour of advertised shortcuts. Their targets are files, for example [#ReadMe.txt]. Standard shortcuts seem very straightforward at a glance, but can quickly become quite complicated due to the nature of user profiles, per-machine/per-user installs, and the relationships between Features, Components and their key paths. MSDN (and others) suggest you should avoid them and use Windows Installer shortcuts whenever possible. Some people suggest the opposite.

Non-advertised shortcuts can each be placed in their own component and such components can be added or removed. A file can be installed per-machine and a shortcut can be per-user: in this case place a shortcut in a component separate from the file it points to.

Don’t make shortcuts key paths. Users like to move shortcuts. Use the registry key mentioned as the key path.

For per-machine installations, it’s OK to ignore the ICE 57 on the HKMU/HKLM key path in an unadvertised shortcut component.

If you create a per-machine installation, which cannot be switched to per-user, the standard Microsoft validation suite does not understand the context and for this reason will create 2 warning messages if any components with non-advertised shortcuts are created with file key path or a key path under HKLM (rather than HKCU which it wants). However if you do use HKCU then an unnecessary repair will take place for each user to create the per-user component key path. This is one case where it is wise to ignore a validation warning (be sure you understand them first though). In summary, when using non-advertised shortcuts with per-machine only installations, use a file or HKLM registry key path and ignore the ICE warning.

Other

To change the name of a shortcut in an upgrade, you must delete and recreate it; hence it can only be done in a major upgrade. It requires either creating a new component and removing the old one, or changing component composition. The latter option is not recommended unless the shortcut only appears in one version of one MSI.

The icon might be the only installed file if the target feature is advertised, so icons should be placed in separate files. If they are resource IDs in a PE file, the whole PE file will be used as the icon file. Therefore you should extract icon files to use as icons otherwise the whole file that the icon is in will be included (maybe multiple times) in the MSI.

Icon IDs that are associated strictly with file extensions or CLSIDs can have any extension, such as .ICO. However, icon files that are associated with shortcuts must have an ID such that their extension matches the extension of the target, so that when the icon is right clicked, the right context menu for it is selected. The shortcut will not work if this rule is not followed. For example, if a shortcut points to “program.exe”, then the icon ID must also have the extension “.EXE”.

For WiX, even though Icon can be nested under shortcut, you must still name the icon ID in the shortcut element.

Icons in non-advertised <Extension>s must be installed and the file ID referenced in the <ProgId>.

ICE ICE Baby

Some common ICE warnings and errors are explained below.

ICE03 “String Overflow”

If you get this in IDs, you can ignore this warning if your strings are unique within the column width limit. However it is best to fix this where possible. If you get this in a condition column, you may need to either break up the condition into subconditions or edit the schema to allow longer strings.

ICE33 “Warning Reg key xxx is used in an unsupported way”

You can ignore this message. Many tools convert <Extension> <ProgId> <Class> and other COM object definitions into registry keys. This is preferred but the ICE is somewhat outdated with respect to current best practice.

ICE43

ICE43 is telling you that your per-machine installation will not properly install the per-user shortcuts for users other than the one performing the installation if the key path is (or can be) per-machine. Creating a HKCU key will ensure that every user that logs on the machine will get the proper shortcuts installed.

You would think a file in a per-user location would suffice but the value of ALLUSERS can be set at install time and switch per-machine to per-user locations. There are also some poorly understood roaming user scenarios that the ICEs test for.

However if you do use an HKCU key path then an unnecessary repair will take place for each user to create the per-user component key path. This is one case where it is wise to ignore a validation message (be sure you understand them first though).

ICE57

If you have a per-machine installer, non-advertised shortcuts will cause this error. Ignore it.

ICE66

If your installer is targeted at Windows Installer versions prior to 4, ignore this message, otherwise you might need to update the schema version.

ICE69

If this is a warning then you can ignore it – the target component and the referencing component will always be installed together.

Sequences and Ordering

A recommended order for the initial actions is:

  • CA to test Privileged (only if elevated installation is required)
  • FindRelatedProducts
  • WixExitEarlyWithSuccess (early to avoid making user wait. Requires an old version to be found by the previous action)
  • AppSearch (may use the product code of a found product to search its ARP registry keys)
  • Retrieve ARPINSTALLLOCATION into INSTALLDIR (dependent on the results of AppSearch)
  • LaunchConditions (may test data found by AppSearch and FindRelatedProducts)

The position of RemoveExistingProducts must be chosen carefully. There are 4 areas listed here: RemoveExistingProducts Action

http://jpassing.wordpress.com/2007/06/16/where-to-place-removeexistingproducts-in-a-major-msi-upgrade/

See also the Phil Wilson book, P115.

The last two positions described on the MSDN page are the positions that ARE NOT “in the area” of InstallInitialize, but they require strict obedience to the component rules. The first two positions ARE “in the area” and are not compatible with fusion (the technology used for both SxS and GAC).
[BLAIR]

This last comment may be out of date now. Try a test.

FindRelatedProducts is sensitive to the value of ALLUSERS. If ALLUSERS is per-user, FRP searches for a user-specific installation. If you have a dialog that sets ALLUSERS, you must sequence FRP somewhere after it, before the scripted sequence starts. This is especially important for upgrades where FRP searches for the old product.

Note that even the earliest positioning of RemoveExistingProducts isn’t quite the same as removing the old version and installing the new. The versioning rules are still applied at some level and you can end up with missing components if the later installer has an earlier version of a key file in a component. In such a circumstance, your only option is to perform a separate removal before installing the new version. Better yet, if you control the versions, increase the later installer’s component’s version.

Custom Actions

Custom actions that modify the system must be deferred custom actions. Deferred actions take place during the installation transaction. Custom actions that change system state must also include a rollback custom action.

Develop data-driven custom actions. This makes it easier to service (i.e., patch) resources since you can transform a table and don’t have to reship a custom action. This is especially important to support patch uninstall, since anything a patch added won’t exist when the patch is uninstall and its transform removed from the view.

In most cases, relate your resources to components in your custom tables and schedule deferred actions based on both the state and the action of each resource’s component. You will find many good examples of this in the Wix sources available on SourceForge CodePlex.

Avoid using broadly scoped properties like PATCH or REINSTALL. This can prevent scenarios like slipstream installs where the product and patch are installed together in a single transaction.

Limit dependencies for custom actions. If you install such dependencies, this limits when your custom actions can be scheduled since your custom action – and installation – will fail without the dependency installed. This is especially important when linking to CRT8 and newer that install as side-by-side native assemblies, and aren’t available on Vista until after the commit phase is complete.
[HEATHS]

Immediate custom actions sequenced after InstallFinalize can’t be rolled back and won’t be elevated on Windows Vista onwards. Use a deferred custom action and mark as no-impersonate if they need elevating.

Starting with Windows Vista, the Windows Installer service only has privileges required for software installation. Custom actions that require additional privileges will fail. The Windows Installer service has the following specific privileges:

  • SeTcbPrivilege
  • SeCreatePagefilePrivilege
  • SeLockMemoryPrivilege
  • SeIncreaseBasePriorityPrivilege
  • SeCreatePermanentPrivilege
  • SeAuditPrivilege,SeSecurityPrivilege
  • SeChangeNotifyPrivilege
  • SeProfileSingleProcessPrivilege
  • SeImpersonatePrivilege
  • SeCreateGlobalPrivilege
  • SeAssignPrimaryTokenPrivilege
  • SeRestorePrivilege
  • SeIncreaseQuotaPrivilege
  • SeShutdownPrivilege
  • SeTakeOwnershipPrivilege
  • SeLoadDriverPrivilege

(Were any privileges reinstated in Windows 7 ?)

Since custom actions inherit the same privileges of the Windows Installer service, custom actions will have only these privileges, even when running in an elevated context. If your application installation requires configuration that requires other privileges, they will have to be granted to the custom action.

Some people use CAs to check for prerequisites because MSI does not run launch conditions in a specified order. That would have meant a Privileged check came after a check that required admin rights to run.

Remember that administrative installations might need to include your custom action, depending on what the CA does. You’ll have to decide which of the sequences it goes in AdminUI/ExecuteSequence/InstallUI/ExecuteSequence), and possibly what conditions are put on it.

Avoid using broadly scoped properties like PATCH or REINSTALL. This can prevent scenarios like slipstream installs where the product and patch are installed together in a single transaction.
[HEATHS]

MsiProcessMessage() logging doesn’t work from DoAction custom actions in OSes before Windows 2003.

Custom actions that modify the system must be set to impersonate=”no” except when changing user-specific resources such as HKCU. The installing user may not have permission to change the system. When not impersonating, the custom action runs as Local System.

Always link C++ custom actions to the runtimes statically. There are several potential problems when you rely on the runtimes already being present.

Custom action scenarios

This is a list of contexts in which custom actions run. They are all sufficiently different that they may not always be expressible in terms of each other, e.g. install rollback isn’t necessarily the same as uninstall during upgrade and patching.

Install

Removal

Patch install

Patch removal *special CA type available in some MSI versions.

Upgrade

Repair

+ Rollback for each of the above

+ Error cases for all of the above including an error in rollback, although it may not be possible to do much there.

Commit is usually for clean up after a CA succeeds.

64-bit

64- and 32-bit components can be in any location but it’s a convention worth following. Document any exceptions.

For binary custom actions, the bitness of the binary will dictate whether a 32- or 64-bit custom action server is created.

64-bit components in 32-bit MSIs will work but be careful that properties resolve to the locations you expect.

The arch[itecture] switch on candle determine whether you build a 32- or 64-bit MSI. That sets the default value of the Component/@Win64.

Where Things Go

Program files

We set INSTALLDIR by default to [ProgramFilesFolder]<company name>\<static product name>\<shortname><majorversion>. You should include the major version number if you want parallel installations.

Files shared by multiple products should go in [CommonFilesFolder]<company name>\<whateveritis>\.

This includes COM objects and registration of assemblies.

Put GACed files in a top level directory called GAC. There’s no point nesting it under the application – shared locations should be the same in all products so that “*” GUIDs evaluate to the same value.

Always keep code and data separate: applications should not create files in the INSTALLDIR at run-time. It prevents the folder being removed on uninstall; it will fail with non-admin users, or cause shadow copying on Vista onwards if INSTALLDIR falls anywhere under ProgramFilesFolder; it’s bad for security.

Configuration and data files will go in one of the following:

AppDataFolder roaming user’s configuration data per-user
CommonAppDataFolder all users’ configuration data per-machine*
LocalAppDataFolder non-roaming configuration data per-user and per-machine
PersonalFolder user’s own data folder“My Documents” per-user

*This location is read-only to non-admins so you may need to relax the permissions on your product’s subdirectory, depending on your requirements.

Registry entries pointing to these locations would usually go in HKLM or HKCU to match the per-user or per-machine attribute of the location.

HKLM…

Company

Product

Version-independent data

Version1

Per-machine data

Version2

Per-machine data

HKCU…

Company

Product

Version-independent data

Version1

Per-user data

Version2

Per-user data

New major versions should import/copy data from previous versions.

Installer data (which can vary over minor versions) may go under its product code –

HKMU/Software/<company name>/[ProductCode] or something similar.

COM (and other globally shared files)

Install each COM DLL or set of related DLLs in its own directory in Common Files. Name the directory after the COM object or group. Any DLLs that are written, used and updated as a group should be installed and updated as group. That way, one product can never update them or their dependencies non-atomically. e.g. if one COM DLL were in one product and two COM DLLs in another, the first’s installer might update only one DLL and its dependencies. If there were any co-dependencies, it might screw up. If the COM objects are properly backwards compatible then this won’t matter but that’s hard to guarantee.

Other global components include ordinary files installed to a globally shared location, possibly by multiple products. On rare occasions, you are forced by third parties to install to System32. If they don’t provide an installer, make such files permanent – you never know if someone else has installed the same file with a different component.

Writing An MSI

This is mostly about WiX. InstallShield users might find a few useful bits. It also assumes there will be a chainer included in the end result.

Things installer builders need to know from developers and PMs

This is not an exhaustive list.

Which products are intended to be compatible with each other, including older versions of themselves. Compatibility usually means “working together” but could also be whether they can be installed on the same machine, even when they aren’t intended to interoperate. We sometimes refer to this as the “product compatibility matrix”.

How the product will be serviced.

UI: product title, package name, icons, splash screens.

New third-party dependencies.

Third-party dependencies no longer needed.

Changes that break backwards compatibility.

Added files & resources.

Removed files & resources.

Whether a file goes in the GAC/WinSxS folder or is if it is a private assembly.

Changes to COM interfaces, including .Net assemblies registered for COM using regasm – the registry entries will need updating.

Whether a resource could be shared between applications or not and must therefore be a shared component.

If an assembly is a multifile assembly and which files are in it.

Information that will be entered into the registry should be provided in a .REG file and maintained by the developer (unlikely to happen).

Regsrvr32, gacutil and regasm are not used. The registry/assembly information is extracted and entered into the MSI tables. Therefore do not assume these tools will be used and any changes picked up automatically. Provide a .REG file that you know works. Don’t allow self-registration which isn’t reference counted.

Version your components otherwise patches won’t replace them and may require the user to insert the source disk. Increment the version when you change it. The build can do this automatically so consult whoever builds it. However, control versions manually in release branches if automatically creating patches.

Don’t put the version number, dates or other non-static info in the file name, except perhaps the major version so that incompatible versions have different names. Put version information in the version resource – that’s what it’s for.

Can the application be repaired back to a working state ? This may be its initial state or it may be the initial state but with user settings left untouched. The user might have cocked the settings up – would you want to reset them in a repair ? A repair will replace files with the ones originally installed so don’t store user data (documents) in anything that gets installed. Instead, create it at runtime. Add a repair/reset configuration option to the application that asks the user if they want to reset their options or not and maybe calls MSI repair.

Be careful how you write database upgrade scripts. They shouldn’t destroy existing data by dropping tables. Make sure they are idempotent.

Layout of source files

There are a few ways to do this. For any scheme you devise, work out both how much effort it is to copy the files into place and to set the source locations in the Wix. Putting minimum effort into one may result in a lot of work in the other.

You can keep different parts of the installer separate by putting the components into a separate DirectoryRef, using ComponentGroups and so on.

One method that can work is to set up all your projects to build into a common output directory. Anything that builds duplicate file names, such as language resource DLLs may need to create subdirectories. You only need to set one reference in the Wix sources. Files that are added to the installer but don’t need to be built, such as static HTML, text files can either be referenced from their location in the source tree or set to copy to the output directory. The outputs of multiple builds can be copied into the final output directory.

Naming suggestions

The component ID defaults to the key path resource’s ID. Omit the Id attribute and use that default unless creating a wixlib and there is a chance of name collisions.

For file ID, use the long filename. Wix 3 will generate Ids for File elements using the file name portion of the Name with any illegal characters substituted.

Feature names should be human friendly as they are referred to on the msiexec command line eg msiexec /i my.msi ADDLOCAL=FeatureName1,FeatureName2. For display in the interface, the feature title is used.

Registry IDs are generated for you and can be omitted unless you need to refer to one, which is less usual.

Directory IDs can be XDir where X is what goes in the dir. E.g. a web service or docs. INSTALLDIR is a well-known convention worth adopting. For a simple application, it is where all the code is installed. For more complex applications, it is where the main executable lives.

Try to come up with an ID for the product which will persist across all versions. Even codenames may change between releases. This will be useful for registry keys. There may be such a key already in the application that can be used.

Try to keep the product name out of any important data such as the MSI name or registry keys. It’s just for display (and at the whim of marketing). For version independent “keys”, create your own identifier. For version dependent “keys”, you might want to combine your product ID from above with the product version, usually just the major version.

A reason to avoid merge modules.

If other parties use your merge module in their product, you cannot release fixes to your software to customers – the other parties must all release fixes. This is impossible if the other party isn’t in business or supporting their product any more. If you provide MSIs, you can release an updated MSI or patch which customers can get directly from you.

What about internal distribution ? It’s better to use Wix libs or fragments, both of which are more flexible. You can read them, they don’t mangle property names, it’s hard or impossible to refer to things inside merge modules, Wix fragments can be kept in source control etc.

Other

In Wix2, always use ?????? for your package code GUID which will generate a new one each time. In later versions, you don’t have to supply the package code.

By default, always ensure your upgrade code is carried over from the previous product version. Products with the same upgrade code are considered related. Only change the upgrade code if you want to allow 2 products to be installed on the same machine at the same time, such as 2 different major versions.

When copy and pasting, remember to change the GUIDs (unless you really mean not to). Be careful that you remove or edit any File/@Source attribute after copying and pasting or you might pick up the wrong version.

The File/@Source attribute for shared components should be the same in all .WXS files that that version of the file appears in. This avoids picking up different versions for the same thing.

If you install a registry value, and it already existed, uninstallation will still remove it. The key remains intact. Possible solution is set Component/@Permanent and make it a key path ? MSI’s incomplete control of registry values is a good reason to prefer files for configuration.

Writing A Merge Module Or Wixlib

Merge modules and wixlibs (which I’ll refer to as “modules”) seem to work best for self-contained pieces of installation that are installed in their own directory so that components don’t clash with other modules. If you did, you’d have to sort out the conflicts.

If the location is variable, you have to ensure you don’t include any resources that might conflict with those installed by other modules in case they both end up in the same directory. This can require factoring out potentially shared resources into their own module.

Merge modules and wixlibs are best used to share resources between products and not to reuse anything multiple times within the same product, which is impossible using these methods (instead, see Components).

Setting the merge module install location

You should install a merge module to a fixed, known location, if possible.

A merge module is installed to its own TARGETDIR. The TARGETDIR is set to the location of the merge reference in the parent installer’s directory hierarchy. [$component] is useful for getting this directory from in the merge module. An example is given below.

Parent installer:

<Directory Id=”INSTALLDIR” name=”Product”>

<Merge Id=”Lib” … />

</Directory>

Merge module:

<Directory Id=”TARGETDIR” Name=”SourceDir”>

<Component … />

<Component … />

</Directory>

Check List

Note: Most of the items that would go in this section are now covered by the use of the boilerplate.

Signing

Sign your MSI with signtool. You can store your certificate securely in a .PFX or, better yet, on a signing server.

If using external cabs, as you might with the Burn bootstrapper, use the DigitalSignature and DigitalCertificate Wix elements.

In Wix 3, add PatchCertificate elements to client software to enable non-admins on Vista to install patches. Use a .CER file with this.

Check for privileged user

Do this using a custom action which runs first in the sequence. If you need privileges to test launch conditions, it may fail because launch condition order is not defined.

There is debate if this is right. If an elevation prompt can occur after the check then there is the possibility of an over-the-shoulder elevation. This would never be reached if the check prevented the installation from continuing. Keep the check in if the MSI is intended to be elevated before it launches.

MSI Bugs

File search/AppSearch

The FileVersion minimum is not inclusive, unless you also specify a language to search for (weird eh). Therefore set it to 0.0.0.1 less than the version you’re after. N.B. all 4 fields are checked.

The MaxVersion I couldn’t work out at all. I had to set it to 3.x.x.x to get it to detect ANY 2.x.x.x. Perhaps someone can narrow this down. Stefan Kruger reckons you find a file less than the value; MSI says its <=. Neither quite reflects my findings though.

Administrative installation

The TARGETDIR property set on the command line needs to be absolute.

If the extracted path is > MAXPATH, you’ll get an error saying the cabinet is corrupt. It isn’t – try installing to a shorter path. The log will tell you which file caused the failure.

Localisation

The recommended approach with Wix now is to use .WXLs. See the Wix help under “How To: User Interface and Localization”. Some of the information below is superseded by this.

Use Wix localisation support to hold locale specific information which makes it easier to generate localised packages. Use the localised packages to generate the language-specific transforms which can be embedded in the MSI. Additionally, the WixUI library was changed from neutral to 1252 for English. You could create an ASCII only copy of the English library for neutral packages.

MSI doesn’t support packages with multiple languages. The officially supported approach is to use one language-neutral package, multiple language-specific transforms, and a bootstrapper that applies the right transform before launching the .MSI.
[boB]

Set Product /@Codepage and Product/@Language to 0 and write all strings in ASCII.

Set Package/@SummaryCodepage to 1252 and write summary properties using that codepage.

Transforms cannot change the summary (package) language so set Package/@Languages to 0. (The recommendation for the summary information language property (Package/@Language) is that it should match the Product/@Language value of the base package).

The ProductLanguage is registered as the language of the product and can be queried from the installer’s configuration data. The SummaryInformation language is used in basic UI to determine the language of the buttons and strings. There are plans to offer improved support for multiple language installs (with full guidelines) in a future version of the Installer.

Instead of using transforms embedded into the MSI, you could also produce multiple products using different .WXL files but they would need patching separately, extra detection code, etc.

Rob Mensching recommends using different product codes for each language version to make it possible to patch each version independently. Using the language neutral scheme described earlier should work with this approach as there is only 1 language – the neutral language. Should we transform the product code with the language transform ? Probably, so that if there are several installed language versions, each can be patched.

ARP

ARP (“Add/Remove Programs”) is known as “Programs And Features” in later Windows versions. It is possible to hide products from ARP.

I wouldn’t recommend hiding the ARP entry if you want to use patches. ARP shows which patches have been applied and provides an interface to remove them. If you set ARPSYSTEMCOMPONENT, your MSI and all its patches and upgrades must write the uninstall registry key ARP entries themselves. Keeping the ARP entries up to date yourself when using this is very complex and probably not worth it when you consider patches and upgrades. The procedure is here: Heath Stewart on the dangers of ARPSYSTEMCOMPONENT

Setting ARPSYSTEMCOMPONENT

If you set ARPSYSTEMCOMPONENT to 1, you may find the following useful.

When the property is set, it causes this registry value to be written:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{Product Code GUID}\SystemComponent=1.

When ARP sees this value it ignores the registry entries and does not display an ARP entry unless the installer has written an alternative registry key. This key can have any name under 63 characters but must go in the HKLM\….\Uninstall key. You may also wish to add a major version number to the key name if you want parallel installations of your product. Windows shell associates the key you create with the product code GUID key.

The only values you have to write under your new key are DisplayName and UninstallPath but the more information you provide, the better ARP will work. I would recommend setting the usual ARP properties and having the RegistryValue elements write the values of the properties. That way if you undefine ARPSYSTEMCOMPONENT, ARP will still be populated properly. Do not write registry values for properties that may be empty. Empty registry values can give bad ARP entries. Obey the component rules and don’t add and remove values from the component in successive versions without either changing the component code and registry key name, or ensuring no other versions can be installed on the same machine at the same time.

Create a hidden, required feature solely for adding your registry key writing component. I call mine ARPFeature. This is needed so that successive patches and upgrades can put their components in here without reorganising the feature tree and so they can be assured of going in a feature that is always installed. Refer to Heath Stewart’s blog in the useful links section for the rest of how to add these components. The rest of this text describes the component itself.

The uninstall string can be set to the value: [SystemFolder]msiexec.exe /x [ProductCode] if you aren’t running a custom uninstaller. Set UninstallString to be the same value. The ModifyPath string is similar to the uninstall string but is run when the Change button is pressed (which may be the only button if you disable remove and repair). You can get the MSI to run in maintenance mode by setting this value to: [SystemFolder]msiexec.exe /i [ProductCode].

Since you have to put the component in its own feature and file element keys are not accessible across features, and to keep within the guidelines of including files with registry keys that access them, include an icon file in the component. Set DisplayIcon to its file path. ARPPRODUCTICON will have to point to a copy of the file included in the icon table.

Registry keys

The table below shows the Uninstall key’s values and the corresponding MSI properties.

DisplayName [ProductName]
DisplayVersion [ProductVersion]
Publisher [Manufacturer]
DisplayIcon [#Product.ico] Product.ico is a File/@Id
HelpLink [ARPHELPLINK]
InstallLocation [INSTALLDIR] Or ARPINSTALLLOCATION
InstallSource [SourceDir]
URLInfoAbout [ARPURLINFOABOUT]
Language [ProductLanguage] RegistryValue/@Type=”integer”
UninstallPath Could be anything.
UninstallString Should be set as UninstallPath.
ModifyPath Could be anything.
NoModify
NoRemove
NoRepair
Comments [ARPCOMMENTS]
Readme [ARPREADME]
Contact [ARPCONTACT]
URLUpdateInfo [ARPURLUPDATEINFO]
HelpTelephone [ARPHELPTELEPHONE]
AuthorizedCDFPrefix [ARPAUTHORIZEDCDFPREFIX]
SettingsIdentifier [MSIARPSETTINGSIDENTIFIER]
Version Could be –
VersionMajor calculated from –
VersionMinor ProductVersion.
InstallDate Format is yyyymmdd

The value for ReadMe is ShellExecute()ed on Windows XP so you could put a program path there, or a script or URL.

The ARPSIZE property is ignored from XP SP2 onwards so unless Vista introduced another mechanism, the size value in ARP could possibly be apocryphal or at least wildly inaccurate and there’s nothing you can do about it. It’s a Windows Shell limitation.

You can set the NoChange, NoModify and NoRemove values or their ARP property equivalents to 0 or 1 to control the buttons that appear. These buttons are connected to the UninstallPath/ModifyPath entries.

If the product is an MSI, the uninstall path is not run on removal. This setting is for other installation technologies. An MSI is removed using the equivalent of msiexec /x {product GUID}.

Bootstrappers

Override installer detection for executables with a manifest. This example assumes you will always require elevation.

Add a manifest for the exe as follows for admin group and standard users

<requestedExecutionLevel  level=”requireAdministrator” uiAccess=”false” />

Embed the manifest with the procedure here Windows Vista Application Development Requirements for User Account Control (UAC)

Q. Is there a way to avoid the setup.exe bootstrapper in the home administrator case?
A. You could recommend folks run the install from an elevated command prompt. Otherwise add a manifest to the setup.exe

Registry

FLEXLM registry key

Since it’s a shared registry value, in Wix2, you must set it to permanent and use the same GUID and make it a key path between all products.

In Wix3, set the parent RegistryKey/@Action to create instead of making it permanent, otherwise proceed as above.

MSI Behaviour

This section describes how MSI works in certain situations.

Files

Files are updated according to the File Versioning Rules.

Registry

For registry value key paths, a component is considered out-of-date if the value data is different from the data in the package or the key or value is missing. Therefore installed registry values should never be modified.

Behaviour on removal

If the Installer… Then the Uninstaller…
Created a new registry value
(RegistryValue/@Action = write)
Deletes that registry value, whether it has changed or not.
Modified an existing registry value(RegistryValue/@Action = write & installer overwrote an existing value) Deletes that registry value, whether it has changed or not.
Deleted an existing registry value Does not restore it.
Created a registry key (only happens if forced or installs subkeys/values) Does not delete the key unless explicitly set to do so then deletes the key regardless of contents.
Deleted an existing registry key Does not restore it.

Other

You may need to support installation under terminal services. This affects custom actions.

Do not attempt to change the target directory path during a Maintenance Installation, because you can’t.

In Wix, you can pass multiple -b switches to light to bind different directories, as long as there are no duplicate files that would end up picking up the wrong files.

Some Useful links

UAC and MSI

http://blogs.msdn.com/rflaming/default.aspx

Using Windows With UAC > Guidelines for packages

How do I get one credential dialog for a multiple package install?

http://blogs.msdn.com/rflaming/archive/2006/10/01/780231.aspx

Heath Stewarts Blog on ARPSYSTEMCOMPONENT

http://blogs.msdn.com/heaths/pages/arpsystemcomponent.aspx

MSI Conditional Statement Syntax

http://msdn.microsoft.com/en-us/library/aa368012(VS.85).aspx

Recommended summary information stream values

http://msdn.microsoft.com/en-us/library/aa372049(VS.85).aspx

Boilerplate Product.wxs File

This was devised to help create projects quickly. It supports our conventions and servicing strategy. COMPANYNAME has been substituted for the company’s name: replace it with your own.

</pre>
<?xml version="1.0" encoding="UTF-8"?>

<!-- !!! IMPORTANT NOTES !!!

Search for "TODO" placeholders and fill in/remove as appropriate. GUIDs must be uppercase.

You must pass a sourceRoot variable containing the path to the directory containing the "Keys" subdirectory.

You must pass a buildNumber variable which will come from the build.

This is a 32-bit installer only but it will install on 64-bit windows under WOW64. It is per-machine and requires admin privileges to install.

You must reference wixutilextension and wixuiextension.

-->

<?ifndef sourceRoot ?>

<?error sourceRoot is not defined. See notes in the .wxs file. ?>

<?endif?>

<?ifndef buildNumber ?>

<?define buildNumber = 0 ?>

<?endif?>

<?if $(var.buildNumber) = "" ?>

<?define buildNumber = 0 ?>

<?endif?>

<!-- Change or pass compression = none for faster builds or high for smaller ones. -->

<?ifndef compression ?>

<?define compression = "high" ?>

<?endif?>

<!-- This is the minor version independent product name that must be common amongst all minor versions because it is used for directory and registry names.

It should be changed for major versions. There shouldn't usually be a MajorVersionIndependentProductName: changing the current version shouldnt be able to break old ones.

If a major version wants to use the previous versions registry/file entries, it would import them. This allows the old version to keep working in parallel.

Do not use minor version or service pack info in this name as upgrades will

go into the same directory and registry keys. It looks wrong to have vN.5 in vN.0 directory.

Example: versionedName = "Product1"

-->

<?define versionedName = "TODO insert unchanging product name 1" ?>

<!-- Product display (marketing) name. Unversioned. Example: "Whizz Bang Editor 2012" -->

<?define displayName = "TODO insert display name" ?>

<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension" xmlns:iis="http://schemas.microsoft.com/wix/IIsExtension" >

<!-- Future versions may add "SP1" etc to the name. The second field of version may be the SP number. -->

<Product Id="TODO generate and insert GUID"

Language="0"

Manufacturer="COMPANYNAME"

Name="$(var.displayName)"

Version="1.0.$(var.buildNumber)"

UpgradeCode="TODO generate and insert GUID">

<Package Comments="This installer database contains the logic and data required to install $(var.displayName)."

Compressed="yes"

Description="$(var.displayName) installation database"

InstallerVersion="301"

InstallPrivileges="elevated"

InstallScope="perMachine"

Keywords="Installer,MSI,$(var.displayName),COMPANYNAME,COMPANYBUSINESS"

Languages="0"

Manufacturer="COMPANYNAME"

Platform="x86"

SummaryCodepage="1252" />

<!-- Ignore warning about invalid attribute. -->

<Media Id="1" EmbedCab="yes" Cabinet="Product.cab" CompressionLevel="$(var.compression)" />

<MajorUpgrade DowngradeErrorMessage="A later version of [ProductName] is already installed. Set-up will now exit." MigrateFeatures="yes" />

<!-- Ignore ICE81 warning about unreferenced digital certificates. The ICE is incorrect. -->

<PatchCertificates>

<DigitalCertificate Id="CodeSigningCertificate" SourceFile="$(var.sourceRoot)\Keys\CodeSigning2011-2014.cer" />

</PatchCertificates>

<!-- Properties and AppSearches -->

<Property Id="MSIENFORCEUPGRADECOMPONENTRULES" Value="1" />

<Property Id="LIMITUI" Value="1" />

<Property Id="ARPNOMODIFY" Value="1" />

<Property Id="ARPCOMMENTS" Value="$(var.displayName) TODO insert product description." />

<Property Id="ARPCONTACT" Value="Customer Support" />

<Property Id="ARPHELPLINK" Value="http://support.COMPANYNAME.com" />

<Property Id="ARPHELPTELEPHONE" Value="Check your maintenance agreement." />

<Property Id="ARPREADME" Value="*** Set by custom action ***" />

<Property Id="ARPURLINFOABOUT" Value="http://www.COMPANYNAME.com" />

<Property Id="ARPPRODUCTICON" Value="ProductIcon.exe" />

<Property Id="ARPINSTALLLOCATION">

<RegistrySearch Id="GetARPInstallLocationFromRegistry" Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductCode]" Name="InstallLocation" Type="raw" />

</Property>

<!-- Features -->

<Feature Id="Main" Level="1" Absent="disallow" Title="Core" Description="Essential components of the application." AllowAdvertise="yes" >

<ComponentRef Id="ReadMe"/>

<ComponentRef Id="RemoveCompanyShortcutDir" />

<ComponentRef Id="RemoveProductShortcutDir" />

<ComponentRef Id="ProductShortcut" />

</Feature>

<!-- Launch conditions -->

<!-- TODO Fill in conditions for OS support etc. -->

<Condition Message="This product only runs on Windows XP or later."><![CDATA[ Installed or VersionNT >= 501 ]]></Condition>

<!-- Directory layout -->

<!-- TODO This is the standard layout for various products. Yours may have different requirements. -->

<Directory Id="TARGETDIR" Name="SourceDir">

<Directory Id="ProgramFilesFolder">

<Directory Id="CompanyProgramsDir" Name="COMPANYNAME">

<Directory Id="ProductDir" Name="TODO insert generic product name">

<Directory Id="INSTALLDIR" Name="$(var.versionedName)" />

</Directory>

</Directory>

</Directory>

<!-- TODO Uncomment this section if installing any shortcuts.

<Directory Id="ProgramMenuFolder">

<Directory Id="CompanyShortcutDir" Name="COMPANYNAME">

<Directory Id="ProductShortcutDir" Name="$(var.displayName)" />

</Directory>

</Directory>

-->

</Directory>

<!-- Custom actions -->

<!-- Before running appsearches that require admin access, we must check in a CA if user is admin. LaunchConditions run after AppSearch. -->

<CustomAction Error="25003" Id="EnsurePrivileged" />

<!-- Set INSTALLDIR to registry version else default if not found. -->

<SetProperty Id="INSTALLDIR" Value="[ARPINSTALLLOCATION]" After="AppSearch">ARPINSTALLLOCATION</SetProperty>

<!-- Write install dir to ARP. -->

<SetProperty Id="ARPINSTALLLOCATION" Value="[INSTALLDIR]" After="CostFinalize" Sequence="execute" />

<!-- Set ARPREADME to the path of an installed file. -->

<SetProperty Id="ARPREADME" Value="[#ReadMe]" After="SetARPINSTALLLOCATION" Sequence="execute" />

<!-- Prevent any chainer aborting if we already have the same (or later) version installed. -->

<CustomActionRef Id="WixExitEarlyWithSuccess" />

<!-- Sequences -->

<!-- TODO This is the usual UISequence. However, on Vista+ the UISequence isnt run elevated unless the MSI is run from a privileged process. If starting unelevated, remove the ensurePrivileged check in the UISequence.

NB Privileged access may be required for some app searches. Elevation is usually achieved with an executable SFX and a manifest. This check is there in case the msi is run external to the SFX. -->

<InstallUISequence>

<Custom Action="EnsurePrivileged" Before="FindRelatedProducts">not Privileged</Custom>

<Custom Action="WixExitEarlyWithSuccess" After="FindRelatedProducts">WIX_DOWNGRADE_DETECTED</Custom>

</InstallUISequence>

<InstallExecuteSequence>

<Custom Action="EnsurePrivileged" Before="FindRelatedProducts">not Privileged</Custom>

<Custom Action="WixExitEarlyWithSuccess" After="FindRelatedProducts">WIX_DOWNGRADE_DETECTED</Custom>

</InstallExecuteSequence>

<!-- Binaries -->

<Icon Id="ProductIcon.exe" SourceFile="TODO insert path to ico file at build-time" />

<!-- User Interface -->

<UI>

<Error Id="25003">You must be running as an administrator or with elevated rights to install, maintain or remove this product.</Error>

</UI>

<UIRef Id="WixUI_ErrorProgressText" />

<!-- Components not automatically harvested. -->

<!-- TODO Uncomment this section if installing any shortcuts.

Ignore ICE57 errors with shortcuts.

<DirectoryRef Id="CompanyShortcutDir">

<Component Id="RemoveCompanyShortcutDir">

<RemoveFolder Id="RemovecompanyShortcutDir" On="uninstall" />

<RegistryValue Root="HKMU" Key="Software\COMPANYNAME\Installer" Name="RemoveCompanyShortcutDir" Value="KeyPath" KeyPath="yes" Type="string" />

</Component>

</DirectoryRef>

<DirectoryRef Id="ProductShortcutDir">

<Component Id="RemoveProductShortcutDir">

<RegistryValue Root="HKMU" Key="Software\COMPANYNAME\$(var.versionedName)\Installer" Name="RemoveProductShortcutDir" Value="KeyPath" KeyPath="yes" Type="string" />

<RemoveFolder Id="RemoveProductShortcutDir" On="uninstall" />

</Component>

<Component Id="ProductShortcut">

<RegistryValue Root="HKMU" Key="Software\COMPANYNAME\$(var.versionedName)\Installer" Name="ProductShortcut" Value="KeyPath" KeyPath="yes" Type="string" />

TODO The value in brackets (ie ProductApplication) should be the Id of your applications main exe file element. Retain the #.

<Shortcut Id="ProductShortcut" Name="$(var.displayName)" Description="The $(var.displayName) application." Advertise="no" IconIndex="0" Icon="ProductIcon.exe" Target="[#ProductApplication]" />

</Component>

</DirectoryRef>

-->

<DirectoryRef Id="INSTALLDIR">

<Component>

<File Id="ReadMe" KeyPath="yes" Source="TODO insert path to release notes at build time. html is the recommended format" />

</Component>

<!-- TODO Further components can be inserted here or in a separate file. -->

</DirectoryRef>

</Product>

</Wix>
<pre>

Published by

One response to “MSI Writing Guidelines”

  1. Great guide! Often refer to it.

Leave a comment