InjectClrDialog throw exception

Sep 23, 2016 at 9:58 AM
Hi, Oleg!
I tried to Inject 2 dialogs.

var project = new Project("CustomDialogTest",
                new Dir(@"%ProgramFiles%\My Company\My Product",
                    new File("setup.exe")));        
project.InjectClrDialog("ShowCustomDialog", NativeDialogs.InstallDirDlg, NativeDialogs.VerifyReadyDlg);
project.RemoveDialogsBetween(NativeDialogs.WelcomeDlg, NativeDialogs.InstallDirDlg);
project.InjectClrDialog("ShowEmptyDialog", NativeDialogs.WelcomeDlg, NativeDialogs.InstallDirDlg);

Second InjectClrDialog function throw exception:
An unhandled exception of type 'System.ApplicationException' occurred in WixSharp.dll
Additional information: Project.CustomUI is already initialized. Ensure InjectClrDialog is invoked before any adjustments made to CustomUI.
Sep 24, 2016 at 1:45 AM
The CLR Dialog injection technique is designed for a single dialog only. Injection is a valid but an intrusive technique. Thus I only planed it for a single 'gentle' insertion and if it's used any different way the compile and runtime behavior will become unpredictable.

If you need to inject more than a single dialog then insert a panel which can host full size sub-panels that are visible only one at the time. This will create the effect of proceeding to the next step of the installation on 'Next' button click.

Judging from all your questions I feel that you are going through all this pain because you are doing too much configuration in your setup. I don't know if you have that luxury but I encourage you to reconsider your deployment strategy.

Deployment responsibility is only to deploy the files. The only user input the deployment should take is the one that controls how files need to be deployed (e.g. which files to install, install location). The configuration (e.g. IP addresses, serial numbers) is a responsibility of your application. Thus your deployment should always start some sort of a config utility at the end of the installation. Or application itself in a pure config mode. If you follow this approach then you don't need to deal with complicated UI scenarios, challenging product detection techniques and so on.

Read more about the matter here:
Sep 28, 2016 at 5:16 PM
Hello, Oleg!
I have a similar question.
I created 2 CustomCLRDialogs

public partial class SQLCredentialsPage : WixCLRDialog

public partial class ChunksConfigurationPage : WixCLRDialog

create CustomActions for them:
    public static ActionResult ShowChunksConfigurationDialog(Session session)
        return WixCLRDialog.ShowAsMsiDialog(new ChunksConfigurationPage());

    public static ActionResult ShowSQLCredentialsDialog(Session session)
        return WixCLRDialog.ShowAsMsiDialog(new SQLCredentialsPage(session));

    Then I created custom UI with injection to it of my first custom page
public static void BuildCustomUI(Project project)
        ManagedAction sqlCredentialsManagedActionDialog = new ShowClrDialogAction("ShowSQLCredentialsDialog");        
        ManagedAction chunksFolderSettingsDialog = new ShowClrDialogAction("ShowChunksConfigurationDialog");

        project.Actions = project.Actions.Add(sqlCredentialsManagedActionDialog);          
        project.Actions = project.Actions.Add(chunksFolderSettingsDialog);

        project.UI = WUI.WixUI_Common;

        var customUI = new CommomDialogsUI();

        var prevDialog = NativeDialogs.LicenseAgreementDlg;
        var nextDialog = NativeDialogs.InstallDirDlg;

        customUI.UISequence.RemoveAll(x => (x.Dialog == prevDialog && x.Control == Buttons.Next) ||
                                           (x.Dialog == nextDialog && x.Control == Buttons.Back));

        customUI.On(prevDialog, Buttons.Next, new ExecuteCustomAction(sqlCredentialsManagedActionDialog.Id));
        customUI.On(prevDialog, Buttons.Next, new ShowDialog(nextDialog, Condition.ClrDialog_NextPressed + " AND LicenseAccepted = \"1\""));
        customUI.On(prevDialog, Buttons.Next, new CloseDialog("Exit", Condition.ClrDialog_CancelPressed) { Order = 2 });

        customUI.On(nextDialog, Buttons.Back, new ExecuteCustomAction(sqlCredentialsManagedActionDialog.Id));
        customUI.On(nextDialog, Buttons.Back, new ShowDialog(prevDialog, Condition.ClrDialog_BackPressed));
        project.CustomUI = customUI;
This works great. Then, if I put my second customUI page(chunksFolderSettingsDialog) between NativeDialogs pages(for example, between NativeDialogs.InstallDirDlg and NativeDialogs.VerifyReadyDlg) this work great too. Both custom pages works. But I need run them one after another. Can I do this?
Sep 30, 2016 at 3:20 PM
I find how to run one customUI page after another, my code looks like this:
        ManagedAction chunksFolderSettingsDialog = new ShowClrDialogAction("ShowChunksConfigurationDialog");
        ManagedAction clientConfigurationDialog = new ShowClrDialogAction("ShowClientConfigurationDialog");

        project.Actions = project.Actions.Add(chunksFolderSettingsDialog);
        project.Actions = project.Actions.Add(clientConfigurationDialog);

        var prevDialog = NativeDialogs.LicenseAgreementDlg;
        var nextDialog = NativeDialogs.InstallDirDlg;

       customUI.UISequence.RemoveAll(x => (x.Dialog == prevDialog && x.Control == Buttons.Next) ||
                                                                      (x.Dialog == nextDialog && x.Control == Buttons.Back));
        customUI.On(prevDialog, Buttons.Next, new ExecuteCustomAction(chunksFolderSettingsDialog));
        customUI.On(prevDialog, Buttons.Next, new ExecuteCustomAction(clientConfigurationDialog, Condition.ClrDialog_NextPressed));
        customUI.On(prevDialog, Buttons.Next, new ShowDialog(nextDialog, Condition.ClrDialog_NextPressed));

        customUI.On(nextDialog, Buttons.Back, new ExecuteCustomAction(clientConfigurationDialog) { Condition =  Condition.ClrDialog_BackPressed });
        customUI.On(nextDialog, Buttons.Back, new ExecuteCustomAction(chunksFolderSettingsDialog) { Condition = Condition.ClrDialog_BackPressed });
        customUI.On(nextDialog, Buttons.Back, new ShowDialog(prevDialog, Condition.ClrDialog_BackPressed) { Condition = Condition.ClrDialog_BackPressed });

        customUI.On(prevDialog, Buttons.Cancel, new CloseDialog("Exit", Condition.ClrDialog_CancelPressed));
But now, when I tried to go Back from the 'nextDialog'(opens page 'clientConfigurationDialog' as must) ,after that again press 'Next', it turn me not on the 'nextDialog' but to 'chunksFolderSettingsDialog'...
it's all because in this code " customUI.On(nextDialog, Buttons.Back, new ExecuteCustomAction(chunksFolderSettingsDialog) { Condition = Condition.ClrDialog_BackPressed });" How I understand I can't specify chunksFolderSettingsDialog instead of 'nextDialog', because it's not a dialog, it's an action...
How can I workaround this? I tried to fix this almost all the day, but all was ineffectually...
Oct 2, 2016 at 4:53 AM
As I mentioned CLR dialog injection is designed to be an ad hoc solution for a single page/step dialog. It was developed before external UI support became available with MSI/WiX. After external UI support was released managed full UI (not CLRDialog) became the preferred custom UI approach. While managed UI is still a recommended techniques CLRDialog approach still serves important role because Burn has a bug that prevents any msi external UI (e.g. ManagedUI) being displayed at runtime.

However it's important to stress that CLRDialog is still a lab effort by nature and it will need to be abandoned in favor of ManagedUI when WiX team fixes Burn (planed for next release). This is the reason why I do not invest in any serious improvements for CLRDialog technique.

Saying that I decided to add a sample of adding multiple pages into inserted CLRDialog as it is described in my first post in this thread. Please find the updated "CustomCLRDialog" sample on Git. You will need to follow the MultiStepDialogSetup.Build use case.

Oct 3, 2016 at 10:09 AM
Ok, I'll try to do it using multiple pages. Thank you!