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.
- [Feature/Component Operator]
- [Feature/Component Id]
- [Boolean Operator]
- [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.
- Is this is a Feature of a Component
- What is the current *State of the Component or Feature.
- 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.
Operator | Type | State or Action | Description | Where this syntax is valid | How is this commonly used? |
---|---|---|---|---|---|
$ | Component | Component – 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. |
? | Component | Component – 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). |
& | Feature | Feature – 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. |
! | Feature | Feature – 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.
Operator | Return 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
Operator | Meaning |
---|---|
>< | 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
Operator | Meaning |
---|---|
>< | 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.
States | Value | Meaning |
---|---|---|
INSTALLSTATE_UNKNOWN | -1 | No action to be taken on the feature or component. |
INSTALLSTATE_ADVERTISED | 1 | Advertised feature. This state is not available for components. |
INSTALLSTATE_ABSENT | 2 | Feature or component is not present. It either has never been installed, or if it was once installed it has been uninstalled. |
INSTALLSTATE_LOCAL | 3 | Feature or component on the local computer. This feature or component is currently installed on the local computer. |
INSTALLSTATE_SOURCE | 4 | Feature 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.
Condition | Definition | How is this commonly used? |
!MyFeature = 2 | If 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 = 3 | If MyFeature is currently installed | Use this on uninstall or upgrade or repair to see if a Feature is already installed. |
&MyFeature = 2 | If 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 = 3 | If MyFeature is to be installed | Use 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