WiX tutorial

Lesson 6 COM, Expression Syntax, Miscellanea

6.1 Components of a Different Color

A tool in WiX, Heat serves to harvest data from various sources (folders, files, performance counters, web sites) where the number of entries might be too large to author the corresponding WiX source file manually. Its primary intention is to be run once, to collect the data to be used later in the usual maintenance of the source files, not to be part of a build environment where it is run on a changing set of input data over and over again. If you still want to use it in this second way, you have to be very careful to make sure that changes in the input don't introduce unwelcome effects (mostly breaking the component rules). Heat does have features that help you achieve this goal but be careful when using them.

The first mode of Heat will help us author the necessary WiX source for one or more folders full of files:

heat dir folder -cg SampleGroup -out SampleGroup.wxs

will harvest the folder specified recursively, creating a Fragment consisting of a ComponentGroup with the name specified with the -cg switch. The group will contain as many components as there are files, each component having a single file inside, as the rules dictate, and will assign uniquely generated component, directory and file identifiers that remain the same when regenerated on the same input set. GUIDs will not be generated (only a placeholder text) unless explicitely instructed to do so by the -gg switch.

In addition to the component group, Heat also generates fragments containing directory references (DirectoryRef tags) for each folder traversed recursively. The root directory harvested will have the identifier TARGETDIR, unless you specify your own name:

heat dir path -dr MyDirName -cg SampleGroup -out SampleGroup.wxs

Note that the automatically generated identifiers use this name as a seed. Changing the name will change all identifiers, with possible dire component-rule violation consequences if changed at an inappropriate time.

Yet another switch, -srd will suppress the identifier generation for the root folder specified. File components in the root folder will refer to their Directory as either TARGETDIR or the name specified in the -dr switch and there will be no separate DirectoryRef fragment for the root folder:

heat dir path -srd -dr MyDirName -cg SampleGroup -out SampleGroup.wxs

The second mode of the tool deals with a single file. If there are registry, COM and similar items to be collected from the file, they will all be extracted by Heat (the subject here was the Interop assembly for the Shell32.dll system library):

heat file file -cg SampleGroup -out SampleGroup.wxs

This results in a similar source file, with all details duly extracted:

<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Fragment>
      <ComponentGroup Id="SampleGroup">
        <Component Id="cmpA8B0842041500B0ACE61F7EFD0FBD893" Directory="dir0F6F75DF46D1BACE2233EC573E6D4AA9" Guid="PUT-GUID-HERE">
          <File Id="filDDAAB2C11E1E5AE4668D99216C3B5523" KeyPath="yes" Source="SourceDir\SampleHeat\Interop.Shell32.dll" />
          <RegistryValue Root="HKCR" Key="CLSID\{0A89A860-D7B1-11CE-8350-444553540000}\InprocServer32\1.0.0.0"
            Name="Class" Value="Shell32.ShellDispatchInprocClass" Type="string" Action="write" />
          <RegistryValue Root="HKCR" Key="CLSID\{0A89A860-D7B1-11CE-8350-444553540000}\InprocServer32\1.0.0.0"
            Name="Assembly" Value="Interop.Shell32, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" Type="string" Action="write" />
          <RegistryValue Root="HKCR" Key="CLSID\{0A89A860-D7B1-11CE-8350-444553540000}\InprocServer32\1.0.0.0"
            Name="RuntimeVersion" Value="v2.0.50727" Type="string" Action="write" />
          <RegistryValue Root="HKCR" Key="CLSID\{0A89A860-D7B1-11CE-8350-444553540000}\InprocServer32\1.0.0.0"
            Name="CodeBase" Value="file:///[#filDDAAB2C11E1E5AE4668D99216C3B5523]" Type="string" Action="write" />
          <RegistryValue Root="HKCR" Key="CLSID\{0A89A860-D7B1-11CE-8350-444553540000}\InprocServer32"
            Name="Class" Value="Shell32.ShellDispatchInprocClass" Type="string" Action="write" />
          <RegistryValue Root="HKCR" Key="CLSID\{0A89A860-D7B1-11CE-8350-444553540000}\InprocServer32"
            Name="Assembly" Value="Interop.Shell32, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" Type="string" Action="write" />
          <RegistryValue Root="HKCR" Key="CLSID\{0A89A860-D7B1-11CE-8350-444553540000}\InprocServer32"
            Name="RuntimeVersion" Value="v2.0.50727" Type="string" Action="write" />
          <RegistryValue Root="HKCR" Key="CLSID\{0A89A860-D7B1-11CE-8350-444553540000}\InprocServer32"
            Name="CodeBase" Value="file:///[#filDDAAB2C11E1E5AE4668D99216C3B5523]" Type="string" Action="write" />
          ...
        </Component>
      </ComponentGroup>
  </Fragment>
  <Fragment>
      <DirectoryRef Id="TARGETDIR">
          <Directory Id="dir0F6F75DF46D1BACE2233EC573E6D4AA9" Name="SampleHeat" />
      </DirectoryRef>
  </Fragment>
  <Fragment>
      <DirectoryRef Id="dir0F6F75DF46D1BACE2233EC573E6D4AA9" />
  </Fragment>
</Wix>

A few additional words have to be added here. People often wonder whether they should author all their registry and other changes into their installation package or let their installed components (DLLs, for instance) register themselves by adding the registry entries or carrying out other setup tasks when they are first run. The answer is clear: never use self registration. The Windows installer needs to be able to keep track of all registration and file changes of your product in order to be able to track versions, updates and to remove the product completely and reliably. Moving important data out of the jurisdiction of the Installer mechanism will jeopardize this and only cause problems to your users. Using self registration is very strongly discouraged by Microsoft and should be avoided at all costs.

The third mode works through a Visual Studio or a compatible project file and creates references to all files of the specified type: Binaries, Symbols, Documents, Satellites, Sources or Content. For instance, with Binaries the tools collects all binary files to be built and deployed by the project, all put into their respective components and directories, ready to be included in your final WiX source with a simple reference:

heat project projectfile -pog:Binaries -cg SampleGroup -out SampleGroup.wxs

In our previous examples, we used Heat to generate a fragment for later inlcusion into a complete setup package. We can also tell it to create a module or a standalone product instead. For smaller packages, the tool might actually author the bulk of the WiX source files and only some items (GUIDs, textual descriptions) has to be added manually.

heat ... -template:module -cg SampleGroup -out SampleGroup.wxs
heat ... -template:product -cg SampleGroup -out SampleGroup.wxs

Typelibs are supported directly in WiX, there is no need to use Heat or any other tool to gather all their internal information:

<File Id="file.dll" Name="file.dll" KeyPath="yes">
  <TypeLib Id="YOURGUID-0BFE-4B1A-9205-9AB900C7D0DA" Language="0" />
</File>

6.2 Expression Syntax

We've already used conditional expressions, among others, in Condition and Publish tags. We are already aware of logical operators like NOT, AND, OR or comparisons like <, >, =. To have them all described, here they are in order of precedence:

Logical operators
NOT Prefix unary operator; inverts state of following term.
AND logical AND
OR logical OR
XOR exclusive OR
EQV equivalence
IMP implication
Comparative operators
= equal
<> not equal
> greater than
>= greater than or equal
< less than
<= less than or equal
˜= equal, case insensitive
˜<> not equal, case insensitive
˜> greater than, case insensitive
˜>= greater than or equal, case insensitive
˜< less than, case insensitive
˜<= less than or equal, case insensitive
Substring operators, only between strings
>< left contains right
<< left starts with right
>> left ends with right
Bitwise operators, only between integers
>< bitwise AND
<< high 16-bits of left equal to right
>> low 16-bits of left equal to right

In these expressions, you can use property names (remember that they are case sensitive). Non-existent property names will be treated as empty strings. The logical value of a property reflects whether it has been set—meaning that you cannot check for a logical value by simply using the property:

PROPERTY
This will evaluate to true if the property has been set and has any value, even if this value is false.
NOT PROPERTY
This will evaluate to true if the property has not been set at all.
PROPERTY = TRUE
PROPERTY = FALSE
This is the proper way to check the value of a logical property.

Prepending some special characters to the names will give them extra meaning:

 
% environment variable (name is case insensitive)
$ action state of component
? installed state of component
& action state of feature
! installed state of feature

The last four can return the following integer values:

 
-1 no action to be taken
1 advertised (only for features)
2 not present
3 on the local computer
4 run from the source

A few examples to make things clearer:

(&FeatureName = 3) AND NOT (!FeatureName = 3)
Run action only if the product will be installed locally. Do not run action on a reinstallation.
The term &FeatureName = 3 means the action is to install the feature locally.
The term NOT (!FeatureName = 3) means the feature is not installed locally.
(&FeatureName = 2) AND (!FeatureName = 3)
Run action only if the feature will be uninstalled.
This condition only checks for a transition of the feature from an installed state of local to the absent state.
(?ComponentName = 3) AND ($ComponentName = 2 OR $ComponentName = 4)
Run action only if the component was installed locally, but is transitioning out of state.
The term ?ComponentName = 3 means the component is installed locally.
The term $ComponentName = 2 means that the action state on the component is absent.
The term $ComponentName = 4 means that the action state on the component is run from source. Note that an action state of advertise is not valid for a component.
?ComponentName = $ComponentName
Run action only on the reinstallation of a component.

6.3 Formatted Strings

Strings appearing in control texts can be formatted. We have already used property names in square brackets but there are more complicated rules around here, too. First if the property name is not valid in [Property], the whole substring is left unchanged.

This rule is somewhat different if the square brackets are nested. [[Property]] will first look up Property, then use its value as another property name, then look this second property up. In any of these property names are not found, the whole substring will be considered an empty string.

Blackslashes can be used as escape characters to denote characters otherwise having special meaning (like square brackets).

The substring [~] is replaced with a null character. This makes is possible to compile REG_MULTI_SZ registry strings.

The [#filekey] notation will return the full path of the file with the specified Id but only after CostInitialize, FileCost or CostFinalize have been run. The path will depend on whether the file belongs to a component that is installed locally or from the source.

The [$componentkey] notation will return the install directory of the specified component but only after CostInitialize, FileCost or CostFinalize have been run. The path will depend on whether the file belongs to a component that is installed locally or from the source.

The [!filekey] notation is usually equivalent to [#filekey] with the exception of the Value attribute of Registry or IniFile tags. In these cases, it will return the full short path of the file.

The various parts of the toolset (compiler, linker) have their own preprocessors to make it possible to inject strings defined outside (eg. on a command line) to be used in the source. The compiler, Candle uses the $(var.Foo) syntax and you can provide the value either in the source file itself:

<?define Foo=bar?>

or with a command line switch:

candle -dFoo=bar

Light, the linker has a similar scheme with a slightly different syntax: !(wix.Foo)—the command line is similar:

light -dFoo=bar

Localized strings use the !(loc.Foo) notation. The linker expects these strings to be provided in a .wxl localization file with the syntax:

<String Id="Foo" Overridable="yes">bar</String>

The localization file is also specified on the command line:

light -loc path

The main difference between localization and other string is the overridable nature of the former. While a library or extension module can have a default value for the string, it can be overridden late during the process of linking.

In order to assign any of these values to a regular Windows Installer property, use the Property tag:

<Property Id="Foo1" Value="$(var.Foo)" />
<Property Id="Foo2" Value="!(wix.Foo)" />
<Property Id="Foo3" Value="$(env.Foo)" />

Environment variables can be accessed in either tools using the $(env.Foo) syntax.

6.4 DDE connections

Shell connections using DDE (Dynamic Data Exchange) can be set up as follows:

<ProgId Id='Program.xyz' Description='Program handling .xyz' Advertise='yes'>
  <Extension Id='xyz' ContentType='text/sql'>
    <Verb Id='open' Sequence='1' Command='Open' Argument='"%1"'/>
  </Extension>
  <Extension Id='xyz0' ContentType='text/sql'>
    <Verb Id='open' Sequence='1' Command='Open' Argument='"%1"'/>
  </Extension>
</ProgId>

<Component Id='regSetup' Guid='YOURGUID-6D8A-4AE2-9D9F-3E074F13A029'>
  <Registry Root='HKLM' KeyPath='yes' Key='SOFTWARE\Classes\Program.xyz\shell\open\ddeexec'
    Type='string' Value='[\[]open("%1")[\]]' />
  <Registry Root='HKLM' Key='SOFTWARE\Classes\Program.xyz\shell\open\ddeexec\application'
    Type='string' Value='Program.xyz' />
  <Registry Root='HKLM' Key='SOFTWARE\Classes\Program.xyz\shell\open\ddeexec\topic'
    Type='string' Value='System' />
</Component>

6.5 Creating Directories

In some cases we have to create new directories without actually installing any file into them. Here is how:

<Directory Id="TARGETDIR" Name="SourceDir">
  <Directory Id="ProgramFilesFolder" Name="PFiles">
    <Directory Id="test" Name="test">
      <Component Id="test" Guid="YOURGUID-4884-4A01-AA04-84B92D222428"
        SharedDllRefCount="no" KeyPath="no" NeverOverwrite="no" Permanent="no" Transitive="no"
        Win64="no" Location="either">
        <CreateFolder/>
      </Component>
    </Directory>
  </Directory>
</Directory>

<Feature Id="test" Title="testfolder" Level="1">
  <ComponentRef Id="test"/>
</Feature>

6.6 Multi-media installations

We have already mentioned multi-media installations (where the files don't fit on a single CD, for instance) in the first lesson. You will need several Media tags in your source file to describe the separate physical media. Unlike with the earlier installations, you can't embed the file archives into the .msi file, of course. Also, you have to specify both a readable (and localizable, if necessary) prompt for the benefit of the user and a volume label (that has to match the actual volume label on the physical media). The installer will use this second to decide whether the user has inserted the expected media:

<Media Id='1' Cabinet='Sample1.cab' EmbedCab='no'
  DiskPrompt="CD-ROM #1" VolumeLabel="FOOBAR_DISK1" />

<Media Id='2' Cabinet='Sample2.cab' EmbedCab='no'
  DiskPrompt="CD-ROM #2" VolumeLabel="FOOBAR_DISK2" />

To craft the actual message asking the user to insert the correct media, Windows Installer requires a DiskPrompt property, too. You need to use a formatted string, [1] will be replaced with the contents of the DiskPrompt attribute of the corresponding Media tag:

<Property Id='DiskPrompt' Value="Acme's Foobar 1.0 Installation [1]" />

6.7 Add or Remove Programs Entries

In Control Panel > Add or Remove Programs, applications can have various entries like phone number and Internet contact information under the Click here for support information entry. To specify those, add any of the following properties to your source file:

<Property Id='ARPCOMMENTS'>any comments</Property>
<Property Id='ARPCONTACT'>contact info</Property>
<Property Id='ARPHELPLINK'>URL for technical support</Property>
<Property Id='ARPURLINFOABOUT'>URL for general info about the product</Property>
<Property Id='ARPURLUPDATEINFO'>URL for product updates</Property>
<Property Id='ARPHELPTELEPHONE'>URL for technical support</Property>
<Property Id='ARPREADME'>path</Property>
<Property Id='ARPSIZE'>app size in kilobytes</Property>

ARPSIZE seems to be superfluous at first sight. However, testing shows that Windows Installer reports a completely bogus value (over 4 GB) for very small packages. In this case, providing the package size manually can help overcome this annoying but harmless nuisance.

To specify the icon used to the left of the application entry, use a reference to a Binary/Id attribute (as we saw with the Shortcut tag, don't forget to append the same extension to the identifier, too). Also note that, as of now, there is a bug in Add or Remove Programs that keeps the icon from displaying after a per-user installation (ie. the ALLUSERS property set to 2):

<Property Id='ARPPRODUCTICON'>appicon.ico</Property>
...
<Icon Id="appicon.ico" SourceFile="Application.ico" />

A couple of other properties can control how the application behaves in Add or Remove Programs. To suppress the Modify button (or to force the installer into product removal instead of maintenance prior to Windows 2000), use:

<Property Id='ARPNOMODIFY'>1</Property>

To disable the Remove button (or to completely remove the application from this list prior to Windows 2000), use:

<Property Id='ARPNOREMOVE'>1</Property>

To remove the application altogether from this list on Windows 2000 or XP, you have to use this one instead:

<Property Id='ARPSYSTEMCOMPONENT'>1</Property>

To suppress the Repair functionality, use:

<Property Id='ARPNOREPAIR'>1</Property>

6.8 New User on the Block

The WiX toolset has additional libraries that allow the installer to perform additional tasks like adding a new user account:

<Component>
  <user:User Id='NewUser' Name='username' Password='password' />
</Component>

When you link the installation package, you have to link it against the appropriate WiX extension module:

light.exe -ext WixUtilExtension -out Sample.msi Sample.wixobj

This library also provides the means to create folder shares. Just put the following code fragment inside any Component to create a share on the folder the component installs into:

<user:User Id='Everyone' Name='Everyone' CreateUser='no' FailIfExists='no' RemoveOnUninstall='no' />
<user:FileShare Id='MainExecutableShare' Description='Foobar 1.0 share' Name='FoobarShare'>
  <user:Permission GenericRead='yes' ReadPermission='yes' Read='yes' GenericExecute='yes' User='Everyone' />
</user:FileShare>

The attributes of FileShare are self-documenting. To specify the permissions that go with the share, you need to use a Permission element and that in turn requires a User to be specified. We don't want to create a new user here (see CreateUser) nor to remove it when we uninstall our product (see RemoveOnUninstall). For the actual permissions, we can chose from a selection of attributes like Delete, Execute, Read, Write, GenericExecute, GenericRead, GenericWrite, TakeOwnership, ReadAttributes, WriteAttributes. See the help file to learn all available attributes.

Also note that you can use Permission tags inside CreateFolder elements as well. Some extra attributes (CreateChild, CreateFile, DeleteChild, Traverse) are reserved for this case.

6.9 Environmentally Friendly

To install environment variables, use the Environment tag in a component:

<Environment Id='UpdatePath' Name='PATH' Action='create' System='yes' Part='last' Value='[INSTALLDIR]' />

The Action attribute specifies what to do when the component is installed, its possible values are create, set and remove. The Part attribute governs the way the new value is assigned: all replaces the previous value with the new one, first prepends it to the original value, last appends to it. Permanent='yes' makes the environment variable stay when the product is removed, otherwise, it will be removed as well. System specifies whether the environment variable should be added to the system or the user environment space. Make sure you use all-uppercase names.

6.10 XML

More and more programs use XML-based configuration files these days instead of the older .ini style. WiX contains prefabricated custom actions to modify such files during installation and uninstallation. Let's suppose that we install a Settings.xml file together with our application. Originally, the file only contains the outermost tags:

<settings>
</settings>

We want the installer to add new nodes, one of those with attribute values:

<settings>
  <add key="a_key" value="a_value">key_item
    <inside>inside_item</inside>
  </add>
</settings>

To achieve this, we can use the XmlFile tag. Apart from its usual attributes like Id and File, its Action, Name and Value attributes will determine what to do in the XML file while the ElementPath specifies where to do it. This last attribute uses the standard XPath specification language to describe the XML node the actual operation will be applied to.

Action Name Value Operation
createElement name a new node will be created inside the one specified in ElementPath
setValue value the text value inside the node specified in ElementPath will be set
setValue name value the node specified in ElementPath will receive an attribute named name with a value of value
deleteValue the text value inside the node specified in ElementPath will be deleted
deleteValue name the attribute named name will be deleted from the node specified in ElementPath

So, to carry out the modifications we planned, we need the following entries (note that we have to provide our own sequencing, this is important to ensure that the changes will be removed in the proper reverse order upon uninstallation):

<Component Id='Settings' Guid='YOURGUID-574D-4A9A-A266-5B5EC2C022A4'>
  <File Id='XmlSettings' Name='settings.xml' DiskId='1' Source='settings.xml' Vital='yes' />
  <util:XmlFile Id='XmlSettings1' File='[INSTALLDIR]settings.xml'
    Action='createElement' Name='add' ElementPath='//settings' Sequence='1' />
  <util:XmlFile Id='XmlSettings2' File='[INSTALLDIR]settings.xml'
    Action='setValue' Name='key' Value='a_key' ElementPath='//settings/add' Sequence='2' />
  <util:XmlFile Id='XmlSettings3' File='[INSTALLDIR]settings.xml'
    Action='setValue' Name='value' Value='a_value' ElementPath='//settings/add' Sequence='3' />
  <util:XmlFile Id='XmlSettings4' File='[INSTALLDIR]settings.xml'
    Action='setValue' Value='key_item' ElementPath='//settings/add' Sequence='4' />
  <util:XmlFile Id='XmlSettings5' File='[INSTALLDIR]settings.xml'
    Action='createElement' Name='inside' ElementPath='//settings/add' Sequence='5' />
  <util:XmlFile Id='XmlSettings6' File='[INSTALLDIR]settings.xml'
    Action='setValue' Value='inside_item' ElementPath='//settings/add/inside' Sequence='6' />
</Component>

If there are several nodes of the same name in the XML file, we can use the usual XPath format of 'node[@attr="value"]' to specify which one we refer to. As ElementPath expects a formatted string, we have to escape the brackets with backslashes ([\[] and [\]]) to keep them from being evaluated as properties.

As this functionality resides in the standard utilities module, we have to link against that:

light.exe -ext WixUtilExtension -out Sample.msi Sample.wixobj

6.11 COM+ Applications

The WiX toolset has a custom action library that allows the installer to create COM+ packages, add components to them and create and set roles.

NOTE: If you want IntelliSense for the COM+ custom actions in Visual Studio you need to copy the file "src\ca\pubca\pcaext\Xsd\pubca.xsd" from the source download to "\Program Files\Microsoft Visual Studio 8\Xml\Schemas" for Visual Studio 2005 or "\Program Files\Microsoft Visual Studio .NET 2003\Common7\Packages\schemas\xml" for Visual Studio 2003.

When you build the installation package, you have to compile and link it against the appropriate WiX library. The candle and light command lines to do this are:

candle -ext WixComPlusExtension Sample.wxs
light -ext WixComPlusExtension Sample.wixobj

You also need to reference the COM+ schema in your WiX source as follows:

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:complus="http://schemas.microsoft.com/wix/ComPlusExtension">

To create a package (My COM+ Application) and add a standard COM DLL (MyCOM.dll) to the package:

<Component Id="MyCOM_dll" DiskId="1" Guid="YOURGUID-242F-4B82-BDC7-588E59E9F393">
  <File Id="MyCOM_dll" Name="MyCOM.dll" Source="MyCOM.dll" KeyPath="yes" Vital="yes" />
  <complus:ComPlusApplication Id="MyCOM" Name="My COM+ Application">
    <complus:ComPlusAssembly Id="MyComPlusAssembly" Type="native" DllPath="[#MyCOM_dll]">
      <complus:ComPlusComponent Id="MyCOM" CLSID="YOURCLSID-CA74-4DC7-BAEF-25AF03BC5F67" />
    </complus:ComPlusAssembly>
  </complus:ComPlusApplication>
</Component>

To create a package (My COM+ Application) and add a .Net assembly (MydotNet.dll) that is not in the GAC to the package:

<Component Id="MydotNet_dll" DiskId="1" Guid="YOURGUID-242F-4B82-BDC7-588E59E9F393">
  <File Id="MydotNet_dll" Name="MydotNet.dll" Source="MydotNet.dll" KeyPath="yes" Assembly="no" />
  <complus:ComPlusApplication  Id="MydotNet" Name="My COM+ Application">
    <complus:ComPlusAssembly Id="MyComPlusAssembly" DllPath="[#MydotNet_dll]" TlbPath="[#MydotNet_tlb]" Type=".net" RegisterInCommit="yes">
      <complus:ComPlusComponent Id="CheckInterface" CLSID="YOURCLSID-241E-4472-8C71-61A22BAF9914"/>
    </complus:ComPlusAssembly>
  </complus:ComPlusApplication>
</Component>

<Component Id="MydotNet_tlb" DiskId="1" Guid="YOURGUID-242F-4B82-BDC7-588E59E9F394">
  <File Id="MydotNet_tlb" Name="MydotNet.tlb" Source="MydotNet.tlb" KeyPath="yes" />
</Component>

To create a package (My COM+ Application) and add a .Net assembly (MydotNet.dll) that is in the GAC to the package:

<Component Id="MydotNet_dll" DiskId="1" Guid="YOURGUID-242F-4B82-BDC7-588E59E9F393">
  <File Id="MydotNet_dll" Name="MydotNet.dll" Source="MydotNet.dll" KeyPath="yes" Assembly=".net" />
  <complus:ComPlusApplication Id="MydotNet" Name="My COM+ Application">
    <complus:ComPlusAssembly Id="MyComPlusAssembly" TlbPath="[#MydotNet_tlb]" Type=".net" DllPathFromGAC="yes" RegisterInCommit="yes">
      <complus:ComPlusComponent Id="CheckInterface" CLSID="YOURCLSID-241E-4472-8C71-61A22BAF9914"/>
    </complus:ComPlusAssembly>
  </complus:ComPlusApplication>
</Component>

<Component Id="MydotNet_tlb" DiskId="1" Guid="YOURGUID-242F-4B82-BDC7-588E59E9F394">
  <File Id="MydotNet_tlb" Name="MydotNet.tlb" Source="MydotNet.tlb" KeyPath="yes" />
</Component>

Neil Sleightholm

6.12 Version by version

The InstallerVersion attribute of the Package tag describes which Installer version the installation package requires. In the tutorial, we have been using the value 100 (meaning version 1.0) because we wanted to show the lowest common denominator. The actual versions are shown in this table in connection with the corresponding Windows version:

Version Windows Features
1.x pre-XP Basic MSI support, 32-bit only
2.0 XP, 2000 Server SP3 64-bit support
3.0 XP SP2 improved patching
3.1 XP SP3, 2003 Server SP2 improved user interface
4.0 Vista, Server 2008 UAC, restart manager, MSI chaining
4.5 Vista and Server 2008 SP2 improved patching
5.0 Windows 7, 2008 Server R2 permissions configuration, improved service control, improved UI, per-user and per-machine installations no longe separated

As a general rule, we suggest specifying up to version 3.1 unless a later version is really called for. This version of Windows Installer is supported back to Windows 2000.

There are more detailed changelogs and descriptions at Microsoft. Those documents also note which packagea are not automatically updated with Windows Update but are available as redistributable downloads.

Copyright © 2004-2010, Gábor DEÁK JAHN, Tramontána
Comments and contributions are most welcome                   Comments and contributions are most welcome
A note: some people use a very braindead spam filtering idea actually generating extra amounts of spam: asking the sender to reply to an acknowledgment message in order to be placed on a whitelist and allowed through. All those who do so, unfortunately, will never receive a reply from me: I refuse this idea both in theory and practice.