33.2.1 Creating a Message Loop

Windows automatically creates a message queue for each thread that creates a window. You must provide a message loop for each window-creating thread in order to retrieve messages from the thread's message queue and dispatch them to the appropriate window procedures.

Windows directs messages to the individual windows in a application, so a thread must create at least one window before starting its message loop. Most Windows applications contain a single thread that creates windows. Typically, an application registers the window class for its main window, creates and shows the main window, and then starts its message loop—all in the WinMain function.

You create a message loop by using the GetMessage and DispatchMessage functions. If your application needs to obtain character input from the user, you must also include the TranslateMessage function in the loop. TranslateMessage translates virtual-key messages into character messages. The following code shows the message loop in the WinMain function of a simple Windows application:

HANDLE hinst;

HWND hwndMain;

int APIENTRY WinMain(hInstance, hPrevInstance, lpszCmdLine,

nCmdShow)

HANDLE hInstance;

HANDLE hPrevInstance;

LPSTR lpszCmdLine;

int nCmdShow;

{

MSG msg;

WNDCLASS wc;

UNREFERENCED_PARAMETER(lpszCmdLine);

/* Register the window class for the main window. */

if (!hPrevInstance) {

wc.style = 0;

wc.lpfnWndProc = (WNDPROC) WndProc;

wc.cbClsExtra = 0;

wc.cbWndExtra = 0;

wc.hInstance = hInstance;

wc.hIcon = LoadIcon((HANDLE) NULL, IDI_APPLICATION);

wc.hCursor = LoadCursor((HANDLE) NULL, IDC_ARROW);

wc.hbrBackground = GetStockObject(WHITE_BRUSH);

wc.lpszMenuName = "MainMenu";

wc.lpszClassName = "MainWndClass";

if (!RegisterClass(&wc))

return FALSE;

}

hinst = hInstance; /* saves handle of application instance */

/* Create the main window. */

hwndMain = CreateWindow("MainWndClass", "Sample",

WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,

CW_USEDEFAULT, CW_USEDEFAULT, (HWND) NULL,

(HMENU) NULL, hinst, (LPVOID) NULL );

/* If the main window couldn't be created, terminate. */

if (!hwndMain)

return FALSE;

/* Show the window and paint its contents. */

ShowWindow(hwndMain, nCmdShow);

UpdateWindow(hwndMain);

/* Start the message loop. */

while (GetMessage(&msg, (HWND) NULL, 0, 0)) {

TranslateMessage(&msg);

DispatchMessage(&msg);

}

/* Return the exit code to Windows. */

return msg.wParam;

}

The GetMessage, TranslateMessage, and DispatchMessage functions take a pointer to an MSG structure as a parameter. If a message is available, GetMessage copies it to the MSG structure. If the message is a virtual-key message (such as WM_KEYDOWN or WM_SYSKEYDOWN), TranslateMessage generates a character message (WM_CHAR or WM_SYSCHAR) and places it in the message queue. The DispatchMessage function uses the fields of the MSG structure as arguments for the window procedure. DispatchMessage doesn't return until the window procedure finishes processing the message.

If a thread supports accelerators, the thread's message loop must include the TranslateAccelerator function. TranslateAccelerator checks for key combinations that match an entry in the thread's accelerator table. When it finds a match, TranslateAccelerator translates the key combination into a WM_COMMAND message and dispatches it to the window procedure. For more information about accelerators, see Chapter 48, “Keyboard Accelerators.”

If a thread uses a modeless dialog box, the message loop must include the IsDialogMessage function so that the dialog box can receive keyboard input. For more information about dialog boxes, see Chapter 49, “Dialog Boxes.”

The following example shows a message loop for a thread that uses accelerators and displays a modeless dialog box. Note that when the TranslateAccelerator or IsDialogMessage function returns TRUE (indicating that the message was processed), the TranslateMessage and DispatchMessage functions should not be called. This is because TranslateAccelerator and IsDialogMessage perform all necessary translating and dispatching of messages.

HWND hwndMain;

HWND hwndDlgModeless = NULL;

MSG msg;

HANDLE haccel;

.

. /* Perform initialization and create a main window. */

.

while (GetMessage(&msg, (HWND) NULL, 0, 0)) {

if (hwndDlgModeless == (HWND) NULL ||

!IsDialogMessage(hwndDlgModeless, &msg) &&

!TranslateAccelerator(hwndMain, haccel, &msg)) {

TranslateMessage(&msg);

DispatchMessage(&msg);

}

}