Feature conditions and UI
A question that’s come up on the wix-users mailing list several times lately has been about how to use feature conditions with properties set in the UI. It doesn’t work as most people expect so I thought I’d dig a little deeper than I would in a bunch of replies on the mailing list.
The basic idea is that you have some kind of optional functionality — like a Web site or Visual Studio integration — that you want the user to be able to enable or disable from the installer UI. (I’m assuming here that the functionality is in reality just a set of discrete components organized into a feature.)
The easiest way to get this working is to use MSI’s built-in SelectionTree control — the normal, boring, not-quite-easy-to-use feature tree that you see in most installers. That’s the approach we used with the WiX installer; in addition to features for plain sets of files, there are features for Visual Studio, Votive, and MSBuild integration.
The thrust of this question, though, is generally about using some other UI, like checkboxes or groups of radio buttons, to offer a better user experience during setup. But if you replace the SelectionTree control, you need to also replace how it manages feature states for you. Feature conditions seem like a fairly cheap way of accomplishing that, assuming you use public (i.e., all UPPERCASE) and secure (Property/@Secure=”yes”) properties to ensure they’re always passed to the MSI server for the installation execution sequence.
But it doesn’t work and as is usual with MSI, reading the SDK doc and examining a verbose log tell us why — almost.
Let’s start with the doc on the Condition table, which is the table where feature conditions are stored. A promising blurb:
The Level may be set based on any conditional statement, such as a test for platform, operating system, or a particular property setting.
A nice feature of the MSI SDK doc is that each table lists the actions that refer to the table. For the Condition table, it says:
This table is referred to when the CostFinalize action is executed.
Follow that link and you get another couple of interesting blurbs:
The CostFinalize action must be executed before starting any user interface sequence which allows the user to view or modify Feature table selections or directories.
and
The CostFinalize action queries the Condition table to determine which features are scheduled to be installed.
So far, everything sounds like it will work. It doesn’t, though, so clearly the docs aren’t telling the whole story. Verbose logs are our next step. Starting with MSI 3.1, they include entries when properties are added, changed, or deleted, so you can verify that the properties are being set correctly during the UI sequence. You can also verify that MSI is passing those properties to the execution sequence. Search the log for “Switching to server” to see the list of properties being passed. For example, from a complete install of the Windows Vista SDK version of Orca:
MSI (c) (6C:24) [14:54:54:015]: Switching to server: CUBDIR=”C:\Program Files (x86)\Orca\” ORCADIRECTORY=”C:\Program Files (x86)\Orca\” TARGETDIR=”V:\” MS.51D569E0_8A28_11D2_B962_006097C4DE24=”C:\WINNT\SysWOW64\” MS.51D569E2_8A28_11D2_B962_006097C4DE24=”C:\WINNT\SysWOW64\” MS.7EBEDD6A_AA66_11D2_B980_006097C4DE24=”C:\WINNT\SysWOW64\” MS.7EBEDD3E_AA66_11D2_B980_006097C4DE24=”C:\WINNT\SysWOW64\” INSTALLLEVEL=”1000″ COMPANYNAME=”MS” USERNAME=”Bob Arnson” CURRENTDIRECTORY=”X:\” CLIENTUILEVEL=”0″ CLIENTPROCESSID=”3180″ SOURCEDIR=”X:\” ACTION=”INSTALL” EXECUTEACTION=”INSTALL” ROOTDRIVE=”V:\” SECONDSEQUENCE=”1″ ADDLOCAL=OrcaHelp,Orca,EvalComServer,MergeModServer,CUBFiles,FullCUBFile,LogoCUBFile,XPLogoCUBFile,MMCUBFile
Notice the ADDLOCAL property setting. It’s a comma-delimited list of feature names that are to be installed. I didn’t pick them individually; instead, I used the “complete” button to say I wanted everything. The orca.msi package publishes a SetInstallLevel control event with a high install level to let MSI decide to install all features rather than using an AddLocal control event to manually list them.
Let’s assume that MSI uses the ADDLOCAL property and its friends the REMOVE and ADDSOURCE properties (among other friends) to communicate to the MSI server the feature choices the user made during the UI sequence. If you think about the many different ways the UI can implicitly and explicitly set which features get installed — from “typical” and “complete” buttons publishing SetInstallLevel control events and SelectionTree controls letting users pick and choose individual features — it makes sense that MSI would need a rich way of controlling feature installation and it makes sense for MSI to use its own properties mechanism rather than invent a new one. (And note that the same thing applies to uninstallation and reinstallation — those are all handled on a per-feature basis via properties.)
OK, so what does that have to do with feature conditions? (I’m getting there, really.) All the feature-selection property topics have this blurb:
The installer sets the Preselected Property to a value of “1″ during the resumption of a suspended installation, or when any of the above properties are specified on the command line.
Follow the Preselected link and you get this blurb:
The Preselected property indicates that features have already been selected and that the selection dialog need not be shown.
Now it’s time to enter the murky world of speculation. There’s no doc to indicate that the feature-selection properties or the Preselected property have any affect on feature-condition evaluation in CostFinalize. But I think it’s reasonable to infer that an explicit feature selection (via ADDLOCAL, for example) would override the more implicit feature-selection options available, including the Condition table and INSTALLLEVEL property.
And, to make a long story short — too late! — Carolyn, MSI Team Dev Lead, confirmed that’s the case. Feature conditions are evaluated only if no feature-selection properties are set. And, as the UI sequence converts SetInstallLevel control events, feature-selection control events, and SelectionTree control settings into the corresponding feature-selection properties, feature conditions that include properties set during the UI sequence won’t work as expected.
Note that an installation that doesn’t run the UI sequence (e.g., using the /qb command-line switch to run a basic-UI installation) can use properties set during the execute sequence in feature conditions. But the same caveat applies: If any feature-selection properties are set, feature conditions aren’t evaluated.
OK, fine, what now?
So how do you get this working? As I said near the beginning of this much-longer-than-expected tome, “if you replace the SelectionTree control, you need to also replace how it manages feature states for you.” As SelectionTree controls end up setting the feature-selection properties, you need to do the same thing, directly or indirectly. The easiest way is to publish feature-selection control events from your feature-selection dialog.
For example, the WixUI dialog set WixUI_Mondo uses SetInstallLevel published from SetupTypeDlg:
<Control Id=”CompleteButton” Type=”PushButton” X=”40″ Y=”171″ Width=”80″ Height=”17″ ToolTip=”!(loc.SetupTypeDlgCompleteButtonTooltip)” Text=”!(loc.SetupTypeDlgCompleteButton)”>
<Publish Property=”WixUI_InstallMode” Value=”InstallComplete”>1</Publish>
<Publish Event=”SetInstallLevel” Value=”1000″>1</Publish>
</Control>
If you wanted to explicitly list features, the easiest way to do so is to use an AddLocal control event with an argument of ALL to install all features locally, then use individual Remove control events to remove the features that don’t apply. Doing so covers lets the user both install and uninstall features. For example:
<Control Id=”Next” Type=”PushButton” X=”235″ Y=”243″ Width=”57″ Height=”18″ Default=”yes” Text=”&Next >”>
<Publish Event=”AddLocal” Value=”ALL”>1</Publish>
<Publish Event=”Remove” Value=”FeatureX”>NOT FEATUREX_CHECKBOX</Publish>
<Publish Event=”Remove” Value=”FeatureY”>NOT FEATUREY_CHECKBOX</Publish>
<Publish Event=”Remove” Value=”FeatureZ”>NOT FEATUREZ_CHECKBOX</Publish>
</Control>
Hope this helps understand why things aren’t always as simple as a first glance might indicate.
July 12th, 2007 at 18:16 #Ken
Bob,
I followed this posting all the way through, but it ended before it got to the part that I was interested in. I _am_ using the SelectionTree and taking advantage of the goodness that you speak of here, and consequently I observe the correct feature IDs being set in ADDLOCAL.
The issue that I’m trying to tackle is this: I have two features, A and B. Feature A does work with SQL Server and thus requires me to present a dialog to the user to get SQL Server credentials. Feature B creates/configures an IIS virtual directory, so I have to present a different dialog to similarly collect IIS information. At runtime, I need to present the correct dialogs based on the feature conmbination the user has chosen from the SelectionTree.
So I was hoping that your posting would lead me through this, including how I could check to see which features were chosen in the SelectionTree so I knew which dialogs to present.
I’m new to MSI and WiX, so I confess to not understanding the relationship between the UI and the MSI Server, and what the best way would be to conditionally control my UIs as I’ve described.
July 13th, 2007 at 07:09 #Bob Arnson
If you’re using the SelectionTree control, you can use the &FeatureId syntax to check for the feature being installed. So a condition to decide whether to show a SQL dialog might look like
&Sql=3 AND !Sql=2
&Sql=3 means “being installed locally” and !Sql=2 means “not already installed.”
July 13th, 2007 at 10:35 #Ken
Bob, thank you for the response.
I’ve followed the example in your original post and also added in your conditional that you mentioned in your response above. The WiX for my SelectionTree dialog’s “Next” button now loks like this:
IF &Sql=3 AND !Sql=2
My question now is: Is this orchestrating the conditional appearance of dialogs by manipulating the button press event handling at runtime the best practice approach to what I’m trying to do?
I’m just looking for validation of the approach and that doing something else (like messing with the InstallUiSequence table) is not a competing approach to this.
Thanks again.
July 13th, 2007 at 10:38 #Ken
The XML tags were stripped from the last posting, trying again:
<Control Id=”Next” Type=”PushButton” …>
<Publish Event=”NewDialog” Value=”DatabaseDialog”>IF &Sql=3 AND !Sql=2<Publish/>
</Control>
July 13th, 2007 at 16:32 #Bob Arnson
You should drop “IF” in there; it’s just an expression, not a statement.
The way MSI UI works is that once you’ve started the wizard sequence with a modal dialog, the InstallUISequence is out of the picture. All your navigation happens in the button control events.
So your approach is the best/only one.
August 30th, 2007 at 11:22 #Alan Frye
Bob, this was a great article but I am having a similar issue that Ken reported. When add the condition using the Id of my feature the push button does nothing.
I am also WIX V3
thanks
October 6th, 2007 at 14:51 #Wahoo
Thank you for sharing!
October 8th, 2007 at 06:20 #Peter
This information is very useful. But REMOVE command only switch Feature in state “not to install” with red cross in SelectTree. User still can install it by open popup menu and change it state. How can I (based on this example) hide one of feature (not chosened in UI) that in SelectTree only one is visible?
October 8th, 2007 at 19:35 #Bob Arnson
Peter, try setting the feature’s level to zero, then using ADDLOCAL/AddLocal to add the ones you want the user to see.
October 8th, 2007 at 23:50 #Peter
It doesn’t work. In this case I have empty SelectTree window with no one Features. When I used AddLocal/REMOVE combination I can see both Features and one of it is switched off (based on UI condition - this works good). This isn’t what I want because I want only one.
Maybe it is possible to switch off all pop-up menu in SelectTree window? Do you know how can I do this? This will solve my problem.
October 9th, 2007 at 11:48 #Bob Arnson
It’s not possible to change how the selection tree control works, just modify what’s shown.
November 1st, 2007 at 02:13 #Lahare
Bob,
this is a great article, what i am trying to figure out, if there is any dependencies of one feature on other, that how should i validate such condition on UI, do i have to write custom handler for it?,
I have 2 features ,let say A and B and are presented in Tree Control for user selection. Here B depends upon A.
If B is selected, then A must be selected, as I understood what actions you take on “Next” button can be checked using condition mention in Ken Reply.
But is there any way to show it on User interface.
Regards
ALAHARE
November 1st, 2007 at 22:33 #Bob Arnson
Unfortunately, the SelectionTree control doesn’t support that. The only way to do it is with a custom feature-selection dialog, using check boxes or radio buttons (for example).
November 16th, 2007 at 10:01 #Matt
Lahare,
To do this you could have feature A be a subfeature of B and in WiX set the Absent attribute of A to disallow. This will force feature A to be installed if feature B installed. It will however not allow feature A to be installed on its own.
November 16th, 2007 at 10:05 #Matt
Bob,
I am trying to create a setup project where the feature set that the user can install is controlled by the contents of a license file. Using the install level will not work since it does not provide the granular level that is needed.
I have tried some of the same things that the above people have, using AddLocal and Remove action and it results in the feature not being installed but still available.
Any other ideas on how to handle this situation?
Thanks
Matt
November 16th, 2007 at 20:31 #Bob Arnson
Matt,
The only way to prevent features from showing in the feature selection tree is to have their level set to 0. Then you can use AddLocal control events to enable and select them.
It’s worth noting that anybody can use ADDLOCAL on the command line to install any feature. Managing licensing via installers doesn’t offer any real protection.
Bob
January 8th, 2008 at 12:01 #Daniel Lee
my co-worker just came across this article. It is interesting to note that if you set the INSTALLLEVEL property to a different value that conditions on all features are re-evaluated. At least that has been our experience. So on our custom dialog that includes check boxes when the user clicks on NEXT part of the control events is to set INSTALLLEVEL to 101.
January 12th, 2008 at 11:40 #Bob Arnson
Daniel,
That makes sense, given the doc. Thanks for the hint!
March 7th, 2008 at 05:18 #Pavel Huml
I use an acceptable solution of selecting available features (by serial number entered in dialog SerialNumberCheckDlg): Split sequences of dialogs to two:
1) Before CostFinalize: MyWelcomeDialog -> SerialNumberCheckDlg!
2) Before ProgressDlg: LicenseAgreementDlg -> SetupTypeDlg -> CustomizeDlg (next as is standard in Mondo)
NOT Installed
NOT Installed
1
VALIDSERIALCODE = “no”
1
My DLL: VerifySerialNumber sets property for each feature (for example MY_FEATURE_UNLOCK) and VALIDSERIALCODE if SN is acceptable.
Feature is enabled by level:
….
MY_FEATURE_UNLOCK = “yes”
I must create new Welcome dialog, and don’t include original WelcomeDialog (it is registred to ProgressDlg too).
This solution works with wix 3.0.2925.0 and is partly tricky.
March 7th, 2008 at 08:19 #Pavel Huml
Once more I try XML markup:
<InstallUISequence>
<Show Dialog="MyWelcomeDialog" Before="CostFinalize">NOT Installed</Show>
<Show Dialog="LicenseAgreementDlg" Before="ProgressDlg">NOT Installed</Show>
</InstallUISequence>
<Dialog Id="SerialNumberCheckDlg">
<Control Id="Next" >
<Publish Event="DoAction" Value="VerifySerialNumber">1</Publish>
<Publish Event="SpawnDialog" Value="InvalidPidDlg">VALIDSERIALCODE = "no"</Publish>
</Control>
</Dialog>
<Publish Dialog="LicenseAgreementDlg" Control="Next" Event="NewDialog" Value="SerialNumberDlg">1</Publish>
Feature is enabled by level:
<Feature Id="MY_FEATURE" Title="" Level="0">
….
<Condition Level="1">MY_FEATURE_UNLOCK = "yes"</Condition>
</Feature>
October 15th, 2008 at 07:37 #msenn
There is a third-party msi.
The features in the selection tree control are selected to run locally by default.
Is it possible to change the state of a feature in the selection tree control, using a transform?
Manfred
October 15th, 2008 at 09:34 #Bob Arnson
Manfred,
You can’t change feature states in the selection tree but you can change them on the features themselves, such as by setting install levels. Generally, you’d control them by setting ADDLOCAL and REMOVE on the command line.
October 16th, 2008 at 00:11 #msenn
Hi Bob,
but there must be possible to change the default state of the feature in the selection tree, when doing a custom setup.
to one of the following possibilities:
- This feature will be installed on local hard drive
- This feature and its subfeatures will be installed on local hard drive
- This feature will be installed to run from CD
- This feature and its subfeatures will be installed to run from CD
- This feature will be installed to run from Network
- This feature and its subfeatures will be installed to run from Network
- This feature will be installed when required
- This feature will not be installed
For example:
When doing a custom setup, Feature1 in the selection tree is displayed per default as “This feature will be installed on local hard drive”.
But I want to change the msi, so that feature1 is displayed per default as “This feature will not be installed”?
Any ideas?
October 16th, 2008 at 11:07 #Nick Stinger
I’m sorry, but this is so contrived, convoluted, and in-cohesive with everything else that someone at Microsoft should go bald for this design. The logic they use to implement “feature conditions” is so narrow that the capability seems to have been made for very specific uses under an even more limited number of circumstances, akin to writing a series of “if” blocks in code to supplement a poor design. The whole idea doesn’t flow with the rest of the installer concept. There is an obvious problem here!
October 16th, 2008 at 18:18 #Bob Arnson
Manfred,
Set the Feature row’s Level field to 0 to disable it.
October 16th, 2008 at 18:19 #Bob Arnson
Nick,
You’ll have difficulty convincing me that having hair is somehow virtuous…