WiX Installer dialog closing after custom action - wix

I have a WiX installer that has a dialog with a button on it.
When I click the button, I want it to open a web page using the default browser.
I've managed to successfully do this, but the problem is that after I click on the button and the web page is launched, the installer closes.
What do I need to do to keep it open?
Here is my code (just the relevant parts):
<!-- the button -->
<Control Id="webpage" Type="PushButton" X="15" Y="245" Width="110" Height="17" Text="!(loc.webpage)" TabSkip="no" Default="yes" >
<!-- the button action -->
<Publish Dialog="MyDlg" Control="webpage" Event="DoAction" Value="OpenWebPage">1</Publish>
<!-- the action (is defined in a dll) -->
<Binary Id='FooBinary' SourceFile='Dll1.dll'/>
<CustomAction Id='OpenWebPage' BinaryKey='FooBinary' DllEntry='open_website' Execute='immediate' Return='check'/>

Related

Wix Progress bar in UI sequence

Can I have a modeless progress bar in UI sequence and get it updated with the custom actions in the UI sequence?
This is the page I have created, the dialog is not getting displayed nor the custom actions are getting called.
Is it possible to do this?
<Fragment>
<UI>
<!-- Progress Text To dispaly Progress Message -->
<ProgressText Action="CA_1">Doing task 1</ProgressText>
<ProgressText Action="CA_2">Doing task 2</ProgressText>
<ProgressText Action="CA_3">Doing task 3</ProgressText>
<Dialog Id="MysqlInstProgressDlg" Width="447" Height="362" Title="Mysql Progress Dialog" NoMinimize="no" Modeless="yes">
<Control Id="InstPrsTxt" Type="Text" X="67" Y="116" Width="372" Height="12" Transparent="yes">
<Text>{\FONT_DESC}</Text>
<Subscribe Event="ActionText" Attribute="Text"></Subscribe>
</Control>
<Control Id="InstPrgs" Type="ProgressBar" X="30" Y="136" Width="397" Height="17" ProgressBlocks="yes" Text="Progress done">
<Subscribe Event="SetProgress" Attribute="Progress" />
</Control>
<Control Id="DownloadBtn" Type="PushButton" X="292" Y="330" Width="66" Height="21" Default="yes">
<Text>{\FONT_DESC}Download</Text>
<Publish Event="DoAction" Value="CA_1" Order="1"><![CDATA[IS_SUCCEED = "true"]]></Publish>
<Publish Event="DoAction" Value="CA_2" Order="2"><![CDATA[IS_SUCCEED = "true"]]></Publish>
<Publish Event="DoAction" Value="CA_3" Order="3"><![CDATA[IS_SUCCEED = "true"]]></Publish>
<Publish Event="EndDialog" Value="Return" Order="4"><![CDATA[IS_SUCCEED = "true"]]></Publish>
<Publish Event="NewDialog" Value="ErrorDlg" Order="5"><![CDATA[(NOT Installed) AND IS_SUCCEED <> "true"]]></Publish>
</Control>
<Control Id="CancelBtn" Type="PushButton" Height="21" Width="66" X="368" Y="330" Cancel="yes" Default="yes">
<Text>{\FONT_DESC}Exit</Text>
<Publish Event="DoAction" Value="InstCancelConfirm_CA" Order="1"><![CDATA[1]]></Publish>
<Publish Event="NewDialog" Value="ErrorDlg" Order="2"><![CDATA[(NOT Installed) AND IS_INTERRUPTED = "true"]]></Publish>
</Control>
</Dialog>
</UI>
</Fragment>
</Wix>
UI Sequence:
<InstallUISequence>
<Show Dialog="InstWelcomeDlg2" Before="MysqlInstProgressDlg"><![CDATA[NOT Installed]]></Show>
<Show Dialog="MysqlInstProgressDlg" Before="ExecuteAction"><![CDATA[NOT Installed]]></Show>
</InstallUISequence>
Managing progress in a Windows Installer install is done in the InstallExecuteSequence, and that's where you can hook into it with this kind of MsiProcessMessage () activity:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa367525(v=vs.85).aspx
where you send INSTALLMESSAGE_PROGRESS messages.
It works this way because there is generally no need for progress or long running custom actions in the UI sequence:
The system should not be changed during the UI sequence because the install can later fail or be canceled, leaving the system changed.
A silent install does not call the UI sequence.
Prerequisites should be installed with a bootstrapper (one of your custom actions seems to be installing SQL).
Actions like populating a database or other configuration are often best done when the app first runs after the install where you are free from the constraints of custom actions, can run in a normal user environment, and more easily re-run if there are issues or more configuration later.
So if you have prerequisites use a bootstrapper, and if you are changing the system put those changes in the execute sequence as deferred custom actions where they can report progress and be undone (with rollback custom actions) if the install fails or is canceled. The short answer is that you can'r get a progress bar like that in the UI sequence.

How to show dialog or message box during uninstall in WiX?

I'm trying to show a dialog or message box(with yes or no buttons) during uninstall.
I need to set a property with user's choice from my dialog (Yes(true) or No(false)).
This property is important because all the files are going to be deleted if user's answer is "Yes".
I tried to show a custom dialog on uninstall and that didn't work. Custom dialog didn't give me an error. It doesn't even appear in the verbose log.
Here is the custom dialog:
<Dialog Id="ClearAllDataDlg" Width="260" Height="85" Title="[Setup] - [ProductName]" NoMinimize="yes">
<Control Id="No" Type="PushButton" X="132" Y="57" Width="56" Height="17" Default="yes" Cancel="yes" Text="[ButtonText_No]">
<Publish Property="CLEARALLDATA" Value="0" />
<Publish Event="EndDialog" Value="Return">1</Publish>
</Control>
<Control Id="Yes" Type="PushButton" X="72" Y="57" Width="56" Height="17" Text="[ButtonText_Yes]">
<Publish Property="CLEARALLDATA" Value="1" />
<Publish Event="EndDialog" Value="Exit">1</Publish>
</Control>
<Control Id="Text" Type="Text" X="48" Y="15" Width="194" Height="30">
<Text>Do yo want to clear all data including your settings?</Text>
</Control>
<Control Id="Icon" Type="Icon" X="15" Y="15" Width="24" Height="24" ToolTip="Information icon" FixedSize="yes" IconSize="32" Text="[InfoIcon]" />
</Dialog>
and the InstallUISequence:
<Show Dialog="ClearAllDataDlg" Before="CostFinalize">REMOVE ~= "ALL"</Show>
I tried After="MigrateFeatureStates" in the Sequence but that didn't work either.
In another question somebody asked Stopping display of custom dialog boxes in WiX uninstall that is funny because all the other questions are trying to do it's opposite.
I don't want to do this inside of a custom action because i want to block the uninstall progress and wait for the user's answer.
Is there any way to accomplish this?
Any help would be appreciated. Thank you!
I do exactly this in a SDK install we produce. The idea being that if the user has done any actual development inside the SDK install location everything is getting deleted and we want to make sure they save anything they really need.
I didn't create a new dialog for this warning box because a message box is a very well-defined and used concept in all windows products.
In product I added a custom action scheduled before anything actually happens.
<CustomAction Id='CA_UninstallWarning' BinaryKey='SDKCustomActionsDLL' DllEntry='UninstallWarning' Execute='immediate' Return='check' />
<InstallExecuteSequence>
<Custom Action='CA_UninstallWarning' Before='FindRelatedProducts'>NOT UPGRADINGPRODUCTCODE AND REMOVE~="ALL"</Custom>
...
</InstallExecuteSequence>
And in my custom action I have
[CustomAction]
public static ActionResult UninstallWarning(Session session)
{
session.Log("Begin UninstallWarning.");
Record record = new Record();
record.FormatString = session["WarningText"];
MessageResult msgRes = session.Message(InstallMessage.Warning | (InstallMessage)System.Windows.Forms.MessageBoxButtons.OKCancel, record);
session.Log("End UninstallWarning.");
if (msgRes == MessageResult.OK)
{
return ActionResult.Success;
}
return ActionResult.Failure;
}
In your case you can use messageboxbuttons.YesNo instead of OKCancel
With return="check" in your custom action, the installation will stop if you return ActionResult.Failure from the custom action.
I do have this uninstall launching from a wix bootstrapper but the behaviour should be the same.

How to display a dialog box with warning message in Wix installer?

I need to display a warning message while running my installer if a firefox maintenance service is installed.
To achieve this, I added a registry search property, and then added the code for dialog box in wix:
<Property Id="MAINTENANCESERVICEINSTALLED">
<RegistrySearch Id="MSID" Root="HKLM" Key="SOFTWARE\Mozilla\MaintenanceService" Name="Installed" Type="raw"/>
</Property>
<UI>
<Dialog Id="MaintenanceServiceWarningDialog" Width="284" Height="73" Title="Warning" NoMinimize="yes">
<Control Id="Text" Type="Text" X="38" Y="8" Width="240" Height="40" TabSkip="no">
<Text>Firefox Maintenance is installed on your system. Disable it to prevent compatibility issues. Click OK to proceed.</Text>
</Control>
<Control Id="OK" Type="PushButton" X="114" Y="52" Width="56" Height="17" Default="yes" Cancel="yes" Text="OK">
<Publish Event="EndDialog" Value="Return">1</Publish>
</Control>
</Dialog>
<InstallUISequence>
<Show Dialog="MaintenanceServiceWarningDialog" Sequence="1"> <![CDATA[NOT Installed AND MAINTENANCESERVICEINSTALLED]]></Show>
</InstallUISequence>
</UI>
I am using to show the dialog box. The above code is under "Product" tag.
I am getting build issues with this like below. And when I move the UI code to Fragment, the installer does not display anything. Not sure what's wrong.
Your InstallUISequence element is inside the UI element.

WIX Popup dialog

I have two custom dialogs - dlg1 and dlg2. After user clicks NEXT on dlg1 another custom popup dialog with some text and OK button should be shown. After user clicks OK on this popup dlg2 should appear. I've tried a lot of things but the best of it just shows dlg2 on top of dlg1 and OK-popup.
You have to create a modal dialog which passes the user from the first dialog to the second. Actually modal dialogs are used to show a message and then return focus to the dialog in which the modal dialog was called. I don't know if you are breaking any installer rules, if you don't return focus to the calling dialog but it seems to work:
Code for dlg1:
<UI>
<Dialog Id="dlg1" ...>
<Control Id="firstText" Type="Text" X="10" Y="10" Width="200" Height="17" Text="First Dialog calls Modal Dialog." />
<Control Id="PopupButton" Type="PushButton" Text="Show Popup" Height="17" Width="56" X="100" Y="243" Default="yes">
<Publish Event="SpawnDialog" Value="PopupDlg" />
</Control>
</Dialog>
</UI>
Code for PopupDlg:
<UI>
<Dialog Id="PopupDlg" ...>
<Control Id="OkButton" Type="PushButton" Text="{\Tahoma_Bold}OK" Height="17" Width="56" X="200" Y="175">
<Publish Event="NewDialog" Value="dlg2" />
</Control>
</Dialog>
</UI>
Code for dlg2:
<UI>
<Dialog id="dlg2" ...>
<Control Id="secondText" Type="Text" X="10" Y="10" Width="200" Height="17" Text="Now proceed." />
<Control Id="CancelButton" Type="PushButton" Text="Cancel" Height="17" Width="56" X="180" Y="243">
<Publish Event="EndDialog" Value="Exit" />
</Control>
</Dialog>
</UI>
UPDATE
Implementing the solution above produces some problems. There is one workaround though, but it will render your code less readable. Let me first describe the concept behind the workaround before I post some code. Basically you are going to only have two dialogs: One which triggers the popup and the popup itself. In the popup, you do not open a new window, as described above, instead you return focus to the calling dialog. Additionally you change the state of a property. The calling dialog gets updated, based upon the property that has been set by the modal dialog.
To achieve this goal, you will have to add controls for each state in the calling dialog, one for the case the property has been set and one for the case the property has not been set.
Code for callingDialog:
<UI>
<Dialog Id="callingDialog" ...>
<Control Id="BeforePopup" Type="Text" X="10" Y="10" Width="200" Height="17" Text="Here is some text." Hidden="yes">
<Condition Action="show"><![CDATA[NOT PROP_SET_BY_MODAL_DLG]]></Condition>
<Condition Action="hide"><![CDATA[PROP_SET_BY_MODAL_DLG]]></Condition>
</Control>
<Control Id="AfterPopup" Type="Text" X="10" Y="10" Width="200" Height="17" Text="Popup was shown." Hidden="yes">
<Condition Action="show"><![CDATA[PROP_SET_BY_MODAL_DLG]]></Condition>
<Condition Action="hide"><![CDATA[NOT PROP_SET_BY_MODAL_DLG]]></Condition>
</Control>
<Control Id="PopupButton" Type="PushButton" Text="Show Popup" Height="17" Width="56" X="100" Y="243" Default="yes">
<Publish Event="SpawnDialog" Value="PopupDlg" />
</Control>
</Dialog>
</UI>
Code for PopupDlg:
<UI>
<Dialog Id="PopupDlg" ...>
<Control Id="OkButton" Type="PushButton" Text="OK" Height="17" Width="56" X="200" Y="175">
<Publish Property="PROP_SET_BY_MODAL_DLG" Value="1" Order="1">1</Publish>
<Publish Event="EndDialog" Value="Return" Order="2">1</Publish>
</Control>
</Dialog>
</UI>
Found one more solution for this. It's to use WinForms dialogs from a custom action.
When user clicks NEXT button custom action is invoked:
<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="!(loc.WixUINext)">
<Publish Event="DoAction" Value="SomeAction">1</Publish>
</Control>
In this custom action you can call WinForm dialog. And don't forget to set the check for the silent install mode to make sure that the dialog will not be showed during silent install:
[CustomAction]
public static ActionResult SomeAction(Session session)
{
if(Int32.Parse(session["UILevel"]) > 3)
{
var result = MessageBox.Show("Do something?", "Popup dialog", MessageBoxButtons.YesNo);
session["SOMEPROP"] = result == DialogResult.Yes ? "True" : "False";
}
return ActionResult.Success;
}

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.