Alex Shevchuk

Always listen to experts. They’ll tell you what can’t be done, and why. Then do it. - Lazarus Long

From MSI to WiX, Part 3 - Launch Conditions and Application Search

From MSI to WiX, Part 3 - Launch Conditions and Application Search

  • Comments 18
  • Likes

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

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="http://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
  • PingBack from http://victorsergienko.com/wix-net-linkdump-samples-howto-s/

  • 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?

  • Sorry, I don't know the answer.  Are you using MSI API to get OS info or just regular Win32 API?  This error indicates that some MSI API call was in use that required costing to be completed.  Also, you might want to get a log file which may help you in identifying the problem.  Obviously, your custom action must put detailed log info into log.

    Also, is your custom action is scheduled in both UI and execute sequence tables?  When you install your application, do you use the same UI level in all three OS's?

  • I'm sorry but this does not work

    Property Id='TESTAPP' Value='[ProgramFilesFolder]XYZ'>

    Regards

    Friedrich

  • 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='SOFTWARE\Microsoft\.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>

  • 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?

  • Search for ADDLOCAL/ADDSOURCE properties.

  • 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.

    thanks

  • 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.

    Alex

  • 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="SOFTWARE\TortoiseSVN" Name="Directory" Type="directory" Id="TortoiseDirectory" Win64="yes"/>

    </Property>

    <CustomAction Id="ISTORTOISEINSTALLED" Error="[TORTOISENOTINSTALLED]"/>

    <InstallUISequence>

           <Custom Action="ISTORTOISEINSTALLED" After="FindRelatedProducts">TORTOISEINSTALLED&lt;&gt;""</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,

    Amir

  • Never mind got it working

    Thanks :)

  • Hi Alex,

    my application needs to have .netframework2.0 and MDAC2.8.

    i'm using launch conditions to check for these components and installing using installUrl property.

    But my problem is,

    if my target machine don't have these two components, my installer starts installing .netframework2.0 and after installing framework2.0 it is not directly installing MDAC2.8.Again i need to click on installer to install the second component.

    is there any way to install all the prerequisites on single click?

  • 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

    Ferdin

  • Hi Ferdin,

    You need a bootstrapper:

    http://wix.mindcapers.com/wiki/Bootstrapper

    Alex

  • Hi

    Is these any way launch condition shouldn’t evaluate at uninstall?

    Thank you,

    Padma.

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment