How to link custom action to control event - wix

I'm studying Wix to build product installer. I've customized the UI successfully but be wondering how to link a custom action to control event (i.e PushButton).
I have 2 projects:
Product.Wix.CustomActions
[CustomAction]
public static ActionResult CustomAction1(Session session)
{
session.Log("Begin CustomAction1");
MessageBox.Show("CustomActions1");
return ActionResult.Success;
}
Product.Wix.Setup (referenced to Product.Wix.CustomActions project). In Setup.wxs, I have declared a custom action:
<Binary Id="CustomActions" SourceFile="..\Product.Wix.CustomActions\bin\Debug\Product.Wix.CustomActions.CA.dll" />
<CustomAction Id='Action1' BinaryKey='CustomActions' DllEntry='CustomAction1' Execute='immediate' Return='check' />
I have a custom dialog with Connect button and wiring to the action as below:
<Control Id="Connect" Type="PushButton" X="325" Y="75" Width="30" Height="17" Text="...">
<Publish Event="DoAction" Value="Action1">1</Publish>
</Control>
It does not work as I expected it should pop-up a message box when clicking on the Connect button.

Am not sure whether MessageBox.Show() will work. Also its better to go with WIX dialogs as you can capture the option selected by user on the popup.
Example
<Control Id="TestConn" Type="PushButton" X="265" Y="205" Width="70" Height="18" Text="&Test Connection">
<Publish Event="DoAction" Value="Action1">1</Publish>
<Publish Property="ERRORMSG" Value="CustomActions1">ACCEPTED = "1"</Publish>
<Publish Event="SpawnDialog" Value="InvalidDBConnDlg">ACCEPTED = "0"</Publish>
</Control>
<Dialog Id="InvalidDBConnDlg" Width="260" Height="120" Title="[ProductName]">
<Control Id="OK" Type="PushButton" X="102" Y="90" Width="56" Height="17" Default="yes" Cancel="yes" Text="OK" />
<Control Id="Text" Type="Text" X="48" Y="22" Width="194" Height="60" Text="[MSGVAR]" />
<Control Id="Icon" Type="Icon" X="15" Y="15" Width="24" Height="24" ToolTip="Information icon" FixedSize="yes" IconSize="32" Text="WixUI_Ico_Info" />
</Dialog>
Custom Action
[CustomAction]
public static ActionResult CustomAction1(Session session)
{
session["MSGVAR"] = "Some Message";
return ActionResult.Success;
}

The log file shows my custom action assemblies could not be loaded properly. The reason is I have unintentionally removed the section:
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0" />
</startup>
from the config file. Added it back and everything works now.

Related

To validate the input controls in WIX Installer

I'm new to WIX. I wanted to validate the input controls like Textbox is not null and password and confirm password is same.
I tried to do it in the custom action,but i couldn't send the parameters.
If at all i send the parameter how to the return the values with the to stay in the same installation page.
<Dialog Id="XXX" Width="370" Height="270" Title="Installation">
<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="!(loc.WixUINext)" >
<Publish Event="DoAction" Value="CheckingPID">1</Publish>
<Publish Event="SpawnDialog" Value="InvalidPidDlg">PIDACCEPTED = "0"</Publish>
<Control Id="Usernamelbl" Type="Text" X="20" Y="100" Width="95" Height="10" NoPrefix="yes" Property="WIXUI_INSTALLDIR" Text="Username:" />
<Control Id="UsernameVal" Type="Edit" X="125" Y="100" Width="200" Height="17" Property="SETUSERNAME" Indirect="no" Disabled="no" />
</Dialog>
<CustomAction Id="CheckingPID" BinaryKey="CustomActionBinary" Impersonate="no" DllEntry="Validate" Execute="immediate" Return="check"/>
[CustomAction]
public static ActionResult Validate(Session session)
{
MessageBox.Show(Session.CustomActionData["SETUSERNAME"]);
return ActionResult.Success;
}
Is this the right way to validate or any other way to validate.
Thanks in advance
Here is a Working Custom Wix Dialog
This code creates a custom UI Dialog with Custom Actions. Purpose of this Dialog is while we are installing a Desktop Application we can setup Db Connection information.
DbConnectionInfo.wxs
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<Property Id="Server" Value="127.0.0.1" />
<Property Id="Port" Value="XXX" />
<Property Id="Database" Value="DbName" />
<Property Id="User" Value="root" />
<Property Id="Password" Value="1234abcA" />
<UI Id="DbConnectionDlgUI">
<Dialog Id="DbConnectionDlg" Width="400" Height="275" Title="Demo : Database Connection Settings">
<Control Id="headerText" Type="Text" X="140" Y="10" Width="260" Height="40" Transparent="no"
Text="{\WixUI_Font_Title}Database Connection Settings Screen" />
<Control Id="SideBar" Type="Bitmap" Text="WixUIBannerBmp" X="0" Y="0" Height="240" Width="130" Image="yes" />
<Control Id="explanationText" X="140" Y="50" NoWrap="no" RightAligned="no" Transparent="yes"
Type="Text" Width="260" Height="100"
Text="{\WixUI_Font_Normal}Before you can use this Service, you need to provide your My Sql Connection settings which is used getting the email database information. If you choose not to install this application, click on the Cancel button to exit." />
<Control Id="ServerLabel" Type="Text" X="160" Y="120" Height="17" Width="65" Transparent="yes" Text="{\WixUI_Font_Normal}Server:" />
<Control Id="ServerTextBox" Type="Edit" X="230" Y="120" Height="17" Width="60" Property="Server" />
<Control Id="PortLabel" Type="Text" X="295" Y="120" Height="17" Width="21" Transparent="yes" Text="{\WixUI_Font_Normal}Port:" />
<Control Id="PortTextBox" Type="Edit" X="325" Y="120" Height="17" Width="30" Property="Port" />
<Control Id="DatabaseLabel" Type="Text" X="160" Y="140" Height="17" Width="65" Transparent="yes" Text="{\WixUI_Font_Normal}Database:" />
<Control Id="DatabaseTextbox" Type="Edit" X="230" Y="140" Height="17" Width="120" Property="Database" />
<Control Id="UserLabel" Type="Text" X="160" Y="160" Height="17" Width="65" Transparent="yes" Text="{\WixUI_Font_Normal}User:" />
<Control Id="UserTextbox" Type="Edit" X="230" Y="160" Height="17" Width="120" Property="User" />
<Control Id="PasswordLabel" Type="Text" X="160" Y="180" Height="17" Width="65" Transparent="yes" Text="{\WixUI_Font_Normal}Password:" />
<Control Id="PasswordTextbox" Type="Edit" X="230" Y="180" Height="17" Width="120" Property="Password" Password="yes"/>
<Control Id="bottomLine" Type="Line" X="130" Y="239" Width="270" Height="1"/>
<Control Id="Back" Type="PushButton" Text="Back" X="208" Y="248" Height="17" Width="60" >
</Control>
<Control Id="Next" Type="PushButton" Text="Next" X="269" Y="248" Height="17" Width="60" Default="yes">
<Publish Event="DoAction" Value="CreateDbConnectionProperties">1</Publish>
</Control>
<Control Id="Cancel" Type="PushButton" Text="Cancel" X="330" Y="248" Height="17" Width="60" Cancel="yes">
<Publish Event="DoAction" Value="CleanUpAction">1</Publish>
<Publish Event="NewDialog" Value="CancelDlg" Order="2">1</Publish>
</Control>
</Dialog>
</UI>
</Fragment>
<Fragment>
<CustomAction Id="CreateDbConnectionProperties" BinaryKey="CustomActionBinary" DllEntry="CreateDbConnectionProperties" />
</Fragment>
</Wix>
CustomAction.cs
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Deployment.WindowsInstaller;
using System.IO;
namespace Demo.InstallerActions
{
public class CustomlActions
{
private readonly static string AppName = "Demo";
[CustomAction]
public static ActionResult CreateDbConnectionProperties(Session session)
{
session.Log("Saving Db Details Started.");
try
{
string Server = session["Server"].Encrypt(AppConstants.SecurityKey,true);
string Database = session["Database"].Encrypt(AppConstants.SecurityKey, true);
string User = session["User"].Encrypt(AppConstants.SecurityKey, true);
string Password = session["Password"].Encrypt(AppConstants.SecurityKey, true);
string Port = session["Port"].Encrypt(AppConstants.SecurityKey, true);
string[] confData = { Server, Database, User, Password, Port };
string appdataPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
if (!Directory.Exists($"{appdataPath}\\{AppName}"))
{
Directory.CreateDirectory($"{appdataPath}\\{AppName}");
}
File.WriteAllLines($"{appdataPath}\\{AppName}\\conf.sys", confData);
session.Log("Db Details Saved");
return ActionResult.Success;
}
catch (Exception ex)
{
session.Log($"Configuration File Creation Failed with Error: {ex.Message}");
return ActionResult.Failure;
}
}
[CustomAction]
public static ActionResult CleanUpAction(Session session)
{
session.Log("Cleanup Started.");
try
{
string appdataPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
if (!Directory.Exists($"{appdataPath}\\{AppName}"))
Directory.Delete($"{appdataPath}\\{AppName}", true);
session.Log("Db Details Saved");
return ActionResult.Success;
}
catch (Exception ex)
{
session.Log($"Cleanup Error: {ex.Message}");
return ActionResult.Failure;
}
}
}
}

WIX: do I have to manually recreate all elements of a dialog, including banners and buttons?

New to WIX here :-)
I added a new dialog to my WIX setup project, and it works well, except that this dialog ONLY shows the controls I added to it - it doesn't have the next/back/cancel buttons or the banner that the other dialogs have.
Did I do something wrong or do I have to manually recreate all controls, inclulding banners? If I have to manually recreate them, where do I find the WXS files that contains the originals?
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<UI>
<RadioButtonGroup Property="MyApp_Database_Type">
<RadioButton Height="17" Text="Dedicated" Value="Dedicated" Width="348" X="0"
Y="0" />
<RadioButton Height="17" Text="Shared" Value="Shared" Width="348" X="0"
Y="18" />
<RadioButton Height="17" Text="Single User" Value="Single User" Width="348" X="0"
Y="36" />
</RadioButtonGroup>
<Dialog Id="MyApp_UI_DatabaseProperties" X="50" Y="50" Width="373" Height="287"
Title="[ProductName]: Database Properties">
<Control Id="CTL_MyApp_UI_DatabaseProperties" Type="RadioButtonGroup" X="18" Y="108" Width="348" Height="48"
Property="MyApp_Database_Type" Text="System Type" TabSkip="no" />
<Control Id="Back" Type="PushButton" X="156" Y="243"
Width="56" Height="17" Hidden="no" Disabled="no" Text="Back" />
<Control Id="Next" Type="PushButton"
X="212" Y="243" Width="80" Height="17" Default="yes"
Text="Next" Hidden="no" Disabled="no">
</Control>
</Dialog>
</UI>
</Fragment>
</Wix>
Yes you have to fully define the dialog you want to use. Each dialog is a self contained and describing thing. It doesn't know about the layout or format of any other dialogs in the installation.
You can see examples of the dialogs you are probably using with the WixUIExtension.dll right here. This is the source code of the UIExtension's wixlib.
You should be able to simply copy over the shared components from the other dialogs into the one you are describing to get it working as you expect.

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;
}

Text template stop working after manual edit

In my installer I want user to connect to database. I support 4 database types in my product.
In the connect to database dialog I created, there is a ComboBox control with all supported database types, Edit control where user suppose to enter the connection string and a PushButton, when pressed it will show connection string text template in Edit control according to selected database type in ComboBox. Now, the problem is:
User clicks Show Template button when MSSQL is selected
User alters manually place holder in text template connection string in Edit
control
User realize that he needs MySQL connection
User change value in ComboBox to MySQL and clicks Show Template button and nothing
happens.
To summarize this, after Edit control were manually altered, the Show Template stops working.
Here is WiX code use:
<Fragment>
<!-- Supported databases templates -->
<Property Id="MSSQLTemplate" Value="Data Source=localhost;Initial Catalog=[database];Integrated Security=yes"/>
<Property Id="MySQLTemplate" Value="Server=localhost;Uid=[username];Pwd=[password];Database=[database];" />
<Property Id="DB2Template" Value="Server=localhost;Uid=[username];Pwd=[password];Database=[database];" />
<Property Id="OracleTemplate" Value="Data Source=[database];User Id=[username];Password=[password];" />
<Property Id="PROP_DATABASE_TYPE">MSSQL</Property>
<Property Id="PROP_CONNECTIONSTRING"></Property>
<Binary Id="CA_DLL" SourceFile="$(var.CustomActions.TargetDir)CustomActions.CA.dll" />
<CustomAction Id="caShowTemplate" BinaryKey="CA_DLL" DllEntry="ShowTemplate" Execute="immediate" />
<UI Id="InstallDlg_UI">
<TextStyle Id="Tahoma_Regular" FaceName="Tahoma" Size="8" />
<Property Id="DefaultUIFont" Value="Tahoma_Regular" />
<Dialog Id="InstallDlg" Width="370" Height="270" Title="Amazing Software" NoMinimize="no">
<!-- Database type -->
<Control Id="lblDatabaseType" Type="Text" X="20" Width="100" Y="60" Height="18" NoPrefix="yes" Text="Database Type" />
<Control Id="cbDatabaseServer" Type="ComboBox" X="120" Width="90" Y="60" Height="18" Property="PROP_DATABASE_TYPE" ComboList="yes" Sorted="yes">
<ComboBox Property="PROP_DATABASE_TYPE">
<ListItem Text="MSSQL" Value="MSSQL" />
<ListItem Text="MySQL" Value="MySQL" />
<ListItem Text="Oracle" Value="Oracle" />
<ListItem Text="DB2" Value="DB2" />
</ComboBox>
</Control>
<Control Id="btnShowTemplate" Type="PushButton" X="215" Y="60" Width="85" Height="17" Text="Show Template">
<Publish Event="DoAction" Value="caShowTemplate" Order="1">1</Publish>
<Publish Property="PROP_CONNECTIONSTRING" Value="[PROP_CONNECTIONSTRING]" Order="2">1</Publish>
</Control>
<!-- Connection string -->
<Control Id="lblConnectionString" Type="Text" X="20" Width="100" Y="85" Height="18" NoPrefix="yes" Text="Connection String" />
<Control Id="tbConnectionString" Type="Edit" X="120" Width="180" Y="85" Height="18" Property="PROP_CONNECTIONSTRING" Text="[PROP_CONNECTIONSTRING]" />
<Control Id="CancelButton" Type="PushButton" Text="Cancel" Height="17" Width="56" X="180" Y="243" Cancel="yes">
<Publish Event="EndDialog" Value="Exit" />
</Control>
</Dialog>
<InstallUISequence>
<Show Dialog="InstallDlg" Before="ExecuteAction" />
</InstallUISequence>
</UI>
</Fragment>
And a custom action written in C#:
[CustomAction]
public static ActionResult ShowTemplate(Session session)
{
string selectedDatabase = string.Format("{0}Template", session["PROP_DATABASE_TYPE"]);
session["PROP_CONNECTIONSTRING"] = session[selectedDatabase];
return ActionResult.Success;
}
What am I doing wrong?
Your code doesn’t have any issue. It is a well-known limitation of WIX UI. Check the below discussions for more details.
http://windows-installer-xml-wix-toolset.687559.n2.nabble.com/UI-Edit-Box-not-updating-td5077648.html
Wix Interactions with Conditions, Properties & Custom Actions

Using a WiX property from a browse dialog

I'm developing a setup project using WiX, and I have the following problem. I get a directory path from the user using the Browse dialog, and I need to put this path in web.config. The problem is that in web.config that puts the value in "WWWMain" and not the path chosen by the user.
This is my code:
Product.wxs
<Property Id="IISLOGDIRECTORY" Value="WWWMain" />
Dialog.wxs
<Control Id="IISLogDirectoryEdit" Type="PathEdit" X="45" Y="100" Width="220" Height="18" Disabled="yes" Property="IISLOGDIRECTORY" Indirect="yes" />
Installation.wxs
<util:XmlFile Id="ModifyIISLogDirectory"
Action="setValue"
Permanent="yes"
ElementPath="/configuration/appSettings/add[\[]#key='isslogdirectory'[\]]/#value"
File="[INSTALLLOCATION]Web\Web.config"
Value="[IISLOGDIRECTORY]"/>
Declare the variable in Dialog.wxs itself but after the control
Example
<Control Id="DiffBackUpEdit" Type="PathEdit" X="120" Y="157" Width="160" Height="18" Property="IISLOGDIRECTORY">
</Control>
<Control Id="Browse12" Type="PushButton" X="290" Y="157" Width="56" Height="17" Text="Browse">
<Publish Property="_BrowseProperty" Value="DIFFDBBACKUPLOC" Order="1">1</Publish>
<Publish Event="SpawnDialog" Value="BrowseDlg" Order="2">1</Publish>
</Control>
Then at bottom in same page after add
<Property Id="IISLOGDIRECTORY" Value="C:\Database\MDM"/>