Why Embedded UI?

Windows Installer’s existing internal UI does not provide the flexibility necessary for unique setups or complex scenarios. Setup authors, who want a UI experience that Windows Installer’s internal UI doesn’t support, use MsiSetExternalUI/MsiSetExternalUIRecord to initialize and use their custom UI.

However, the custom UI can only be used from an external process that initializes the external UI before the install begins. Because of this requirement, the OS natively has an extremely limited set of scenarios in which external UI can be used. Repairs from ARP, admin installs, installations via shortcut activation never use external UI. For many teams wanting to use external UI, the bootstrapper requirement, is an onerous requirement itself. Use of the bootstrapper can also break some deployment scenarios, as software distribution has much richer support for native deployment of MSI files than for bootstrapper executables.

Windows Installer 4.5 provides support for embedded UI. This provides the package author enhanced control of the Windows Installer user interface through custom code which is bundled with the MSI file itself and without requiring complex and bug-prone workarounds to handle the various activation paths which can trigger an install. Regardless of the activation method of the installer operation, MSI will use the custom embedded UI provided within the package.

The PPT slide deck attached to this blog provides an overview of the workings of Windows Installer’s internal, external and embedded UI.

How to use it?

A setup author who wishes to use embedded UI provides a DLL that exports the InitializeEmbeddedUI, EmbeddedUIHandler, and ShutdownEmbeddedUI functions. The setup author should then include MsiEmbeddedUI table containing information about the custom DLL that handles the Windows Installer messages.

An Example

In this section we will walk through the steps involved in creating a package that uses embedded UI.

Create a Embedded UI DLL

Similar to external UI, a setup UI developer should provide an embedded UI handler. Such an embedded UI handler should export InitializeEmbeddedUI, EmbeddedUIHandler, and ShutdownEmbeddedUI functions. A template implementation of each of these functions is provided below:

int __stdcall InitializeEmbeddedUI(MSIHANDLE hInstall, LPCWSTR szResourcePath, LPDWORD pdwInternalUILevel)

{

                // Use hInstall to query the install type. Ex: Is it product/patch install/uninstall?

                // Use the resource patch provided in the parameter szResourcePath to read any UI resources

                // Use the pdwInternalUILevel to read/write the internal UI level

                if(MsiGetMode(hInstall, MSIRUNMODE_MAINTENANCE))

                {

                                // Put up maintenance UI

                }

                if(ERROR_SUCCESS == MsiGetProperty(hInstall, TEXT("PATCH"), rgchValueBuf, &dwValueBuf))

                {

                                if(dwValueBuf > 0)

                                                // Put up patching UI

                }

 

                // Embedded UI handler will handle all the Windows Installer messages and I do not

                // want to display Windows Installer’s internal UI

                *pdwInternalUILevel = INSTALLUILEVEL_NONE;

 

                return ERROR_SUCCESS;

}

 

// EmbeddedUIHandler is very similar to the callback provided to MsiSetExternalUIRecord

int __stdcall EmbeddedUIHandler(UINT iMessageType, MSIHANDLE hRecord)

{

                INSTALLMESSAGE mt;

                UINT uiFlags;

 

                mt = (INSTALLMESSAGE)(0xFF000000&(UINT)iMessageType);

                uiFlags = 0x00FFFFFF&iMessageType;

 

                switch (mt)

                {

                case INSTALLMESSAGE_ERROR:

                                {

               

                                }

                case INSTALLMESSAGE_WARNING:

                                {

               

                                }

                case INSTALLMESSAGE_FILESINUSE:

                                {

               

                                }

                }

                return 0;

}

 

int __stdcall ShutdownEmbeddedUI()

{

                // Let me shutdown the UI and exit gracefully.

}

Populate the MsiEmbeddedUI table

MsiEmbeddedUI

FileName

Attributes

MessageFilter

Data

UIDLL

UIDLL.dll

3

38

[Binary Data]

ResDLL1

ResDLL1.dll

0

 

[Binary Data]

ICO

my.ico

0

 

[Binary Data]

Notice that the table in the above example says that the UIDLL handles UI messages INSTALLLOGMODE_ERROR | INSTALLLOGMODE_WARNING | INSTALLLOGMODE_FILESINUSE (i.e., 2 + 4 + 32 = 38). In a way, the MessageFilter column of MsiEmbeddedUI table is similar to the dwMessageFilter parameter of the MsiSetExternalUIRecord API; in that both define the messages for which their callbacks expect to be called. The Data column corresponding to the row where `MsiEmbeddedUI`.`MsiEmbeddedUI` == UIDLL contains the embedded UI DLL.

MsiEmbeddedUI table also lists the resource DLL and the icon file used by the UI DLL. When InitializeEmbeddedUI callback gets called, the szResourcePath parameter for that function points to the directory that has all the resource binaries extracted from this table.

FAQ

Q: What if I do not want my embedded UI to handle certain Windows Installer message?
 A:
Include only the messages that your implementation of EmbeddedUIHandler can handle in the MSIEmbeddedUI.MessageFilter column. That will ensure that the EmbeddedUIHandler callback gets just the messages that it can handle.

Q: What will happen if InitializeEmbeddedUI fails to return any of the documented return values or just crashes?
A:
That will cause the install to fail with ERROR_INSTALL_FAILURE.

Q: Where is my embedded UI DLL hosted?
A:
The embedded UI DLL will be hosted in its own sandboxed impersonated custom action server.

Q: What happens after ShutdownEmbeddedUI call is returned?
A:
The embedded UI DLL will be unloaded and the custom action server hosting the embedded UI DLL will be shutdown.

Q: Can my embedded UI DLL run during silent installs?
A:
No.

Q: Will embedded UI get any messages if I am already using external UI using MsiSetExternalUI or MsiSetExternalUIRecord?
A:
If you are using external UI, then embedded UI within the package will not be used. Similarly, if embedded UI is already being used, calls to set the external UI will fail.

Q: What happens to the messages that embedded UI does not handle?
A:
Depending on the internal UI level, the messages will be sent to the internal UI handler.

[Author: Hemchander  Sannidhanam]
This posting is provided "AS IS" with no warranties, and confers no rights. Use of included script samples are subject to the terms specified at http://www.microsoft.com/info/cpyright.htm.