Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Microsoft Corporation
August 2006
Applies to:
.NET Framework 3.0
Summary: Present a customized or unified setup experience for applications that require the .NET Framework 3.0. This article explains how to silently launch, track, and cancel the .NET Framework 3.0 setup process. (8 printed pages)
Contents
Introduction
Hooking Up the Callback Function
Receiving Messages
Message Data
Canceling the Setup
CSetupWatcher Reference
Introduction
The .NET Framework 3.0 is a redistributable runtime. The .NET Framework 3.0 setup may often be included as a prerequisite component in the setup of applications. To present a customized or unified setup experience for applications that require the .NET Framework 3.0, you may want to silently launch and track the .NET Framework 3.0 setup process while showing your own view of the setup progress. To enable this, the .NET Framework 3.0 setup process (which can be "watched") sends frequent progress messages to the main setup process (called the "watcher"). The watcher process may respond to any of the progress messages by using a code that indicates whether the setup should be canceled.
Hooking Up the Callback Function
To enable the silent launch and tracking of the .NET Framework 3.0, the watcher process must take a few steps to hook up a callback function before launching the .NET Framework 3.0 setup. The necessary support code is included in a single header file named SetupWatcher.h, available here: SetupWatcher.h. The following shows the starter hookup code.
#include "SetupWatcher.h"
CSetupWatcher* pSetupWatcher = new CSetupWatcher(L"NetFX3SetupWatcher");
setupWatcher->Connect();
setupWatcher->ReceiveMessages(SetupUIHandler, NULL);
// TODO: launch setup process:
// dotnetfx3setup.exe /q /progress NetFX3SetupWatcher
// Wait for setup process to exit.
All the details of the communication between the setup watcher and the setup process being watched are handled by the CSetupWatcher class. When creating a new instance of the CSetupWatcher class, you must pass a unique name to the constructor. The same name is used in the progress switch of the command line when launching the .NET Framework 3.0 setup process. The name must be unique on the computer (for the duration of that setup session). The name must be unique, because it is used as the name of a named pipe that carries the communication.
To watch for messages and specify a callback function to handle those messages, you use the ReceiveMessages method. The ReceiveMessages method creates a new thread that is managed by the CSetupWatcher class. The thread listens for progress messages, and invokes the callback function as each message is received.
Note For more information about the CSetupWatcher methods, including parameters and return codes, see the reference at the end of this document, or the code comments in SetupWatcher.h.
Receiving Messages
The CSetupWatcher callback function (typically named SetupUIHandler) has exactly the same prototype, and similar semantics, as the Microsoft Windows Installer external user-interface (UI) handler (INSTALLUI_HANDLER_RECORD).
int SetupUIHandler(void* pvContext, UINT iMessageType,
MSIHANDLE hrecMsg);
The message type parameter can be any of the standard INSTALLMESSAGE_ xxx types defined by the Windows Installer, or it can be some custom extension. In the current .NET Framework 3.0 implementation, the message type is always INSTALLMESSAGE_SUITEPROGRESS. Standard Windows Installer messages are not passed through directly. The void pointer is just an optional context object that is passed to the ReceiveMessages method.
Messages consist of an integer type, plus a Windows Installer record that contains a message and data fields that depend on the type and context of the message. The message record contains a string in field 0 that describes the current action that is in progress. The message is not localized, so it is not necessarily appropriate for display in the UI; however, it may be used for debugging or logging. Other fields contain integer data that depends on the phase of the installation.
The callback function must return one of the values in Table 1 in response to an INSTALLMESSAGE_SUITEPROGRESS message.
Table 1. Callback Function Return Values
Return Value | Description |
---|---|
0 | The message was ignored, or the message type was unrecognized. |
IDOK | The message was handled. |
IDCANCEL | The setup should be canceled. |
Message Data
Table 2 lists the data that messages of type INSTALLMESSAGE_SUITEPROGRESS contain in each field, depending on the setup phase.
Table 2. Data in INSTALLMESSAGE_SUITEPROGRESS Messages
Phase | Fields |
---|---|
Phase 1
Initializing |
|
Phase 2
Downloading |
|
Phase 3
Installing |
|
Rollback
(After error or cancellation) |
|
Canceling the Setup
The .NET Framework 3.0 setup cannot be canceled at any arbitrary point. The watcher process must wait for a progress message, and then return IDCANCEL if the setup should be canceled. However, progress messages are very frequent throughout most of the setup process.
When a cancellation response is received, the setup may not exit immediately. Depending on the phase of the setup, it may take some time to partially or fully roll back. If the setup is too far along, it may simply complete. The exit code of the process will indicate whether the setup was canceled (ERROR_INSTALL_USEREXIT), whether the setup completed (ERROR_SUCCESS / ERROR_SUCCESS_REBOOT_REQUIRED), or whether there was some error.
CSetupWatcher Reference
///////////////////////////////////////////////////////////////////////////////
// CSetupWatcher //
///////////////////
//
// Facility for hooking up external (local out-of-proc) setup UI using named
// pipes.
// Uses a design similar to MSI's external UI handler (and the same callback
// prototypes), but can be integrated into any kind of setup engine.
//
class CSetupWatcher
{
public:
///////////////////////////////////////////////////////////////////////////////
// CSetupWatcher constructor
//
// Creates a new setup watcher instance, for use either by the process watching
// the setup, or by the setup process being watched.
//
// szName - Identifies the setup instance being watched. The watcher and
// the watchee must use the same name. The name should be unique
// enough to avoid conflicting with other instances on the system.
//
// fWatcher - True if the calling process is the watcher process, false if the
// calling process is the setup process being watched.
//
CSetupWatcher(const wchar_t* szName, bool fWatcher=true);
///////////////////////////////////////////////////////////////////////////////
// CSetupWatcher destructor
//
// Closes any open handles and frees any allocated memory.
//
~CSetupWatcher();
///////////////////////////////////////////////////////////////////////////////
// CSetupWatcher::Connect()
//
// Connects the inter-process communication channel.
// (Currently implemented as a named pipe.)
//
// This method must be called first by the watcher process, then by the watched
// setup process. The method does not block; the watcher will asynchronously
// wait for the watched process to make the connection.
//
// Returns: 0 on success, Win32 error code on failure.
//
DWORD Connect();
///////////////////////////////////////////////////////////////////////////////
// CSetupWatcher::IsConnected()
//
// Checks if the watcher process and setup process are currently connected.
//
bool IsConnected() const;
///////////////////////////////////////////////////////////////////////////////
// CSetupWatcher::ReceiveMessages()
//
// For use by the watcher process. Watches for messages in the input buffer and
// calls the callback for each one.
//
// This method does not block; it spawns a separate thread to do the work.
//
// pHandler - Callback function to handle any messages received.
//
// pvContext - Optional context pointer to pass to the callback function.
//
// Returns: 0 on success, Win32 error code on failure.
//
DWORD ReceiveMessages(INSTALLUI_HANDLER pHandler, void* pvContext);
///////////////////////////////////////////////////////////////////////////////
// CSetupWatcher::ReceiveMessages()
//
// For use by the watcher process. Watches for messages in the input buffer and
// calls the callback for each one.
//
// This method does not block; it spawns a separate thread to do the work.
//
// pHandler - Callback function to handle any messages received.
//
// pvContext - Optional context pointer to pass to the callback function.
//
// Returns: 0 on success, Win32 error code on failure.
//
DWORD ReceiveMessages(INSTALLUI_HANDLER_RECORD pHandler, void* pvContext);
///////////////////////////////////////////////////////////////////////////////
// CSetupWatcher::SendMessage()
//
// For use by the watched setup process. Sends a message to the watcher and
// synchronously waits on a reply, up to the timeout value.
//
// iMessageType - Type of message being sent, typically one of the
// INSTALLMESSAGE_* values, optionally combined with other
// flags.
//
// szMessage - Message string being sent.
//
// pdwReply - [OUT] Receives the reply code from the watcher.
//
// Returns: 0 on success, Win32 error code on failure.
// Returns WAIT_TIMEOUT if no reply was received in time.
//
DWORD SendMessage(UINT iMessageType, const wchar_t* szMessage, DWORD*
pdwReply);
///////////////////////////////////////////////////////////////////////////////
// CSetupWatcher::SendMessage()
//
// For use by the watched setup process. Sends a message to the watcher and
// synchronously waits on a reply, up to the timeout value.
//
// iMessageType - Type of message being sent, typically one of the
// INSTALLMESSAGE_* values, optionally combined with other
// flags.
//
// hRec - Message record being sent.
//
// pdwReply - [OUT] Receives the reply code from the watcher.
//
// Returns: 0 on success, Win32 error code on failure.
// Returns WAIT_TIMEOUT if no reply was received in time.
//
DWORD SendMessage(UINT iMessageType, MSIHANDLE hRec, DWORD* pdwReply);
///////////////////////////////////////////////////////////////////////////////
// CSetupWatcher::SetReplyTimeout()
//
// For use by the watched setup process. Configures the amount of time to
// wait for a reply to a message. If a reply is not received within that time,
// then the connection is broken.
//
void SetReplyTimeout(DWORD dwMilliseconds);
}