Posts tagged ‘WIX’

Understanding Conditions in Custom Actions and InstallExecuteSequence

If you have ever had to deal with Custom Actions and their Conditions in InstallExecuteSequence, you can get frustrated really quickly. This is not intuitive, though not hard to learn. It definitely is one area that Microsoft could have designed much better, but it is what it is.

The first problem with the design is that it is hard to just read a little bit, and understand. So take a moment and read this all, so you can understand it all.

What is a Feature? What is a Component?

A Feature is a usually a group of Components. The Feature is a part of a product you want to install, at a high level. A Component is a list of install tasks to implement a feature.

Lets look at a use case using a Client/Server application.

Think of an application that has a Server piece, a GUI to manage the server, and a client piece.  When you install, the MSI gives you a check box with those three items and you can install any of the three, or all three on the same machine.  You have three Features.

Feature_1_Server
Feature_2_User_Interface
Feature_3_Client

A Component is a lower lever.  A feature is a list of Components.  So Feature_1_Server might install a list of files and a web service.

C:\Program Files\MyApp\Server\Server.exe
C:\Program Files\MyApp\Common\Common.dll
C:\inetpub\wwwroot\MyApp\Server\WebService.asmx

A Component can only add files to a single path. So because we have two directories above, there will be at least three Components. But we also need to create the web service, so this one Feature has four Components.

Component_1_Server_Files
Component_2_Common_Files
Component_3_WebService_Files
Component_4_WebService

Now if you install Feature_2_User_Interface, you get these files:

C:\Program Files\MyApp\UI\Server.exe
C:\Program Files\MyApp\Common\Common.dll

Notice that the same files in Common are included.  You don’t need to create a new Component for that.

Component_2_Common
Component_5_User_Interface

Install the Feature_3_Client and you see it is similar in the files it installs.

C:\Program Files\MyApp\Client\Client.exe
C:\Program Files\MyApp\Common\Common.dll

Notice that the same files in Common are included.  You don’t need to create a new Component for that.

Component_2_Common
Component_6_Client

Ok, so you have three features, and six components.

What are Conditions?

A Condition is a boolean expression added to a Custom Action to make sure it only runs if the expression returns true.

Imagine you are installing software that has a Server and a Client Feature.  You have a Custom Action that should run for the Server but not for the client.  It needs a condition that follows the logic in the following sentence.

If I am installing the Server Feature, run this action, otherwise, don’t run this action.

Unfortunately MSI doesn’t speak English so while the above sentence helps you understand what needs to be done, the Condition syntax is quite different.

Condition Expression Syntax

The expression is usually in this syntax:

[$|?|&|!][Component|Feature] [bool operator] [-1|1|2|3|4]

Example: If I am installing the Server Feature, run this action, otherwise, don’t run this action.

&Feature_1_Server = 3

Wow, I bet that unless you have some serious experience with MSI Conditions, you are looking at that syntax and shaking your head in confusion. I must admit that I would never have designed this the way it is and I consider it poor design, but again, it is what it is and if we are going to use MSIs, we need to learn this.

Lets break this out into four understandable parts.

  1. [Feature/Component Operator]
  2. [Feature/Component Id]
  3. [Boolean Operator]
  4. [Integer representation of a State]

Let’s cover the four different parts of this syntax.

What is the Feature/Component Operator?

There are only four Feature/Component Operators, all show in the table below.  There are a pair of operators for Features and a pair of operators for Component. So while there are four operators, only three things are determined.

  1. Is this is a Feature of a Component
  2. What is the current *State of the Component or Feature.
  3. What will be the new *State of the Component of Feature when the install completes.

* State is discussed later as it is the fourth item in the Condition syntax but very quickly, State can basically be understood as installed or not installed.

OperatorTypeState or ActionDescriptionWhere this syntax is validHow is this commonly used?
$ComponentComponent – Gets the action that will occur.This is used in front of a Component to get the State change that will occur to the component. Returns a the new State.In the Condition table, and in the sequence tables, after the CostFinalize action.Use this to see if a Component will be installed or uninstalled.
?ComponentComponent – Gets the current state.This is used in front of a Component to get the current state of that Component. Returns the current State.In the Condition table, and in the sequence tables, after the CostFinalize action.Use this to see if a Component is already installed or has not been installed (absent) or was uninstalled (absent).
&FeatureFeature – Gets the action that will occur.This is used in front of a Feature to get the State change that will occur to the Feature. Returns a the new State.In the Condition table, and in the sequence tables, after the CostFinalize action.Use this to see if a Feature will be installed or uninstalled.
!FeatureFeature – Gets the current state.This is used in front of a feature to get the current State of that feature. Returns the current State.In the Condition table, and in the sequence tables, after the CostFinalize action.Use this to see if a Feature is already installed or has not been installed (absent) or was uninstalled (absent).

$ and & are doing the same task, only for $ is for Components and & is for Features.  These operators are used before a Feature/Component Id to determine the new State, or more clearly, is this Feature/Component going to be installed or uninstalled.

? and ! also do the same task, again the difference is that ? is for Components and ! is for Features.  These operators are used before a Feature/Component Id to determine the current State, or more clearly, is this Feature/Component already installed or not installed.

What is the Feature/Component ID?

The Feature or Component Id is a unique string. In WIX, you create the Features and Components, so you should easily know the ID.  I like to use understandable Feature/Component names, such as Feature_1_Server and Feature_3_Client or Component_1_Server_Files and Component_6_Client_Files.

What is the Boolean Operator?

The boolean operator is any operator that when used in an expression leads to returning true or false. There are quite a few boolean operators that can be used.

  • Comparative Operators
  • Substring Operators
  • Bitwise Numeric Operators

Comparative Operators

There are the basic boolean operators that all languages have.  One difference is that in most languages, = is an assignment operator and == is comparison, but since there is no assignment like that when dealing with Features/Components and States, the = is the comparison operator and == is never used.

OperatorReturn Value
=Returns TRUE if values on both sides are equals.
<>Returns TRUE if the value on the left side is not equal to the value on the right side.
>Returns TRUE if the value on the left side is greater than the value on the right side.
>=Returns TRUE if the value on the left side is greater than or equal to the value on the right side value.
<Returns TRUE if the value on the left side is less than the value on the right side.
<=Returns TRUE if the value on the left side is less than or equal to the value on the right side.

Substring Operators

OperatorMeaning
><TRUE if left string contains the right string.
<<TRUE if left string starts with the right string.
>>TRUE if left string ends with the right string.

Bitwise Numeric Operators

OperatorMeaning
><Bitwise AND, TRUE if the left and right integers have any bits in common.
<<True if the high 16-bits of the left integer are equal to the right integer.
>>True if the low 16-bits of the left integer are equal to the right integer.

Any of the above operators can be used.

What is a State?

Basically you can think of State was an integer represented Installed or Not Installed.  There are actually five options but the often only two are used:

  • INSTALLSTATE_ABSENT (2) – Means it is Not Installed.
  • and INSTALLSTATE_LOCAL (3) – Means it is Installed.
StatesValueMeaning
INSTALLSTATE_UNKNOWN-1No action to be taken on the feature or component.
INSTALLSTATE_ADVERTISED1Advertised feature. This state is not available for components.
INSTALLSTATE_ABSENT2Feature or component is not present. It either has never been installed, or if it was once installed it has been uninstalled.
INSTALLSTATE_LOCAL3Feature or component on the local computer. This feature or component is currently installed on the local computer.
INSTALLSTATE_SOURCE4Feature or component run from the source. This feature or component is installed, but files are not local, but on the source. So running this feature may require a mapped drive to the original installation source.

Examples

So now that we understand the parts, lets put it all together with these Examples.

ConditionDefinitionHow is this commonly used?
!MyFeature = 2If MyFeature is not there (Absent)Use this to check if a Feature is not currently installed. It may have never been installed, or it may have been once installed but is now uninstalled. Both never installed and uninstalled result in the term Absent.
!MyFeature = 3If MyFeature is currently installedUse this on uninstall or upgrade or repair to see if a Feature is already installed.
&MyFeature = 2If MyFeature is to not be installed (remain absent)Use this to check if a Component is not currently installed. It may have never been installed, or it may have been once installed but is now uninstalled. Both never installed and uninstalled result in the term Absent.
&MyFeature = 3If MyFeature is to be installedUse this on uninstall or upgrade or repair to see if a Component is already installed.

Component examples would be similar to these Feature examples.

Now that you are this far.  Guess what.  You can combine these two Conditions. Maybe I will post about combining conditions later.

Resources

http://msdn.microsoft.com/en-us/library/aa368012%28v=VS.85%29.aspx

WIX: Creating an MSI and Deploying Your First File

Windows Install XML (WIX) is a nice simple way to create an MSI installation file.  Some of the items it automatically handles, such as registering with Add / Remove Programs, are very nice features.

In this example we are going to create a simple MSI that deploys a single file.

Prequisites

Step 1 – Create a WIX project in Visual Studio

Creating a new project is a very simple task once you have done it a few times.  However, I try to make my walk-thrus newbie proof, so even some one who has never done this feels comfortable. So if you need help with this step, use my instructions below.  If you don’t, skip them.

  1. Open Visual Studio if it is not already open.
  2. Got to File | New | Project.
  3. You should have an option under Installed Templates for Windows Install XML. Select it.
  4. Now you should see the option for Setup Project.  Select it.
  5. Enter a Name for the project. I called the project I made for this walk-thru DeployOneFile.
  6. Change the directory to store the project if you want.  It doesn’t matter what directory you choose, but it is nice to keep your learning projects organized.
  7. Click OK.

Your project should now be created.  In solution explorer you should now have a solution, a project, a reference and the Product.wxs.  See the image below.

Ok, I hope that was easy for you.  Let move on.

Step 2 – Add a file to the project

These steps are preformed in Visual Studio on the project you just created in the above step.

  1. Right-click on the project name, DeployOneFile, and from the drop down, choose Add | New Item.
  2. Select Text File.
  3. Name it whatever you want. I  used Install.conf for this example.
  4. Click Add.

Your file is now added to the Visual Studio project.  However, it is not automatically added into the Product.wxs as a file to be installed. This is done manually in the next step.

Step 3 – Take a moment to learn

We are going to change the Product.wxs file to include the file we just created.

The Product.wxs file has the following XML text in it.  Take a moment to look at the XML nodes and their elements so are familiar with the syntax it is using.

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
	<Product Id="fae97512-4eca-4271-821d-75b7a8861557" Name="DeployOneFile" Language="1033" Version="1.0.0.0" Manufacturer="DeployOneFile" UpgradeCode="fec17f7b-060e-466f-8bd2-7895eb2b92ce">
		<Package InstallerVersion="200" Compressed="yes" />

		<Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />

		<Directory Id="TARGETDIR" Name="SourceDir">
			<Directory Id="ProgramFilesFolder">
				<Directory Id="INSTALLLOCATION" Name="DeployOneFile">
					<!-- TODO: Remove the comments around this Component element and the ComponentRef below in order to add resources to this installer. -->
					<!-- <Component Id="ProductComponent" Guid="927a42ea-a6df-45f6-ac52-4b979194bc10"> -->
						<!-- TODO: Insert files, registry keys, and other resources here. -->
					<!-- </Component> -->
				</Directory>
			</Directory>
		</Directory>

		<Feature Id="ProductFeature" Title="DeployOneFile" Level="1">
			<!-- TODO: Remove the comments around this ComponentRef element and the Component above in order to add resources to this installer. -->
			<!-- <ComponentRef Id="ProductComponent" /> -->

			<!-- Note: The following ComponentGroupRef is required to pull in generated authoring from project references. -->
			<ComponentGroupRef Id="Product.Generated" />
		</Feature>
	</Product>
</Wix>

The text of the Xml gives us hints as to what we are supposed to do in its comments.  Take a moment and read the comments.

Step 4 – Configure the WIX Xml file to deploy that file

Lets start editing that XML file.

Note: I am going to use the term “node’ to indicate and XML section. So and everything it contains is a node. If a node is inside it, I might call it a subnode.

  1. Uncomment the Component node the Directory nodes.
  2. Remove the two TODO: comments.
  3. Uncomment the ComponentRef node that is inside the Feature node.
  4. Remove the TODO: comment and the Note comment.
  5. Inside the Component node, at a File node as follows:
    <File Id='Install.conf_id' Name='Install.conf' DiskId='1' Source='Install.conf' KeyPath='yes' />
    

Ok, you are done.  Yes, that was all there is too it.

You XML syntax should now look as follows:

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Product Id="fae97512-4eca-4271-821d-75b7a8861557" Name="DeployOneFile" Language="1033" Version="1.0.0.0" Manufacturer="DeployOneFile" UpgradeCode="fec17f7b-060e-466f-8bd2-7895eb2b92ce">
    <Package InstallerVersion="200" Compressed="yes" />

    <Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />

    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="ProgramFilesFolder">
        <Directory Id="INSTALLLOCATION" Name="DeployOneFile">
          <Component Id="ProductComponent" Guid="927a42ea-a6df-45f6-ac52-4b979194bc10">
            <File Id='Install.conf_id' Name='Install.conf' DiskId='1' Source='Install.conf' KeyPath='yes' />
          </Component>
        </Directory>
      </Directory>
    </Directory>

    <Feature Id="ProductFeature" Title="DeployOneFile" Level="1">
      <ComponentRef Id="ProductComponent" />
      <ComponentGroupRef Id="Product.Generated" />
    </Feature>
  </Product>
</Wix>

Ok, you are ready to build.

Step 4 – Build the project

Well, there really isn’t much to debug, so we are only going to build a release version here.

  1. In the Visual Studio 2010 tool bar, there should be a drop down box that either says Debug or Release.  Change it to Release if it is not already at Release.
  2. Select Build | Build Solution or use the shortcut key.

You should now have an MSI built in the project’s bin\release directory.

Step 5 – Test the MSI

Let’s go get the MSI and test it.

  1. Right-click on the project, DeployOneFile, and choose, Open Folder in Windows Explorer.
  2. In Explorer, navigate into the bin\release directory.
  3. You will see two files:
    DeployOneFile.msi – This is the MSI and is all you need.
    DeployOneFile.wixpdb – This is a file for debugging only. You may never use it unless you need to debug.
  4. You may or may not want to test the MSI on you development box. If  you do, just double-click the MSI.  Otherwise, copy it to a test box and run the MSI there.
  5. Verify that the file installed.
    Note: If on a 64 bit system, it will by default install as an x86 app, so look in c:\program files (x86)\ for a folder called DeployOneFile.
  6. Check Add / Remove Programs to make sure the install shows up there and that the uninstall works.

Congratulations. You just use WIX to create and deploy your first MSI.

 

Bonus Information – Learning about Repair

Repairing an installation is an important feature of  to understand.

What you are going to learn here, is that if you delete a file in a component that has the KeyPath=’yes’ tag, the component will reinstall.

  1. Make sure the MSI you just created above is installed. If you uninstalled it doing steps above, reinstall it.
  2. On the machine where you installed the MSI, Browse to the installation directory.My directory is here.
    C:\Program Files (x86)\DeployOneFile
  3. The only file in that directory is the file you installed:
    Install.conf
  4. Delete the Install.conf file.
  5. Still on the machine where you installed the MSI, go to Add / Remove Programs.
  6. Right-click on the DeployOneFile instance and choose Repair.
  7. The file you deleted should be restored.

Ok, so you probably have questions about repair.  This was just a basic introduction.  Hopefully we will learn more about repair as we go on.