Code Buckets

Buckets of code

Development Operations

Conditional Deployment with WIX

Often when deploying an application you need guaranteed access to a certain area of the file system. This can change depending on the OS. The challenge is to produce an msi that can detect that and supply the correct values.

Summary of WIX

Wix is an xml based tool used to generate msi files. To use

  1. Create your WIX file – MyMsi.wxs
  2. Run the tool candle.exe to generate the wixobj file
  3. Run the light.exe tool to generate the msi

WIX has the advantages of being

  1. Free
  2. Complete – does everything that I have asked of it
  3. Fairly well documented
  4. Command line runnable so it can be easily integrated into continuous builds

It’s not all sweetness and light though. I find handcrafting the XML hard going and a bit obscure. Projects such as WixSharp and WixEdit can take the sting out of it. However when going past the basics it’s often easier to start to handcraft the XML.

The Problem

Often when deploying an application you want an area with guaranteed access to a certain area of the file system. The ProgramData folder fits the bill nicely. This folder is used for application data that is not user specific so it’s a good candidate as an area for downloads and so forth from an installed application. Unfortunately many of us need to support older OS such as XP which don’t have the ProgramData folder. In this instance a good candidate could be

C:\Documents and Settings\All Users\Application Data

We install our application using an msi generated by WIX.  What I really don’t want to do is maintain more than one wix source file (.wxs). Really, in my life I don’t want to maintain more than one of anything (DVD player, car, daughter etc…) and I really don’t want to maintain more than one wxs file. So there needs to be a way of

  1. Detecting the OS version in WIX
  2. Using that information to change a property within the wxs file

WIX is nothing if not complete so there is such a way.

Detecting OS version

The operating system is exposed by the Window Installer Property – VersionNT. So

  1. Windows 2000 is 500
  2. Windows XP is 501
  3. Windows Vista 600

And so forth as detailed here.

So to detect Windows XP you would need to include a condition element such as

<Condition Message="This application is only supported on Windows Vista or higher.">
    <![CDATA[Installed OR (VersionNT > 500)]]>
</Condition>

Of course this blocks the install completely rather than changes a property so it’s only half the story.

Changing a Property Setting

To change a property conditionally on OS you need to reach for the SetPropertyElement. There are a few wrinkles with this

Action attribute

My first pass at this was

<Property Id="FolderPath" Value="C:\DefaultPath" />
<SetProperty Id="FolderPath" Value="C:\XPPath">
  <![CDATA[Installed OR (VersionNT = 501)]]>
</SetProperty>
<SetProperty Id="FolderPath"  Value="C:\StandardPath">
  <![CDATA[Installed OR (VersionNT > 501)]]>
</SetProperty>

So you have a property. The two set property elements target the property element because of its common id. However if you run this then you get this error

error CNDL0045 : The SetProperty element's After or Before attribute was not found; one of these is required when attribute Id is present.

So you need to deploy the Action attribute defined in the WIX documentation as

By default the action is “Set” + Id attribute’s value. This optional attribute can override the action name in the case where multiple SetProperty elements target the same Id (probably with mutually exclusive conditions).

So the wix elements become

<Property Id="FolderPath" Value="C:\DefaultPath" />
<SetProperty Id="FolderPath" Value="C:\XPPath" Action="Action1">
  <![CDATA[Installed OR (VersionNT = 501)]]>
</SetProperty>
<SetProperty Id="FolderPath" Value="C:\StandardPath" Action="Action2">
  <![CDATA[Installed OR (VersionNT > 501)]]>
</SetProperty>

Which gets over that issue

Before and After Attributes

Unfortunately making the installer with the revised XML generates this error

error CNDL0045 : The SetProperty element's After or Before attribute was not found; one of these is required when attribute Id is present.

Which has the merit of being straightforward. It needs to know when in the install sequence the property should be set. The windows installer has a sequence of standard actions detailed here for example

  • InstallFiles
  • InstallExecute
  • InstallFinalise

And many others. (To me) it makes sense to set the property just before the files installs so …

Before="InstallFiles"

And the WIX XML now becomes

<Property Id="FolderPath" Value="C:\DefaultPath" />
<SetProperty Id="FolderPath" Value="C:\XPPath" Action="Action1" Before="InstallFiles">
  <![CDATA[Installed OR (VersionNT = 501)]]>
</SetProperty>
<SetProperty Id="FolderPath" Value="C:\StandardPath" Action="Action2" Before="InstallFiles">
  <![CDATA[Installed OR (VersionNT > 501)]]>
</SetProperty>

Equally the After attribute can be used to set the properties after the InstallFiles install action.

After="InstallFiles"

SequenceType Attributes

Now when we make the WIX file we now get a more cryptic error

error LGHT0094 : Unresolved reference to symbol 'WixAction:InstallUISequence/InstallFiles' in section 'Product:{BF945826-8B20-42F7-96FD-6153F0BE235C}'.

It turns out that there are 2 install sequences

  1. UI
  2. Execute

Looking again at this standard action table you can see there are two sections

  1. Wizard Dialog Stage (UI)
  2. Install Execution Stage (Execute)

The InstallFiles installer action only exists on the execute sequence and the WIX compiler needs to know that. So you now reach for the Sequence attribute and the WIX XML becomes

<Property Id="FolderPath" Value="C:\DefaultPath" />
<SetProperty Id="FolderPath" Value="C:\XPPath" Action="Action1" Before="InstallFiles" Sequence="execute">
  <![CDATA[Installed OR (VersionNT = 501)]]>
</SetProperty>
<SetProperty Id="FolderPath" Value="C:\StandardPath" Action="Action2" Before="InstallFiles" Sequence="execute">
  <![CDATA[Installed OR (VersionNT > 501)]]>
</SetProperty>

It now compiles and creates an msi that behaves differently on XP and later versions of windows. Job done!!

Uses

Once you have the flexible property it can be used to redirect output to a different location or used as a source for a config file transform such as

<util:XmlFile Id="UpdateVersionsFolder" Action="setValue" File="[#File.app.config]" SelectionLanguage="XPath" Permanent="yes" ElementPath="/configuration/appSettings/add[\[]@key='AppSettingKey'[\]]" Name="value" Value="[ FolderPath]" />

Which can be very useful.

So well done if you read this far. All the documentation exists on the internet but I found it puzzling so I hope this might help in a general demystification effort.

Useful links

About the ProgramData folder
http://stackoverflow.com/questions/9518890/what-is-the-significance-of-the-programdata-folder-in-windows

WIX documentation
http://wixtoolset.org/documentation/

Windows Installer Operating System Property Values
https://msdn.microsoft.com/en-us/library/windows/desktop/aa370905(v=vs.85).aspx

Windows Installer Standard Actions
http://www.advancedinstaller.com/user-guide/standard-actions.html

WixSharp
https://wixsharp.codeplex.com/
A C# API for WIX. Good for generating a base XML file to start with. Not complete though so will need to fall back to direct XML editing for more advanced scernarios

WixEdit
http://wixedit.sourceforge.net/
A simple UI for the XML file. Useful.

1 COMMENTS

LEAVE A RESPONSE

Your email address will not be published. Required fields are marked *