From MSI to WiX, Part 3 - Launch Conditions and Application Search
The main page for the series is here.
Introduction
Before we start with Launch Conditions and Application Search let's take a look at the sequence of actions Windows Installer executes during installation. You can find the suggested sequence for InstallExecuteSequence table here.
Basically, what this table is saying is that in order to install any product, Windows Installer is executing the following actions in following order (the list here is high-level and does not include all standard actions):
- LaunchConditions - Evaluates conditional statements from LaunchCondition table and if any of them fails, displays an error message associated with the launch condition and terminates the installation.
- AppSearch - Looks for already installed products on the target system. It uses AppSearch, Signature, CompLocator, DrLocator, RegLocator, and IniLocator tables to search for installed component, existing directory or file, registry entry, or Ini file entry. If installation is dependent on previously installed software, AppSearch must be scheduled before LaunchConditions action.
- File Costing - includes CostInitialize, FileCost, CostFinalize, and InstallValidate actions. This process determines the total disk space required for the installation. InstallValidate action will terminate the installation if any of the disks does not have enough of free disk space.
- InstallInitialize - starts the sequence of actions which will change the system, i.e. install files, create directories, registry entries, etc. Up untill InstallFinalize action, all changing system actions will be written into installation script and will not actually change the system.
- Actions which install components on the target system and register the installed package with the system..
- InstallFinalize - marks the end of transaction started with the InstallInitialize action. This action runs the installation script which actually updates the system.
Looking at this sequence we can say that purpose of Launch Conditions and Application Search is just to find an answer on this question: "Can we even start the installation of our product on the target system?".
Launch Conditions
In MSI database launch conditions are represented by records in the LaunchCondition table. This table has two columns. Condition column contains an expression which must evaluate to True for installation to continue. Description column is a localizable text, Windows Installer displays when the condition fails.
In WiX, Launch Conditions are represented as one or more <Condition> elements. Use Message attribute for the localizable error message and inner text for the condition expression.
Important: The <Condition> element in WiX is an overloaded element. It can have different parent elements, but only if the parent element is <Product> or <Fragment>, <Condition> element represents a row in the LaunchCondition table.
Condition Expressions
In launch conditions we can use properties and environment variables in the condition expressions. If identifier is prefixed with the % symbol, it means environment variable. Unprefixed identifiers are properties from the Property table.
There are two types of properties: User defined properties and Windows Installer predefined properties. Windows Installer predefined properties are created by Windows Installer engine when installation starts up. Not all of the predefined properties will have the same value at the time when LaunchConditions action is executed and when installation will actually starts and because of that those properties cannot be reliably used in laucn conditions.
Condition expression syntax is described in great details here.
Useful properties for launch coditions
- Privileged, AdminUser and MSIUSEREALADMINDETECTION properties. Normally you want to use Privileged property to check if the user is Admin.
- Installed property - To differentiate between first install and maintenance mode.
- MsiNetAssemblySupport property - If this property is not set, system does not have .NET Framework installed. Otherwise, this property is set to the version number of fusion.dll. Can be used to check if certain version or later of the .NET Framework is installed on the target system.
- To check the type of the operating system installed on the target system:
- MsiNTProductType
- MsiNTSuiteBackOffice
- MsiNTSuiteDataCenter
- MsiNTSuiteEnterprise
- MsiNTSuitePersonal
- MsiNTSuiteSmallBusiness
- MsiNTSuiteSmallBusinessRestricted
- MsiNTSuiteWebServer
- MsiTabletPC
- RemoteAdminTS
- TerminalServer
- ServicePackLevel
- ServicePackLevelMinor
- Version9X
- VersionNT
- VersionNT64
- WindowsBuild
Samples
<
Condition Message='.NET Framework 2.0 must be installed prior to installation of this product.'>
MsiNetAssemblySupport >= "2.0.50727"
</Condition>
<
Condition Message="Web Edition of Windows Server 2003 is required.">
MsiNTSuiteWebServer
</Condition>
<
Condition Message="NODEFAULTVALUE variable must be set in the command line">
Installed OR NODEFAULTVALUE
</Condition>
Using WixNetFxExtension library
In order to determine if particular version of .NET Framework is installed on the target system we can use the WixNetFxExtension library. More information on how to use it can be found at wixwiki, but basic steps are:
Add property reference link to a property from the WixNetFxExtension library using <PropertyRef> element:
<PropertyRef Id="NETFRAMEWORK20"/>
Add the condition using referenced property:
<Condition Message="The .NET Framework 2.0 must be installed">
Installed OR NETFRAMEWORK20
</Condition>
Add the extension to candle and light:
candle.exe Minimal.wxs -ext "Microsoft.Tools.WindowsInstallerXml.Extensions.NetFxCompiler, WixNetFxExtension"
light.exe -out Minimal.msi Minimal.wixobj -ext "Microsoft.Tools.WindowsInstallerXml.Extensions.NetFxCompiler, WixNetFxExtension" d:\wix\netfx.wixlib
Here is the MSBuild project file:
<Project DefaultTargets="Build" xmlns="https://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<!-- Required by WiX -->
<!-- Path and name of the output without extension -->
<OutputName>Minimal</OutputName>
<!-- What need to be built -->
<OutputType Condition="$(OutputType)==''">package</OutputType>
<!-- The path to the WiX installation -->
<ToolPath>d:\WIX\</ToolPath>
<!-- Input path to source files.
If not passed, assumes the same folder where project file is located. -->
<BaseInputPath Condition="$(BaseInputPath)==''">$(MSBuildProjectDirectory)\</BaseInputPath>
<!-- Create a compiled output in the folder where project is located -->
<OutputPath Condition="$(OutputPath)==''">$(MSBuildProjectDirectory)\</OutputPath>
<!-- Add missing trailing slash in paths -->
<ToolPath Condition="!HasTrailingSlash('$(ToolPath)') ">$(ToolPath)\</ToolPath>
<BaseInputPath Condition="!HasTrailingSlash('$(BaseInputPath)') ">$(BaseInputPath)\</BaseInputPath>
<OutputPath Condition="!HasTrailingSlash('$(OutputPath)') ">$(OutputPath)\</OutputPath>
</PropertyGroup>
<!-- Candle.exe command-line options -->
<ItemGroup>
<CompileExtension Include="WixNetFxExtension">
<Class>Microsoft.Tools.WindowsInstallerXml.Extensions.NetFxCompiler</Class>
</CompileExtension>
</ItemGroup>
<!-- Light.exe command-line options -->
<ItemGroup>
<LinkExtension Include="WixNetFxExtension">
<Class>Microsoft.Tools.WindowsInstallerXml.Extensions.NetFxCompiler</Class>
</LinkExtension>
<WixLibrary Include="$(ToolPath)netfx.wixlib"></WixLibrary>
</ItemGroup>
<Import Project="$(ToolPath)wix.targets"/>
<!-- List of files to compile -->
<ItemGroup>
<Compile Include="$(BaseInputPath)Minimal.wxs"/>
</ItemGroup>
</Project>
Important
Launch Conditions do not guarantee the order in which conditions will be evaluated. Here is the excerpt from the Remarks section of LaunchCondition table reference:
"You cannot guarantee the order in which the launch conditions are evaluated by authoring this table. If it is necessary to control the order in which conditions are evaluated, you should do this by using Custom Action Type 19 custom actions in your installation."
This is even more important when we do Application Search before evaluating launch conditions because, for example, some registry keys can be accessed by admins only.
To do the check if user is an admin before Application Search and evaluating launch conditions add the declaration of custom action:
<CustomAction Id='IsPrivileged' Error='You must be an admin to install this product' />
Schedule custom action:
<
InstallExecuteSequence>
<Custom Action='IsPrivileged' Before='LaunchConditions'>
Not Privileged
</Custom>
</InstallExecuteSequence>
Application Search
As we mentioned already in the Introduction to this post, AppSearch standard action is using AppSearch, Signature, CompLocator, DrLocator, RegLocator, and IniLocator tables to search for installed component, existing directory or file, registry entry, or Ini file entry.
The idea here is that when Windows Installer finds an installed component, existing directory or file, registry key or value, or entry in the Ini file, it will set provided in the request property to the found value. If search failed, property will remain undefined or it will keep its original value.
In WiX we are using the following elements to do the application search:
- <Property>: Property to set after successful search.
- <ComponentSearch>: Search for installed component. If component is installed, search continues for the file or directory which is the KeyPath of the found component.
- <RegistrySearch>: Search for directory or file if registry value points to a directory or file, or search for a registry value itself.
- <IniFileSearch>: Search for directory or file if Ini file entry points to a directory or file, or search for a Ini file entry itself.
- <DirectorySearch>: Search for directory or file in that directory.
- <FileSearch>: Search for the file. This entry creates a record in the Signature table.
Samples
Find if .NET Framework 2.0 is installed (from NetFxExtension.wxs):
<Property Id="NETFRAMEWORK20">
<RegistrySearch Id="NetFramework20" Root="HKLM" Key="Software\Microsoft\NET Framework Setup\NDP\v2.0.50727" Name="Install" Type="raw" />
</Property>
Find if component from another application is installed (from VSExtension.wxs):
<Property Id="VS2005PROJECTAGGREGATOR2">
<ComponentSearch Id="VS2005ProjectAggregator2Search" Guid="B0BB80E0-5CCC-474E-A75E-05DC1AE073BC" />
</Property>
We can also define a default value for the property in case search is failed. For example, this search will succeed. Installing this installation package will result in the message box with the message similar to "C:\Windows\Microsoft.NET\Framework\" and then installation will be aborted.
<
Property Id='TESTAPP'>
<RegistrySearch Id='TestAppRegSearch'
Root='HKLM'
Key='SOFTWARE\Microsoft\.NETFramework'
Name='InstallRoot'
Type='raw' />
</Property>
<
CustomAction Id='ShowTESTAPP' Error='[TESTAPP]' />
<
InstallExecuteSequence>
<Custom Action='ShowTESTAPP' Before='InstallInitialize' />
</InstallExecuteSequence>
Now, let's change the name of the registry value to point to a value which does not exist, for example, InstallRoot2. We can also add the Value attribute to the <Property> element to define the default value for the property. Without the Value attribute TESTAPP property will be undefined.
<
Property Id='TESTAPP' Value='XYZ'>
<RegistrySearch Id='TestAppRegSearch'
Root='HKLM'
Key='SOFTWARE\Microsoft\.NETFramework'
Name='InstallRoot2'
Type='raw' />
</Property>
<
CustomAction Id='ShowTESTAPP' Error='[TESTAPP]' />
<
InstallExecuteSequence>
<Custom Action='ShowTESTAPP' Before='InstallInitialize' />
</InstallExecuteSequence>
Now message box will show "XYZ" text.
Default value can also be set to a value of another property:
<Property Id='TESTAPP' Value='[ProgramFilesFolder]XYZ'>
Whats next
Next time we will discuss components and component rules.
Comments
Anonymous
March 20, 2008
PingBack from http://victorsergienko.com/wix-net-linkdump-samples-howto-s/Anonymous
May 21, 2008
Hi Alex, I'm trying to get a CustomAction running before LaunchConditions (it retrieves som system info I want to check). This works on Vista and XP but not on a Server 2003 SP1. The error is 2731 Selection Manager not initialized. (I read somewhere that initialization occurs in the file costing). To be specific the msiexec versions are Vista: V 4.00.6001.0 XP: V 3.01.4000.1823 Server 2003: V 3.01.4000.3959 Is a there a know difference between 2003 and the other two OS versions as regards this?Anonymous
May 21, 2008
The comment has been removedAnonymous
June 13, 2008
I'm sorry but this does not work Property Id='TESTAPP' Value='[ProgramFilesFolder]XYZ'> Regards FriedrichAnonymous
June 25, 2008
Strange. It does work for me. Here is sample project: <?xml version="1.0" encoding="UTF-8"?> <Wix xmlns="http://schemas.microsoft.com/wix/2003/01/wi"> <Product Id="{EF2D46DE-0A93-4ee2-9FC8-38AFA0052845}" Name="Your Product" Language="1033" Version="0.0.0.0" Manufacturer="Your Company"> <Package Id="????????-????-????-????-????????????" Description="Description of your product" Comments="This will appear in the file summary stream." InstallerVersion="200" Compressed="yes" /> <Media Id="1" Cabinet="Product.cab" EmbedCab="yes" /> <Directory Id="TARGETDIR" Name="SourceDir"> <Directory Id="ProgramFilesFolder"> <Directory Id="INSTALLLOCATION" Name="MyAppDir" LongName="My Application Directory"> <Component Id="ProductComponent" Guid="{6B74BE11-30D2-4f67-A5A9-956C5FF13F52}"> <!-- TODO: Insert your files, registry keys, and other resources here. --> </Component> </Directory> </Directory> </Directory> <Property Id='TESTAPP' Value='[WindowsFolder]XYZ'> <RegistrySearch Id='TestAppRegSearch' Root='HKLM' Key='SOFTWAREMicrosoft.NETFramework' Name='InstallRoot2' Type='raw' /> </Property> <CustomAction Id='ShowTESTAPP' Error='[TESTAPP]' /> <InstallExecuteSequence> <Custom Action='ShowTESTAPP' Before='InstallInitialize' /> </InstallExecuteSequence> <Feature Id="ProductFeature" Title="Feature Title" Level="1"> <ComponentRef Id="ProductComponent" /> </Feature> </Product> </Wix>Anonymous
August 15, 2008
I have a wix project that has 2 features and is working the way I want from the UI. What I need is a way for someone to select features from the commandline without using a transform. Can this be done by setting properties and using condition statements?Anonymous
August 15, 2008
Search for ADDLOCAL/ADDSOURCE properties.Anonymous
September 04, 2008
Hi Alex, I'm using setup and deployment project to create setup for my vb.net windows application. while deploying the application on target machine i need to check the user permissions. if the user is administrator then it should install. if not the installation should terminate. please help me to achieve this. thanksAnonymous
September 06, 2008
Hi Ram, Best practice for checking if user has administrative privileges is to use Custom Action Type 19: <InstallExecuteSequence> <Custom Action='IsPrivileged' Before='LaunchConditions'> Not Privileged </Custom> </InstallExecuteSequence> You need to schedule it in both InstallExecuteSequence and InstallUISequence. Keep in mind that on Vista with UAC on, Privileged may be defined even if user is not an Admin. AlexAnonymous
November 24, 2008
Hi Alex, Great article, i am working on a local app for our company and i am having a bit of a problem implementing AppSearch codes, here is my xml, any comments? <Property Id="TORTOISENOTINSTALLED">Could not find TortoiseSVN.</Property> <Property Id="TORTOISEINSTALLED"> <RegistrySearch Root="HKLM" Key="SOFTWARETortoiseSVN" Name="Directory" Type="directory" Id="TortoiseDirectory" Win64="yes"/> </Property> <CustomAction Id="ISTORTOISEINSTALLED" Error="[TORTOISENOTINSTALLED]"/> <InstallUISequence> <Custom Action="ISTORTOISEINSTALLED" After="FindRelatedProducts">TORTOISEINSTALLED<>""</Custom> </InstallUISequence> now when i got to HKLM\SOFTWARE\TortoiseSVN and change it to HKLM\SOFTWARE\TDDDDDDDDDDDDDDDDDDDortoiseSVN TORTOISEINSTALLED seems to be returning ":1" any help is greatly appreciated Thanks, AmirAnonymous
November 24, 2008
Never mind got it working Thanks :)Anonymous
January 19, 2009
The comment has been removedAnonymous
February 27, 2009
Hi I am a newbie to Wix and i'm using .NET 2.0, Visual Studio 2005, Wix 2.0. I need to check in the client machine if .NET 2.0 or later versions are installed if not it should start the installation of .NET 2.0. I tried many but nothing works it detects .NET 2.0 is not installed and winds up the installation. Is there any ideas which you could suggest me? Thanks in advance FerdinAnonymous
February 27, 2009
Hi Ferdin, You need a bootstrapper: http://wix.mindcapers.com/wiki/Bootstrapper AlexAnonymous
March 18, 2009
Hi Is these any way launch condition shouldn’t evaluate at uninstall? Thank you, Padma.Anonymous
May 13, 2010
One of the examples you give is <Condition Message='.NET Framework 2.0 must be installed prior to installation of this product.'> MsiNetAssemblySupport >= "2.0.50727" </Condition> However, the documentation on conditional statements which you link to states that there is no support for comparing versions. Is this a brittle check which would break when .Net 10 is released?Anonymous
May 13, 2010
MSI will compare strings, so "2.0.50727" will be greater than "1.1.4322" (.NET 1.1). As far as .NET 10, try this program in C#: static void Main(string[] args) { string v2 = "2.0.50727"; string v10 = "10.0.45786"; Console.WriteLine(string.CompareOrdinal(v2, v10)); Console.ReadLine(); } If result is greater than 0, condition still works.Anonymous
September 22, 2010
Hi Alex, I need to check windows version and copy two DLLs if the OS is Windows Server 2008. I am confused as to whats the best place to do the check and copy the files? Can you please help? Thanks! Keyur