From MSI to WiX, Part 20 – User Interface – Required Dialog Boxes


The main page for the series is here.


 



Introduction 


We will start with exploring Required Dialog Boxes.  Microsoft Windows Installer uses three special dialogs in response to the following events during product installation:



  • Exit Dialog – shown when installation completed successfully.  Required name for the dialog is ExitDialog.

  • Fatal Error Dialog – shown in response to installation termination due to the fatal error.  Required name for the dialog is FatalError.

  • User Exit Dialog – shown in response to user’s request to cancel an installaton.  Required name for the dialog is UserExit.

Each of these dialogs has a special sequence number in both AdminUISequence and InstallUISequence tables:




  • Exit Dialog: -1


  • Fatal Error Dialog: -3


  • User Exit Dialog: -2

There is also one special dialog which Windows Installer uses to show an installation error.


For now, I will leave alone all the bells, whistles, and icing and will use the bare minimum set of WiX elements and their attributes, just enough to explain what needs to be done to make a required dialog.  So, here it goes.



Exit dialog


The Exit dialog is displayed at the end of successful installation.  The only requirement for this dialog is to have a button control which will be used by the user to close the dialog to complete the installation.  It is also advisable to give some sort of message to the user indicating that the installation is completed successfully, but as I said earlier, we will make our dialogs more “civilized” later.



By default, Microsoft Windows Installer is using System font to show text in the user interface.  This can cause the installer to improperly display text strings if the package’s code page is different from the user’s default UI code page.


The recomendation here is to set the DefaultUIFont property to one of the styles defined in the TextStyle table.  In WiX it is done like this:


<TextStyle Id=DefaultFont FaceName=Tahoma Size=8 />


<Property Id=DefaultUIFont Value=DefaultFont />


To make the correct implementation of Exit dialog the following must be done:



Here is the very simple implementation of the Exit dialog:


<?xml version=1.0 encoding=UTF-8?>


<Wix xmlns=http://schemas.microsoft.com/wix/2006/wi>


  <Product Id=732e6d89-5fa5-43a1-b590-88ebed02a05f


           Name=UIRequiredDialogs


           Language=1033


           Version=1.0.0.0


           Manufacturer=UIRequiredDialogs


           UpgradeCode=8f2b749f-f563-4a4f-ab66-5a121bf07378>


    <Package InstallerVersion=200 Compressed=yes />


 


    <Media Id=1 Cabinet=UIRequiredDialogs.cab EmbedCab=yes />


 


    <UI>


      <Dialog Id=ExitDialog Width=370 Height=270 Title=Exit Dialog>


        <Control Id=Exit


                 Type=PushButton


                 X=236 Y=243 Width=56 Height=17


                 Default=yes


                 Cancel=yes


                 Text=Exit>


          <Publish Event=EndDialog Value=Return>1</Publish>


        </Control>


      </Dialog>


 


      <TextStyle Id=DefaultFont FaceName=Tahoma Size=8 />


      <Property Id=DefaultUIFont Value=DefaultFont />


 


      <InstallUISequence>


        <Show Dialog=ExitDialog OnExit=success />


      </InstallUISequence>


 


      <AdminUISequence>


        <Show Dialog=ExitDialog OnExit=success />


      </AdminUISequence>


    </UI>


 


    <Directory Id=TARGETDIR Name=SourceDir>


      <Directory Id=ProgramFilesFolder>


        <Directory Id=INSTALLLOCATION Name=UIRequiredDialogs>


          <Component Id=ProductComponent Guid=df47e78b-325c-4371-ba28-10e899dad783>


            <File Id=Readme.txt Name=Readme.txt Source=Readme.txt Vital=yes KeyPath=yes />


          </Component>


        </Directory>


      </Directory>


    </Directory>


 


    <Feature Id=ProductFeature Title=UIRequiredDialogs Level=1>


      <ComponentRef Id=ProductComponent />


    </Feature>


  </Product>


</Wix>



As you can see, all UI-related elements are located inside the <UI> element.


The Id attribute of the <Dialog> element can have any value and must match the value of Dialog attribute of the <Show> element and this value does not have to be “ExitDialog”.  What makes this dialog Exit dialog is the value of OnExit attribute of the <Show> element.  By setting OnExit attribute’s value to “success” we are telling to WIX that this dialog must have sequence number -1.


It seems to me that X and Y attributes of the <Dialog> element in WIX are coordinates of the center of the dialog on the sceen as a percentage of the screen’s width and height.  By default, their values will be set to 50 and dialog will appear at the center of the screen.


Width and Height attributes are the width and height of the dialog window.  These values are in Installer units.


<Control> element’s position and size attributes are in Installer unitsDefault attribute set to “yes” means that pressing Enter key will have the same effect as clicking the button control.  By setting Cancel attribute’s value to “yes” we are saying that closing the dialog by clicking X button in the right upper corner of the dialog will also have the same effect as clicking the button control.


To raise the EndDialog control event when user click the button, we must add <Publish> element as a chile element of the push button <Control> element.  Event attribute’s value must be “EndDialog” (the name of the standard EndDialog control event) and Value attribute’s value set to “Return” (means that control returns to the installer with the Success value).


Let’s compile our project.  Ignore for now six ICE20 errors.  Install our test product and see how your dialog looks like.  Don’t forget to uninstall the test product.



Fatal Error dialog


The FatalError dialog is displayed when installation is terminated because of a fatal error.  Requirements and implementation of this dialog are almost identical to requirements and implementation of Exit dialog.  The only difference is in the <Show> element (only relevant code is shown):


<Property Id=FailureProgram>


<![CDATA[


    Function Main()


      Main = 3


    End Function


    ]]>


</Property>


 


<CustomAction Id=FakeFailure


              VBScriptCall=Main


              Property=FailureProgram />


 


<InstallExecuteSequence>


  <Custom Action=FakeFailure


          Before=InstallInitialize>TESTFAIL</Custom>


</InstallExecuteSequence>


 


<UI>


  <Dialog Id=FatalError


          Width=370 Height=270


          Title=Fatal Error Dialog>


    <Control Id=Finish Type=PushButton


             X=236 Y=243 Width=56 Height=17


             Default=yes Cancel=yes


             Text=Exit>


      <Publish Event=EndDialog Value=Exit>1</Publish>


    </Control>


  </Dialog>


     


  <TextStyle Id=DefaultFont FaceName=Tahoma Size=8 />


  <Property Id=DefaultUIFont Value=DefaultFont />


 


  <InstallUISequence>


    <Show Dialog=FatalError OnExit=error />


  </InstallUISequence>


 


  <AdminUISequence>


    <Show Dialog=FatalError OnExit=error />


  </AdminUISequence>


</UI>


To immitate the fatal error I added custom action which returns a failure exit code. To test it you need to pass the TESTFAIL property in the command line:


Msiexec /I UIRequiredDialogs.msi TESTFAIL=1



User Exit dialog


The UserExit dialog is displayed when installation is terminated at the user’s request.  Requirements and implementation of this dialog are identical to requirements and implementation of FatalError dialog:


<Property Id=UserExitProgram>


  <![CDATA[


    Function Main()


      Main = 2


    End Function


    ]]>


</Property>


 


<CustomAction Id=FakeUserExit


              VBScriptCall=Main


              Property=UserExitProgram />


 


<InstallExecuteSequence>


  <Custom Action=FakeUserExit


          Before=InstallInitialize>TESTUSEREXIT</Custom>


</InstallExecuteSequence>


 


<UI>


  <Dialog Id=UserExit


          Width=370 Height=270


          Title=User Exit dialog>


    <Control Id=Finish Type=PushButton


             X=236 Y=243 Width=56 Height=17


             Default=yes Cancel=yes


             Text=Exit>


      <Publish Event=EndDialog Value=Exit>1</Publish>


    </Control>


  </Dialog>


 


  <TextStyle Id=DefaultFont FaceName=Tahoma Size=8 />


  <Property Id=DefaultUIFont Value=DefaultFont />


 


  <InstallUISequence>


    <Show Dialog=UserExit OnExit=cancel />


  </InstallUISequence>


 


  <AdminUISequence>


    <Show Dialog=UserExit OnExit=cancel />


  </AdminUISequence>


</UI>


To immitate the user exit requestI added custom action which returns a UserExit code. To test it you need to pass the TESTUSEREXIT property in the command line:


Msiexec /I UIRequiredDialogs.msi TESTUSEREXIT=1



Error dialog


The Error dialog displays the error message.  To display an Error dialog we must create a dialog and set the ErrorDialog property to the Id of the Error dialog.


The following are requirements for the Error dialog:




  • Dialog must have attribute ErrorDialog set to “yes”.


  • First control in the dialog must be control with Id=”ErrorText”.


  • Dialog must contain seven buttons.  Each button must publish EndDialog event.  The only two differences between buttons are the value of Id attribute of the Control element and the value of the Value attribute (event argument) of Publish element:



    • Abort button: publishes an event with argument ErrorAbort and must have Control/@Id=”A”.


    • Cancel button: publishes an event with argument ErrorCancel and must have Control/@Id=”C”.


    • Ignore button: publishes an event with argument ErrorIgnore and must have Control/@Id=”I”.


    • No button: publishes an event with argument ErrorNo and must have Control/@Id=”N”.


    • OK button: publishes an event with argument ErrorOK and must have Control/@Id=”O”.


    • Retry button: publishes an event with argument ErrorRetry and must have Control/@Id=”R”.


    • Yes button: publishes an event with argument ErrorYes and must have Control/@Id=”Y”.


  • All buttons must have attribute TabSkip set to “yes”.


  • All buttons must have the same position (same value of the X and Y attributes).


  • In Error dialog the values of Cancel and Default attributes of button Control elements are ignored, so there is no need to give them any value.


  • Error dialog can also have an Icon control.  If included, this control must have an Id attribute set to “ErrorIcon” and attribute FixedSize set to “yes”.  Setting Text attribute is not necessary, but not setting it will generate an ICE17.  As a workaround, add an icon to Binary table and set Text attribute to that icon.

Here is the complete source code which includes all four required dialogs.  Compilation of this code still produces ICE20 about missing FilesInUse dialog, but for now we can safely ignore this error.


<?xml version=1.0 encoding=UTF-8?>


<Wix xmlns=http://schemas.microsoft.com/wix/2006/wi>


  <Product Id=732e6d89-5fa5-43a1-b590-88ebed02a05f Name=UIRequiredDialogs Language=1033 Version=1.0.0.0 Manufacturer=UIRequiredDialogs UpgradeCode=8f2b749f-f563-4a4f-ab66-5a121bf07378>


    <Package InstallerVersion=200 Compressed=yes />


 


    <Media Id=1 Cabinet=UIRequiredDialogs.cab EmbedCab=yes />


 


    <Binary Id=ErrorIcon SourceFile=ErrorIcon.ico />


 


    <!– Fatal Error –>


    <Property Id=FailureProgram>


    <![CDATA[


        Function Main()


          Main = 3


        End Function


        ]]>


    </Property>


 


    <CustomAction Id=FakeFailure


            VBScriptCall=Main


            Property=FailureProgram />


 


    <InstallExecuteSequence>


      <Custom Action=FakeFailure


          Before=InstallInitialize>TESTFAIL</Custom>


    </InstallExecuteSequence>


 


    <!– User Exit –>


    <Property Id=UserExitProgram>


      <![CDATA[


        Function Main()


          Main = 2


        End Function


        ]]>


    </Property>


 


    <CustomAction Id=FakeUserExit


            VBScriptCall=Main


            Property=UserExitProgram />


 


    <InstallExecuteSequence>


      <Custom Action=FakeUserExit


          Before=InstallInitialize>TESTUSEREXIT</Custom>


    </InstallExecuteSequence>


 


    <!– Error dialog –>


    <CustomAction Id=TestError Error=25000 />


 


    <InstallExecuteSequence>


      <Custom Action=TestError Before=InstallInitialize>TESTERROR</Custom>


    </InstallExecuteSequence>


 


    <UI>


      <Dialog Id=ExitDialog


          Width=370 Height=270


          Title=Exit Dialog>


        <Control Id=Exit Type=PushButton


             X=236 Y=243 Width=56 Height=17


             Default=yes Cancel=yes


             Text=Exit>


          <Publish Event=EndDialog Value=Return>1</Publish>


        </Control>


      </Dialog>


 


      <Dialog Id=FatalError


          Width=370 Height=270


          Title=Fatal Error Dialog>


        <Control Id=Finish Type=PushButton


             X=236 Y=243 Width=56 Height=17


             Default=yes Cancel=yes


             Text=Exit>


          <Publish Event=EndDialog Value=Exit>1</Publish>


        </Control>


      </Dialog>


     


      <Dialog Id=UserExit


          Width=370 Height=270


          Title=User Exit dialog>


        <Control Id=Finish Type=PushButton


             X=236 Y=243 Width=56 Height=17


             Default=yes Cancel=yes


             Text=Exit>


          <Publish Event=EndDialog Value=Exit>1</Publish>


        </Control>


      </Dialog>


 


      <Property Id=ErrorDialog Value=ErrorDlg />


      <Error Id=25000>Error message goes here!</Error>


 


      <Dialog Id=ErrorDlg Width=270 Height=105 Title=My Error Dialog ErrorDialog=yes>


        <Control Id=ErrorText Type=Text X=48 Y=15 Width=205 Height=60 TabSkip=no Text=Error text />


        <Control Id=Y Type=PushButton X=100 Y=80 Width=56 Height=17 TabSkip=yes Text=Yes>


          <Publish Event=EndDialog Value=ErrorYes>1</Publish>


        </Control>


        <Control Id=A Type=PushButton X=100 Y=80 Width=56 Height=17 TabSkip=yes Text=Abort>


          <Publish Event=EndDialog Value=ErrorAbort>1</Publish>


        </Control>


        <Control Id=C Type=PushButton X=100 Y=80 Width=56 Height=17 TabSkip=yes Text=Cancel>


          <Publish Event=EndDialog Value=ErrorCancel>1</Publish>


        </Control>


        <Control Id=I Type=PushButton X=100 Y=80 Width=56 Height=17 TabSkip=yes Text=Ignore>


          <Publish Event=EndDialog Value=ErrorIgnore>1</Publish>


        </Control>


        <Control Id=N Type=PushButton X=100 Y=80 Width=56 Height=17 TabSkip=yes Text=No>


          <Publish Event=EndDialog Value=ErrorNo>1</Publish>


        </Control>


        <Control Id=O Type=PushButton X=100 Y=80 Width=56 Height=17 TabSkip=yes Text=OK>


          <Publish Event=EndDialog Value=ErrorOk>1</Publish>


        </Control>


        <Control Id=R Type=PushButton X=100 Y=80 Width=56 Height=17 TabSkip=yes Text=Retry>


          <Publish Event=EndDialog Value=ErrorRetry>1</Publish>


        </Control>


        <Control Id=ErrorIcon Type=Icon X=15 Y=15 Width=24 Height=24 ToolTip=Error tooltip FixedSize=yes Text=ErrorIcon />


      </Dialog>


     


      <TextStyle Id=DefaultFont FaceName=Tahoma Size=8 />


      <Property Id=DefaultUIFont Value=DefaultFont />


 


      <InstallUISequence>


        <Show Dialog=ExitDialog OnExit=success />


        <Show Dialog=FatalError OnExit=error />


        <Show Dialog=UserExit OnExit=cancel />


      </InstallUISequence>


 


      <AdminUISequence>


        <Show Dialog=ExitDialog OnExit=success />


        <Show Dialog=FatalError OnExit=error />


        <Show Dialog=UserExit OnExit=cancel />


      </AdminUISequence>


    </UI>


 


    <Directory Id=TARGETDIR Name=SourceDir>


      <Directory Id=ProgramFilesFolder>


        <Directory Id=INSTALLLOCATION Name=UIRequiredDialogs>


          <Component Id=ProductComponent Guid=df47e78b-325c-4371-ba28-10e899dad783>


            <File Id=Readme.txt Name=Readme.txt Source=Readme.txt Vital=yes KeyPath=yes />


          </Component>


        </Directory>


      </Directory>


    </Directory>


 


    <Feature Id=ProductFeature Title=UIRequiredDialogs Level=1>


      <ComponentRef Id=ProductComponent />


    </Feature>


  </Product>


</Wix>



To test the Error dialog you need to pass the TESTERROR property in the command line:


Msiexec /I UIRequiredDialogs.msi TESTERROR=1



 

UIRequiredDialogs.zip

Comments (4)

  1. gosaca says:

    Thanks for the useful info.

    The following may be of interest to others working with Wix dialogs:

    To quickly view your all of your dialogs without running setup:

    "%mssdk%"samplessysmgmtmsiscriptsWiDialog.vbs MySetup.msi

    Generate reference Wix source for most dialogs you would possibly need, including FilesInUse:

    dark /nologo "%mssdk%"samplessysmgmtmsidatabaseUISample.Msi

  2. BarryH says:

    Basically, my question is in the subject line.  I can’t seem to find documentation or examples illustrating the basic technique of disabling a pushbutton control as a result of the user clicking on that same control without changing dialogs.  I’ve tried so many permutations on Condition Actions and Publish Event constructs and Custom Actions that I don’t really have a coherent code snippet from which to work.  I was hoping that the problem is simple and well known and that I’ve just somehow managed to miss the obvious.  Many thanks.

  3. chotorakib2 says:

    Hi Alex,

    Thanks for these helpful tutorials. I have learned a lot here.

    I am using Wix 3. I have a start-up wizard application that I start at the end of installation, from Exit dialog (at the Finish button). But, since this Exit dialog is not actually the finishing step, my client is requesting to remove this dialog.

    So, I want to bypass the Exit dialog but still want to start that wizard application right after the Progress dialog.

    Please let me know, if it is possible to do so? if yes, how?

    Regrads

    Rakib

  4. conrad says:

    Hi Alex,

    Is it possible to override the installer’s stock message ERROR_PRODUCT_VERSION with a message that’s specific to my product?  I’ve tried using the fatalerror dialog above but I can’t seem to place it early enough in the sequence, the stock message always comes up first.

    Thanks so much for your articles, they help a lot.