Set property in custom dialog for conditional feature - wix

I have a property called SELECTEDFEATURESET with a default value of 'none'. In a custom dialog I'm trying to set this property, according to which button the user pressed. At the end I use the property to decide if certain components shall be installed or not.
Propery defined in the main installer file (Product.wxs):
<Property Id='SELECTEDFEATURESET' Value='none' />
Set the property in the dialog:
<Control Id="InternalFeatureButton" Type="PushButton" Text="FeatureSetA">
<Publish Event="NewDialog" Value="InstallDirDlg">1</Publish>
<Publish Property="SELECTEDFEATURESET" Value="FeatureSetA">1</Publish>
</Control>
<Control Id="ProductionFeatureButton" Type="PushButton" Text="FeatureSetB">
<Publish Event="NewDialog" Value="InstallDirDlg">1</Publish>
<Publish Property="SELECTEDFEATURESET" Value="FeatureSetB">1</Publish>
</Control>
<Control Id="DemoFeatureButton" Type="PushButton" Text="FeatureSetC">
<Publish Event="NewDialog" Value="InstallDirDlg">1</Publish>
<Publish Property="SELECTEDFEATURESET" Value="FeatureSetC">1</Publish>
</Control>
Decide whether the feature shall be installed or not:
<Feature Id='ParentFeature' Level='0'>
<ComponentRef Id='ComponentA' />
<Condition Level='1'><![CDATA[SELECTEDFEATURESET="FeatureSetA"]]></Condition>
</Feature>
<Feature Id='ParentFeature' Level='0'>
<ComponentRef Id='ComponentB' />
<Condition Level='1'><![CDATA[SELECTEDFEATURESET="FeatureSetB"]]></Condition>
</Feature>
<Feature Id='ParentFeature' Level='0'>
<ComponentRef Id='ComponentC' />
<Condition Level='1'><![CDATA[SELECTEDFEATURESET="FeatureSetC"]]></Condition>
</Feature>
When I run the installer and press the FeatureSetA button, no optional component is installed. When I initialize the SELECTEDFEATURESET property with 'FeatureSetA', then the ComponentA is installed (same for A, B and C).
Why is the value of SELECTEDFEATURESET 'none' instead of 'FeatureSetA' / 'FeatureSetB' / 'FeatureSetC' at the time of the feature condition evaluation?
How do I fix it?

I fixed it my moving the condition from the feature to the component.

Related

Dialog sequence based on Registry Search

What I'm trying to do is if a certain registry value is not found on the machine, a custom dialog will be shown to them where they can choose the value that they want to add. The problem is when they select that value and click Next, then click Back, since the property relating to that registry is already filled, the custom dialog will not be shown anymore unless they re-run the installer. I hope I'm clear enough, here're the snippets of the code.
<Property Id="REG_VAL" Value="NoValueFound">
<RegistrySearch ... />
</Property>
<Component ...>
<RegistryValue Value="[REG_VAL]".../>
</Component>
<UI...>
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="ChooseValueDlg">
<![CDATA[(REG_VAL="NoValueFound")]]>
</Publish>
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg">
<![CDATA[(REG_VAL<>"NoValueFound")]]>
</Publish>
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="ChooseValueDlg">
<![CDATA[(REG_VAL="NoValueFound")]]>
</Publish>
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg">
<![CDATA[(REG_VAL<>"NoValueFound")]]>
</Publish>
</UI>
<UI>
<Dialog Id="ChooseValueDlg" ...>
<Control Id="rdoBtnGrp" Type="RadioButtonGroup" Property="REG_VAL" ...>
<RadioButtonGroup Property="REG_VAL">
<RadioButton Value="NoValueFound" .../>
<RadioButton Value="Value1" .../>
<RadioButton Value="Value2" .../>
</RadioButtonGroup>
</Control>
</Dialog>
</UI>
You need to save the results of the registry search into two properties and bind one of them to the UI for editing by the user and one used as conditions for the mutually exclusive control events. This way if you start off with both null the dialog gets displayed and then when the user enters data into one the other is still null and the dialog will still be displayed.
BTW I like to ditch the unneeded CDATA and use PROPERTY and Not PROPERTY. I think it's easier to read.

Conditional Feature based on Property

In my bundle I have 2 features which both are about creating a folder structure based on the value of a property. As you can see my conditions are fairly simple. What is interesting is that only the condition on feature ONE works, while in feature TWO (where it does a comparison) it does nothing, but If I add the condition on every component I need just like in the other pasted code it works fine.
I was wondering why this is happening
<Property Id="NUMBER" />
<Feature Id="ONE" Level="0">
<Condition Level="1">
<![CDATA[NOT NUMBER]]>
</Condition>
<ComponentGroupRef Id="OneStructure"/>
</Feature>
<Feature Id="TWO" Level="0">
<Condition Level="1">
<![CDATA[NUMBER <> ""]]>
</Condition>
<ComponentGroupRef Id="OneStructure"/>
</Feature>
-
<Directory Id="dir77996843FCCE5E3734A5EDAA86FCE55B" Name="Input">
<Component Id="cmp2F4C23D858A887EF0B2539F7EC1884BE" Guid="{625D2714-157F-4B21-86C0-D4954A4E1F73}" KeyPath="yes"> <Condition> <![CDATA[NUMBER <> ""]]> </Condition>
<CreateFolder />
</Component>
</Directory>
Had the same issue. Apparently it has to do with the condition only being evaluated once.
Instead of putting a condition directly in a feature, you can put conditions in a Control Event pertaining to a button in a given dialog.
Example:
<Control Id="Install" Type="PushButton" X="304" Y="243" Width="56" Height="17" Default="yes" Text="Install">
<!--Check property value here:-->
<Publish Event="AddLocal" Value="ALL">1</Publish>
<Publish Event="Remove" Value="ONE">NUMBER = 0</Publish>
<Publish Event="Remove" Value="TWO">NUMBER = 1</Publish>
<Publish Event="EndDialog" Value="Return" />
</Control>
You would then change the value of the property "NUMBER" with a Radio Button.
Hope this helps!

possible way to disable controls at runtime?

Is there a way to disable UI controls while I'm doing some actions like checking database availability? Now it's like this:
<Control Id="Next" Type="PushButton">
<Publish Event="DoAction" Value="CheckDBConnection" Order="3">
<![CDATA[SQL_SERVER <> "" AND SQL_USER <> "" AND SQL_PASSWORD <> ""]]>
</Publish>
<Publish Event="DoAction" Value="Confirm_OverwriteDatabase" Order="2"/>
<Publish Event="NewDialog" Value="VerifyReadyDlg" Order="1"/>
</Control>
When CheckDBConnection is executing, all controls are alive, but doesn't do anything. It will be more accurate to disable them while performing background action.
Use Conditions with enable\disable Actions, based on the desired properties:
<Control Id="{Id}" Type="{Type}">
<Condition Action="enable" ><![CDATA[SOMEPROPERTY = SomeValue]]></Condition>
<Condition Action="disable"><![CDATA[SOMEPROPERTY <> SomeValue]]></Condition>
</Control>
Thanks to #vitaliy-zadorozhnyy, I was able to come up with an extra weird code that I dont't understand myself. But it works.
...
...
<Property Id="DBCHECK_INPROGRESS" Value="0" />
<Property Id="DBCHECK_NOTINPROGRESS" Value="0" />
...
...
<Control Id="{Id}" Type="{Type}">
<Condition Action="enable"><![CDATA[DBCHECK_INPROGRESS <> 1]]></Condition>
<Condition Action="disable"><![CDATA[DBCHECK_INPROGRESS = 1]]></Condition>
</Control>
{insert those conditions for any control you want to disable during action}
...
...
<Control Id="Next" Type="PushButton">
<Condition Action="enable"><![CDATA[DBCHECK_INPROGRESS <> 1]]></Condition>
<Condition Action="disable"><![CDATA[DBCHECK_INPROGRESS = 1]]></Condition>
<Publish Event="DoAction" Value="SetInProgress">1</Publish>
<Publish Property="TEMP_INPROGRESS" Value="[DBCHECK_INPROGRESS]">1</Publish>
<Publish Property="DBCHECK_INPROGRESS" Value="[TEMP_INPROGRESS]" />
<Publish Event="DoAction" Value="CheckDBConnection"/>
<Publish Event="DoAction" Value="SetNotInProgress">1</Publish>
<Publish Property="TEMP_NOTINPROGRESS" Value="[DBCHECK_NOTINPROGRESS]">1</Publish>
<Publish Property="DBCHECK_NOTINPROGRESS" Value="[TEMP_NOTINPROGRESS]" />
{other events}
</Control>
and custom actions (JScript) are simple:
function SetInProgress_CA() {
Session.Property("DBCHECK_INPROGRESS") = "1";
}
function SetNotInProgress_CA() {
Session.Property("DBCHECK_INPROGRESS") = "0";
}

Inserting Custom Action between Dialogs (InstallUISequence) in WiX

I have two custom dialog boxes (plus the required ones ExitDlg, FatalErrorDlg, etc.), the first one sets a property using an Edit control and the second one shows this property using a Text control. Here is the meaningful code:
<Dialog Id="DialogA" ...>
<Control Id="ControlEdit" Type="Edit" Property="MY_PROPERTY" .../>
<Control Id="ControlNext" Type="PushButton" ...>
<Publish Event="EndDialog" Value="Return" /></Control>
</Dialog>
And then the second dialog:
<Dialog Id="DialogB" ...>
<Control Id="ControlText" Type="Text" Text="[MY_PROPERTY]" .../>
<Control Id="ControlBack" Type="PushButton" ...>
<Publish Event="EndDialog" Value="Return" /></Control>
<Control Id="ControlNext" Type="PushButton" ...>
<Publish Event="EndDialog" Value="Return" /></Control>
</Dialog>
And the action sequence:
<InstallUISequence>
<Show Dialog="DialogA" Before="MyCustomAction" />
<Custom Action="MyCustomAction" Before="DialogB" />
<Show Dialog="DialogB" Before="ExecuteAction" />
</InstallUISequence>
The custom action changes the value of MY_PROPERTY. My problem is how to make the Back button in DialogBget back to DialogA. Using NewDialog is simple, but then I can't get the custom action to be executed between the dialogs, or can I?
edit - 2013-05-02
After the answer from #caveman_dick, I tried to change the DialogA almost like he said, but instead of using EndDialog, I changed to Action="NewDialog" Value="DialogB". But now the Custom Action isn't being called. If I remove the Publish event to go to next dialog, then the CA is called. If I leave as #caveman_dick said, I can't get back to DialogA from DialogB.
edit - 2013-05-02
After searching in book WiX 3.6: A Developer's Guide to Windows Installer XML, I found the following: "if you have more than one Publish event, they must have conditional statements as their inner text. Otherwise, all of the events simply won't be published."
So the answer from #caveman_dick is correct, except that you need to change to the following:
<Publish ...>1</Publish>
Rather than scheduling the custom action in the InstallUISequence you can publish it on the button click:
<Dialog Id="DialogA" ...>
<Control Id="ControlEdit" Type="Edit" Property="MY_PROPERTY" .../>
<Control Id="ControlNext" Type="PushButton" ...>
<Publish Event="DoAction" Value="MyCustomAction">1</Publish>
<Publish Event="EndDialog" Value="Return">1</Publish>
</Control>
</Dialog>
EDIT: The Publish element's condition needs to explicitly evaluate to true to run, so add "1" as the text of the Publish elements.

WiX: Can't handle "Launch an application after install" checkbox = 0

I'm making an installation with WiX that have a "Launch an application after install" checkbox.
The goal is to have a reaction on settting the checkbox as well as on unsetting the checkbox. In case the checkbox is set, I need to run an application. In case the checkbox is not set, I need to run the same application but with command line argument.
Here is a part of my WiX script.
<CustomAction Id="StartConfigManagerOnExit"
FileKey="ParamsShower.exe"
ExeCommand=""
Execute="immediate"
Impersonate="yes"
Return="asyncNoWait" />
<CustomAction Id="StartUpgradeConfigOnExit"
FileKey="ParamsShower.exe"
ExeCommand="/upgrade"
Execute="immediate"
Impersonate="yes"
Return="asyncNoWait" />
<UI>
<Publish Dialog="ExitDialogEx"
Control="Finish"
Order="1"
Event="DoAction"
Value="StartConfigManagerOnExit">LAUNCHAPPONEXIT = 1</Publish>
<Publish Dialog="ExitDialogEx"
Control="Finish"
Order="1"
Event="DoAction"
Value="StartUpgradeConfigOnExit">LAUNCHAPPONEXIT = 0</Publish>
<Publish Dialog="ExitDialogEx"
Control="Finish"
Event="EndDialog"
Value="Return"
Order="999">1</Publish>
<Dialog Id="ExitDialogEx"
Width="370"
Height="270"
Title="[ProductName] Setup">
<Control Id="LaunchCheckBox"
Type="CheckBox"
X="135"
Y="190"
Width="220"
Height="40"
Property="LAUNCHAPPONEXIT"
Hidden="yes"
CheckBoxValue="1"
Text="Launch an app">
<Condition Action="show">NOT Installed</Condition>
</Control>
</Dialog>
<InstallUISequence>
<Show Dialog="ExitDialogEx"
OnExit="success" />
</InstallUISequence>
<AdminUISequence>
<Show Dialog="ExitDialogEx"
OnExit="success" />
</AdminUISequence>
</UI>
An installation starts the application when LaunchCheckBox is set. But it doesn't run it in case the checkbox is not set.
I've found an answer. Looks like checkbox property is not equal to 0 when unchecked. Simply change the condition "LAUNCHAPPONEXIT = 0" with "NOT LAUNCHAPPONEXIT" solves the situation.
Make default:
<Property Id="LAUNCHAPPONEXIT" Value="1" />
Then correct the conditions (corrected with sascha's comment):
<Publish Dialog="ExitDialogEx" Control="Finish" Order="1" Event="DoAction" Value="StartConfigManagerOnExit">LAUNCHAPPONEXIT</Publish>
<Publish Dialog="ExitDialogEx" Control="Finish" Order="1" Event="DoAction" Value="StartUpgradeConfigOnExit">NOT LAUNCHAPPONEXIT</Publish>
A checkbox has no value at all when unchecked, so rather than use the 1/0 notation you can simply use
LAUNCHAPPONEXIT
and
Not LAUNCHAPPONEXIT
You need to add initialize custom action for your property,
<CustomAction ID="InitLAUNCHAPPONEXIT"
Property="LAUNCHAPPONEXIT"
Value="0"
Return="check"/>
and then add it to InstallUISequence before show exit dialog, or simply add your property to product <Property Id="LAUNCHAPPONEXIT" Value="0" />.