Inserting Custom Action between Dialogs (InstallUISequence) in WiX - 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.

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.

Use WiX browser dialog to set edit box value

I'm trying to create a WiX installer dialog that provides a series of textboxes that users need to fill in with directory locations.
What I would like to do is put a Browse button next to each dialog and when they click it, the WiX Browse dialog will come up, they select a file location, click OK, and the text box next to the browse button will be filled in.
I know how to do it with a custom action, but I was wondering if there was a pure WiX way of doing this.
EDIT: I should be more clear. I meant directory locations, not file locations. Wix doesn't have file browsing support as one user indicated below.
I found a way to do it completely in WiX. WiX comes with a browse dialog called BrowseDlg. Here's what I did:
I created a dialog that includes a PathEdit control and PushButton control. Notice that the PathEdit control has the Indirect property set to yes. This means that whatever you set Property to is just a pointer to something else.
<Dialog Id="BackupConfigDlg" Width="370" Height="270" Title="Backup Configuration">
<Control Type="Text" Id="lblInstructions" Width="348" Height="13" X="10" Y="10">
<Text>{\WixUI_Font_Title}Please select the directory you want to backup.</Text>
</Control>
<Control Type="Text" Id="lblBackupDirectory" Width="69" Height="9" X="10" Y="40" Text="Backup directory:">
</Control>
<Control Type="PathEdit" Id="Folder" Width="219" Height="15" X="82" Y="38" Property="_BrowseProperty" Indirect="yes" />
<Control Type="PushButton" Id="Browse" Width="56" Height="17" X="304" Y="37" Text="Browse..." />
<Control Type="Line" Id="line" Width="362" Height="2" X="4" Y="229" />
<Control Id="Cancel" Type="PushButton" X="239" Y="240" Width="56" Height="17" Cancel="yes" Text="Cancel">
<Publish Event="SpawnDialog" Value="CancelDlg">1</Publish>
</Control>
<Control Type="PushButton" Id="Install" Width="56" Height="17" X="300" Y="240" Text="Install">
<Publish Event="EndDialog" Value="Return" />
</Control>
</Dialog>
The browse dialog (that we'll eventually get to) expects to set an object in the Directory table, so we need to create a Directory object that will only be used to hold the value we browse to. Since we won't put any components in it, nothing on the file system will change relating to the directory we choose. I call mine TARGETBACKUPDIRECTORY.
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="TARGETBACKUPDIRECTORY">
</Directory>
...
</Directory>
Now we need to create a property that points to the Directory object.
<Property Id="BACKUPDIRECTORY" Value="TARGETBACKUPDIRECTORY" />
We now need to make sure that the _BrowserProperty property is properly bound to BACKUPDIRECTORY (because it points to the Directory object we want set) before this dialog opens. If you don't, you will get an error during the install process when you attempt to open the dialog. In my example, PrevDlg is a dialog that appears before BackupConfigDlg. What's happening here is that when the Next button is pushed, I set the _BrowserProperty property to BACKUPDIRECTORY, I then open the dialog. It must take place in that order so I use the Order property to enforce it. I do the same thing when the browse button is pushed, not sure I need to do, but I do it just for safe measure.
<Publish Dialog="PrevDlg" Control="Next" Property="_BrowseProperty" Value="[BACKUPDIRECTORY]" Order="1">1</Publish>
<Publish Dialog="PrevDlg" Control="Next" Event="NewDialog" Value="BackupConfigDlg" Order="2">1</Publish>
<Publish Dialog="BackupConfigDlg" Control="Browse" Property="_BrowseProperty" Value="[BACKUPDIRECTORY]" Order="1">
</Publish>
<Publish Dialog="BackupConfigDlg" Control="Browse" Event="SpawnDialog" Value="BrowseDlg" Order="2">
</Publish>
That's what worked for me.
The selected answer to this question is way too much work. You don't need to do all that.
Set your PathEdit control to the directory to configure as you normally would. Then, in the actions of the browse button, set _BrowseProperty to the NAME (not value of) of your property to configure and then SpawnDialog. That's it.
<Control Type="PathEdit"
Id="TxtDir"
Width="155" Height="15"
X="105" Y="57"
Property="OUTPUTDIRECTORY"/>
<Control Id="btnDirBrowse"
Type="PushButton"
Width="56" Height="17"
X="260" Y="57"
Text="Browse..." >
<Publish Property="_BrowseProperty" Value="OUTPUTDIRECTORY" Order="1">1</Publish>
<Publish Event="SpawnDialog" Value="BrowseDlg" Order="2">1</Publish>
</Control>
In order to have multiple directories (filled by the BrowseDlg) in the same dialog window, an extra indirection is required. Notice also the numbering within the <Publish></Publish> tags :
<Control Id="WorkingDirFolderLabel" Type="Text" Width="220" Height="12" X="10" Y="50" Text="Working directory:"/>
<Control Id="WorkingDirFolder" Type="PathEdit" Width="250" Height="17" X="10" Y="62" Property="_WorkingDirBrowseProperty" Indirect="yes"/>
<Control Id="WorkingDirBrowse" Type="PushButton" Width="56" Height="17" X="265" Y="62" Text="Browse..." >
<Publish Property="_BrowseProperty" Value="[_WorkingDirBrowseProperty]" Order="2">1</Publish>
<Publish Event="SpawnDialog" Value="BrowseDlg" Order="3">1</Publish>
</Control>
<Control Id="DocsDirFolderLabel" Type="Text" Width="220" Height="12" X="10" Y="100" Text="Documentation area:"/>
<Control Id="DocsDirFolder" Type="PathEdit" Width="250" Height="17" X="10" Y="112" Property="_DocsDirBrowseProperty" Indirect="yes" />
<Control Id="DocsDirBrowse" Type="PushButton" Width="56" Height="17" X="265" Y="112" Text="Browse..." >
<Publish Property="_BrowseProperty" Value="[_DocsDirBrowseProperty]" Order="2">2</Publish>
<Publish Event="SpawnDialog" Value="BrowseDlg" Order="3">2</Publish>
</Control>
Then pass the references as before (no need to define extra properties) :
<Publish Dialog="PrevDlg" Control="Next" Property="_WorkingDirBrowseProperty" Value="TARGETWORKINGDIRECTORY" Order="1">1</Publish>
<Publish Dialog="PrevDlg" Control="Next" Property="_DocsDirBrowseProperty" Value="TARGETDOCSDIRECTORY" Order="1">1</Publish>
<Publish Dialog="PrevDlg" Control="Next" Event="NewDialog" Value="BackupConfigDlg" Order="2">1</Publish>
Windows Installer doesn't support file browsing, so there is no direct support for this in WiX. The best solution is still a custom action executed when clicking browse button.
You can find a sample custom action here: http://www.installsite.org/pages/en/msi/ca.htm
None of the above or anywhere else worked for me. What did work is so more simple and straight forward.
Like many, I need to prompt the Installer user for the target locations of SQL Server .mdf and .ldf files, which will likely reside outside of any pre-installation Directory structure. In fact, my preceding Dialog prompts the user for the target database server. Given that, I then custom act on that preceding Dialog's "Next" button to (a) find the mdf & ldf paths of the server's "master" database and then (b) default the new database's respective paths to those of the master db.
But hours of frustration making PathEdit play nice with BrowseDlg proved futile. What I ended up doing was creating a placeholder Property called PATH_TEMP_BROWSE. It is the "SetTarget" call in the BrowseDlg that forces us to enlist the Wix Directory tree. Get rid of that line in your own custom rip-off of BrowseDlg and instead pass around the user's selected directory via PATH_TEMP_BROWSE thusly:
<Dialog Id="DirectoryBrowserDlg" Width="370" Height="270" Title="Dir Browse">
<Control Id="Path" Type="PathEdit" X="25" Y="202" Width="320" Height="18" Property="PATH_TEMP_BROWSE" Indirect="yes" />
<Control Id="OK" Type="PushButton" X="240" Y="243" Width="56" Height="17" Default="yes" Text="!(loc.WixUIOK)">
<!-- NO! -->
<!--<Publish Event="SetTargetPath" Value="[_BrowseProperty]">1</Publish>-->
<Publish Event="EndDialog" Value="Return">1</Publish>
</Control>
...
<Control Id="DirectoryCombo" Type="DirectoryCombo" X="70" Y="55" Width="220" Height="80" Property="PATH_TEMP_BROWSE" Indirect="yes" Fixed="yes" Remote="yes">
<Subscribe Event="IgnoreChange" Attribute="IgnoreChange" />
</Control>
...
<Control Id="DirectoryList" Type="DirectoryList" X="25" Y="83" Width="320" Height="98" Property="PATH_TEMP_BROWSE" Sunken="yes" Indirect="yes" TabSkip="no" />
...
</Dialog>
Then, to make use of my pop-up modal DirectoryBrowserDlg within my installation Dialog that prompts the user for the new database name as well as its logical files...
<Control Id="MdfPath"
Type="PathEdit"
X="37"
Y="184"
Width="313"
Height="18"
Property="PATH_DBMDFCS"
/>
<Control Id="MdfBrowse"
Type="PushButton"
X="350"
Y="184"
Width="22"
Height="17"
Text="Browse..."
>
<Publish Property="PATH_TEMP_BROWSE" Value="PATH_DBMDFCS" Order="1">1</Publish>
<Publish Event="SpawnDialog" Value="DirectoryBrowserDlg" Order="2">1</Publish>
<Publish Property="PATH_DBMDFCS" Value="PATH_TEMP_BROWSE" Order="3" />
</Control>
Do the same for your .ldf file.
K.I.S.S
I have a very easy solution. We can use the ConfigurableDirectory attribute of feature to enable the browse directory. It works well for me.
<Feature Id="OCMSnapshotConfigAppFeature" Title="OCM Snapshot Configuration" Level="1" ConfigurableDirectory="INSTALLDIR">

Install Sequence

I am writing an installer which has a EULA, and then a dialog to ask whether they want to do a 'custom install', or 'default install'.
If they select custom they get a bunch of dialogs relating to SQL settings (as shown below with the next button starting the 'StartupMenu' dialog if they have selected custom install).
If they select default the installer should skip to the 'installing' stage and not ask any more questions. How do I do this?
My next button is defined like this,
<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Disabled="no" Text="Next" >
<Publish Event="NewDialog" Value="StartupMenu" >CUSTOMINSTALL = 1</Publish>
<Publish Event="EndDialog" Value="Return">CUSTOMINSTALL = 0</Publish>
</Control>
When CUSTOMINSTALL = 1 it is fine, but it is the next line, what do I have to put with CUSTOMINSTALL = 0 to make it go straight to the installation stage?
an update on this one. I found that the first issue was with my InstallUISequence table, I had to make it like this,
<InstallUISequence>
<Custom Action="CreateConnectionStrings" After="ExecuteAction">NOT Installed</Custom>
<Show Dialog="CustomInstall" After="WelcomeEulaDlg" >NOT Installed</Show>
<Show Dialog="StartupMenu" After="CustomInstall" >NOT Installed and CUSTOMINSTALL = 1</Show>
<Show Dialog="iCalibraDatabaseSelection" After="StartupMenu">NOT Installed and CUSTOMINSTALL = 1</Show>
<Show Dialog="GlobalStoreDatabaseSelection" After="iCalibraDatabaseSelection">NOT Installed and CUSTOMINSTALL = 1</Show>
</InstallUISequence>
Note that on StartupMenu, iCalibraDatabaseSelection, and GlobalStoreDatabaseSelection dialogs I have added the new condition CUSTOMINSTALL = 1.
This solves my problem when I select the 'default install' option. This skips to the installation process.
The problem I have now is when I select the 'custom install' option. I go through each of the above dialogs and the GlobalStoreDatabaseSelection dialog should be the last dialog. On that dialog I have this,
<Control Id="Install" Type="PushButton" X="236" Y="243" Width="56" Height="17" Disabled="no" Text="Install" >
<Publish Event="EndDialog" Value="Return"></Publish>
</Control>
But this does not progress the installer to do the actual installation. It jumps back to the StartupMenu dialog.
So I guess what does Event="EndDialog" Value="Return" actually do? Where does it jump to?