In a WiX installer how do you determine which features have been selected for installation? - wix

I am using WiX Toolset v3.11 and would like to find out the state of the features after the user has made selections by using a Selection Tree in the Custom dialog.
For example, I have this feature:
<Feature Id="Miscellaneous" Title="Miscellaneous" Description="Stuff." Level="4" ConfigurableDirectory="INSTALLFOLDER">
<MergeRef Id="miscMM"/>
</Feature>
How can I test if this features has been selected to be installed?
I am trying to set a property that will be used in the final dialog that will summarize the features that will be installed.
For the Next button that opens the Summary Dialog I have tried this:
<Publish Property="INST_MISC" Value="Yes"><![CDATA[Miscellaneous <> "4"]]></Publish>
but it always evaluates to true regardless of the Selection Tree setting.
Thanks.

This seems to do what I want:
<Publish Property="INST_MISC" Value="Yes"><![CDATA[(&Miscellaneous AND &Miscellaneous=3)]]></Publish>
<Publish Property="INST_MISC" Value="No"><![CDATA[NOT &Miscellaneous OR (&Miscellaneous AND &Miscellaneous<>3)]]></Publish>
<Publish Property="INST_MISC" Value="When required"><![CDATA[(&Miscellaneous AND &Miscellaneous=1)]]></Publish>

Related

Wix Installer - ICE03: Not a valid foreign key

I'm really struggling trying to figure these error out. I'm trying to build a setup using the following sequence:
Welcome Screen
License Screen
Scope Screen
Browse Screen
Features Screen
Custom Screen
Shortcut Screen
Install Overview Screen
Install/Progress Screen
Finish Screen
Rather that jumping into the lot in one go, I thought I'd do them one by one and I got as far as building the Welcome, License, Scope screen.
I then tried to include the Features Screen but I had the error I'll explain below. Strangely enough when I change the FeatureDlg to CustomizeDlg it works.
I then try to introduce the BrowseDlg before the CustomizeDlg but now I keep getting similar errors when I tried to use the FeaturesDlg.
I'm new to Wix so apologies if I've left something obvious out. I'm using VS2013 to build this.
My UI.Wxs has the following code within its UI tags
<DialogRef Id="BrowseDlg" />
<DialogRef Id="DiskCostDlg" />
<DialogRef Id="ErrorDlg" />
<DialogRef Id="FatalError" />
<DialogRef Id="FilesInUse" />
<DialogRef Id="MsiRMFilesInUse" />
<DialogRef Id="PrepareDlg" />
<DialogRef Id="ProgressDlg" />
<DialogRef Id="ResumeDlg" />
<DialogRef Id="UserExit" />
<!--<DialogRef Id="FeaturesDlg" />-->
<DialogRef Id="CustomizeDlg" />
<!-- Welcome -->
<Publish Dialog="WelcomeDlg"
Control="Next"
Event="NewDialog"
Value="LicenseAgreementDlg">NOT Installed</Publish>
<!-- License -->
<Publish Dialog="LicenseAgreementDlg"
Control="Back"
Event="NewDialog"
Value="WelcomeDlg">1</Publish>
<Publish Dialog="LicenseAgreementDlg"
Control="Next"
Event="NewDialog"
Value="InstallScopeDlg">LicenseAccepted = "1"</Publish>
<!-- Scope -->
<Publish Dialog="InstallScopeDlg"
Control="Back"
Event="NewDialog"
Value="LicenseAgreementDlg">1</Publish>
<Publish Dialog="InstallScopeDlg"
Control="Next"
Event="NewDialog"
Value="BrowseDlg">1</Publish>
<!-- BrowseDlg -->
<Publish Dialog="BrowseDlg"
Control="Back"
Event="NewDialog"
Value="InstallScopeDlg">1</Publish>
<Publish Dialog="BrowseDlg"
Control="Next"
Event="NewDialog"
Value="CustomizeDlg">1</Publish>
<!-- Features -->
<Publish Dialog="CustomizeDlg"
Control="Back"
Event="NewDialog"
Value="BrowseDlg">1</Publish>
<Publish Dialog="CustomizeDlg"
Control="Next"
Event="NewDialog"
Value="CustomFeaturesDlg">1</Publish>
<!--Custom Features--><!--
<Publish Dialog="CustomFeaturesDlg"
Control="Back"
Event="NewDialog"
Value="FeaturesDlg">1</Publish>
<Publish Dialog="CustomFeaturesDlg"
Control="Finish"
Event="EndDialog"
Value="Return">1</Publish>-->
<!-- Finished -->
<Publish Dialog="ExitDialog"
Control="Finish"
Event="EndDialog"
Value="Return"
Order="999">1</Publish>
<UIRef Id="WixUI_Common" />
Now the error's I'm getting are:
Error 1 ICE03: Not a valid foreign key; Table: ControlEvent, Column: Control_, Key(s): BrowseDlg.Back.NewDialog.InstallScopeDlg.1
Error 2 ICE03: Not a valid foreign key; Table: ControlEvent, Column: Control_, Key(s): BrowseDlg.Next.NewDialog.CustomizeDlg.1
Error 3 ICE03: Not a valid foreign key; Table: ControlEvent, Column: Control_, Key(s): CustomFeaturesDlg.Finish.EndDialog.Return.1
Error 4 ICE03: Not a valid foreign key; Table: ControlEvent, Column: Control_, Key(s): CustomFeaturesDlg.Back.NewDialog.FeaturesDlg.1
Error 5 ICE17: PushButton: 'Back' of Dialog: 'FeaturesDlg' does not have an event defined in the ControlEvent table. It is a 'Do Nothing' button.
I'm not too worried about the last one... But the 'Foreign' key error is driving me mad.
Can anyone point me in the right direction?
Thanks.
I guess you wanted to use InstallDirDlg instead of BrowseDlg. BrowseDlg is just the folder-selection dialog which is opened from InstallDirDlg when you want to change default installation location. It contains buttons OK and Cancel (there is no Back and Next).
Look here for an example: WiX installer fails with error code 2819 (and don't forget to add WIXUI_INSTALLDIR property - see the comments below).
For the CustomFeaturesDlg it's most likely the same story.
You can check this answer for some overall ICE info (recommended read if you are unsure what ICE checks do). Here is the ICE03 MSDN page - as you see there are many different errors possible in this particular ICE since it is concerned with overall database referential integrity (that the foreign keys match in linked tables so that they can be joined).
I have never dealt with dialogs in Wix this way, I always just use the default dialog set as explained here - it is nice and simple and does the job auto-magically. However, I have dealt with Installshield dialogs in sequence, and it is always a matter of syncing up the next and previous button events on each dialog to allow the proper dialog sequence to unfold as the buttons are pressed. It works like a doubly linked list of sorts with proper pointers to what the next dialog should be. If messed up the dialogs show up haphazardly - which can turn into very strange behavior. For example a button might not work at all, it might take you to the wrong dialog or it could even start the whole install prematurely.
In conclusion: you need to go over the compiled MSI to verify that the dialog sequence is working, both interactively whilst running the setup, and by inspecting the compiled MSI using Orca or a similar tool. Perhaps this description of Orca is helpful too? I have seen foreign key problems like this relate to capitalization or even whitespace, or a genuine mismatch.

Combining features conditions in a new property

I have a Wix setupKit I have a few features that based on them some custom dialogs will be displayed. There are some conditions that I want to combine them in a new property. For example:
<Property Id="SERVERSETTINGSISNEEDED">
<![CDATA[(&Client = 3) OR (!Client = 3) OR (&CoreService = 3) OR (!CoreService = 3) OR (&ResourceService = 3) OR (!ResourceService = 3)]]>
</Property>
and then:
<Publish Dialog="CustomizeDlg" Control="Next" Event="NewDialog" Value="ServersSettingsDlg" Order="1">SERVERSETTINGSISNEEDED</Publish>
But it does not work and always show that dialog.
Any help is appreciated.
When you define an MSI property with the <Property> element in WiX, it is constructed at build time and you end up having a record in the Property table by the time MSI package is built. On the other hand, the feature states are resolved at run time, that is, when your package is being installed.
Hence, you try to reference runtime values during build time, which is not correct. I'm sure it can be explained why the resulting property evaluates to true, but that's not the question.
As far as I understand your question, you'd like to shorten the condition part of the element in order not to copy/paste the long string a number of times. If that's the case, you don't have to involve the MSI properties here - you can achieve this with WiX preprocessor feature.
So, do the following:
define the condition as a preprocessor variable:
<?define ServerSetingIsNeeded = "(&Client = 3) OR (!Client = 3) OR (&CoreService = 3) OR (!CoreService = 3) OR (&ResourceService = 3) OR (!ResourceService = 3)>"?>
use the variable instead of condition:
<Publish Dialog="CustomizeDlg" Control="Next" Event="NewDialog" Value="ServersSettingsDlg" Order="1">$(var.ServerSetingIsNeeded)</Publish>

WIX WixUI_InstallMode property and quotes weird behavior

I faced very strange behavior of WIX installer conditions, using WixUI_InstallMode property.
It behaves completely different in conditions of element and in conditions of elements. For example, this condition correctly leads to MaintenanceTypeDlg only when remove or repair option was used:
<UI>
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="6"><![CDATA[WixUI_InstallMode = "Repair" OR WixUI_InstallMode = "Remove"]]></Publish>
</UI>
while this one never shows the previous dialog:
<UI>
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="6"><![CDATA[WixUI_InstallMode = Repair OR WixUI_InstallMode = Remove]]></Publish>
</UI>
The only difference is that "Repair" and "Remove" values are without quotes in the second example. So it looks like normal and we should put values in quotes, like it is in WIX sources.
But let's consider two more examples:
<InstallExecuteSequence>
<Custom Action="AssertUserCredentials" After="InstallValidate"><![CDATA[WixUI_InstallMode<>"Remove"]]></Custom>
</InstallExecuteSequence>
This condition is always true. So in remove mode the custom action is fired which is completely unexpected. Why is it fired?
And again example withot quotes:
<InstallExecuteSequence>
<Custom Action="AssertUserCredentials" After="InstallValidate"><![CDATA[WixUI_InstallMode<>Remove]]></Custom>
</InstallExecuteSequence>
This now works fine without quotes and don't execute custom action in uninstall mode.
As for me, these two usages of conditions are completely the same logic, but the behavior is completely opposite. Can anybody explain what happens?

checking the registration number entered by user

I was trying to use a custom dll for checking the registration number entered by user, but I run every time into “magic” behavior. In the tutorial examples http://wix.tramontana.co.hu/tutorial/events-and-actions/whats-not-in-the-book the custom action is running after ‘CostFinalize’, which normally should be before file copying procedure, but it turns out that the action runs before the very first dialog box with License Agreement appears. I have tried to solve the problem by assigning an action on the event of clicking the “next” button in registration Dialog
<Publish Dialog="MySerialCheckDlg" Control="Next" Event="DoAction" Value="CheckingPID">1</Publish>
<Publish Dialog="MySerialCheckDlg" Control="Next" Event="SpawnDialog" Value="InvalidPidDlg">PIDACCEPTED = "0"</Publish>
...
<CustomAction Id="CheckingPID" BinaryKey="CheckPID" DllEntry="CheckPID" />
<Binary Id="CheckPID" SourceFile="serialcheck.dll" />
In dll, using MsiGetProperty (hInstall, "PIDKEY", Pid, &PidLen); does not get the PIDKEY value from msi specified in MySerialCheckDlg UI
<Control Id="CDKeyEdit" Type="Edit" X="45" Y="159" Width="220" Height="16" Property="PIDKEY" Text="[PIDTemplate]" />
And in the msi the PIDACCEPTED property is not been checked in the line
<Publish Dialog="MySerialCheckDlg" Control="Next" Event="SpawnDialog" Value="InvalidPidDlg">PIDACCEPTED = "0"</Publish>
Thus, the InvalidPidDlg does not appear, and the installation process continues further.
Can you please specify the order of WiX Action Sequence, or maybe specify any other approach which can be used in this situation.
There are two sequences: InstallExecuteSequence and InstallUISequence. If MSI runs with full UI, it executes actions from InstallUISequence; in case where no UI is shown, these actions are skipped. Actions from InstallExecuteSequence are executed during the installation process, with or without UI.
First of all, is your PIDKEY property tied to an edit control? You should something similar in the dialog where you ask users to type in PIDKEY:
<Control Id="PidKeyEdit" Type="Edit" X="45" Y="105" Width="220" Height="18" Property="PIDKEY" Text="{80}" />
Type could be either Edit or MaskedEdit. Publish elements should be associated with Next button control on the dialog:
<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17"
Default="yes" Text="Next">
<Publish Event="DoAction" Value="CheckingPID">1</Publish>
<Publish Event="SpawnDialog" Value="InvalidPidDlg">PIDACCEPTED = "0"</Publish>
</Control>
Run your installation with verbose logging to see how actions are executed, and how property values change:
msiexec /i your-product.msi /lv* your-product.log

How to display a non-blocking warning for the operating system in Wix?

I already block installation of our software on operating systems where it is known not to work like this:
<Condition Message="This software needs Windows XP or newer.">
<![CDATA[VersionNT >= 501]]>
</Condition>
Now I would also like to display a non-blocking warning if the user tries to install the software on an operating system (VersionNT + Service Pack) that is not explicitly supported, even though it might work.
For example, we only explicitly support the latest service pack of each operating system.
How can I display such a warning?
I tackled this problem in 3 parts:
defining an OSWarningText property
which is only set when a warning
needs to be given
authoring a custom warning screen
Inserting the custom warning screen in the UI sequence if necessary
1. Defining an OSWarningText property
First, declare the property and give it the "unset" value by default:
<Property Id="OSWarningText" Value="{}"/>
To construct the actual value of the property, set an intermediary property for each possible warning. Make sure the conditions for each warning do not overlap:
<SetProperty Id="OSWarningText1" After="AppSearch"
Value="Detected XP SP [ServicePackLevel]. SP3 or higher is recommended.">
<![CDATA[(VersionNT = 501) AND NOT (ServicePackLevel >= 3)]]>
</SetProperty>
<SetProperty Id="OSWarningText2" After="SetOSWarningText1"
Value="Detected Vista SP [ServicePackLevel]. SP2 or higher is recommended.">
<![CDATA[(VersionNT = 600) AND NOT (ServicePackLevel >= 2)]]>
</SetProperty>
Assuming the conditions don't overlap, we can safely condense the warnings in a single property like this:
<SetProperty Id="OSWarningText" After="SetOSWarningText2"
Value="[OSWarningText1][OSWarningText2]" />
2. Authoring a custom warning screen.
This is similar to the example for adding a checkbox for the desktop shortcut. Copy one of the existing dialog definitions from the wix sources, e.g. InstallDirDlg.wxs and rename it to WarningDlg.wxs.
Set the dialog ID to Id="WarningDlg. Strip out the unnecessary controls and replace them by a warning image and our previously defined OSWarningText:
<Control Id="OSWarning" Type="Text" X="100" Y="80" Width="250" Height="60"
NoPrefix="yes" Text="[OSWarningText]" />
<Control Id="WarningIcon" Type="Icon" X="20" Y="60" Width="64" Height="64"
Text="Warning.ico" >
<Binary Id="Warning.ico" SourceFile="..\icons\warning.ico"/>
</Control>
The idea is to create something like this:
3. Inserting the custom warning screen in the UI sequence
Now we need to make sure that the warning screen is displayed between the welcome dialog and the license agreement dialog, but only if there actually is a warning to show. This is a special case of the more general branching wizard sequences problem.
Again, copy a predefined UI sequence from the wix sources, e.g. WixUI_InstallDir.wxs and rename the UI ID to Id="MyWixUI". Reference this in your main wxs file as <UIRef Id="MyWixUI" />. Now find and edit the event handlers for the WelcomeDlg next button.
You can set properties in response to a button press and an extra condition, and you can show the next dialog based on a property. We'll make use of that to handle the WelcomeDlg next button like this:
reset the WelcomeDlg_Next property
to "unset"
set the WelcomeDlg_Next property to
"WarningDlg" but only if
OSWarningText is set
set the WelcomeDlg_Next property to
"LicenseAgreementDlg" but only if
OSWarningText is NOT set.
Show the dialog given by
WelcomeDlg_Next, if the property was
correctly set.
The Wix code to do that looks like this:
<Publish Dialog="WelcomeDlg" Control="Next"
Property="WelcomeDlg_Next" Value="{}"
Order="1">1</Publish>
<Publish Dialog="WelcomeDlg" Control="Next"
Property="WelcomeDlg_Next" Value="WarningDlg"
Order="2">OSWarningText</Publish>
<Publish Dialog="WelcomeDlg" Control="Next"
Property="WelcomeDlg_Next" Value="LicenseAgreementDlg"
Order="3">NOT OSWarningText</Publish>
<Publish Dialog="WelcomeDlg" Control="Next"
Event="NewDialog" Value="[WelcomeDlg_Next]"
Order="4">WelcomeDlg_Next</Publish>
Then do the equivalent for the License Agreement "back" button: it should go back to the welcome screen if there is no warning, or else to the warning screen.