Export (0) Print
Expand All

Window Messages

A GUI application must respond to events from the user and from the operating system.

  • Events from the user include all of the ways that someone can interact with your program: mouse clicks, key strokes, touch-screen gestures, and so forth.
  • Events from the operating system include anything "outside" of the program that can affect how the program behaves. For example, the user might plug in a new hardware device, or Windows might enter a lower-power state (sleep or hibernate).

These events can occur at any time while the program is running, in almost any order. How do you structure a program whose flow of execution cannot be predicted in advance?

To solve this problem, Windows uses a message-passing model. The operating system communicates with your application window by passing messages to it. A message is simply a numeric code that designates a particular event. For example, if the user presses the left mouse button, the window receives a message with the following message code.

#define WM_LBUTTONDOWN    0x0201

Some messages have data associated with them. For example, the WM_LBUTTONDOWN message includes the x-coordinate and y-coordinate of the mouse cursor.

To pass a message to a window, the operating system calls the window procedure registered for that window. (And now you know what the window procedure is for.)

The Message Loop

An application will receive thousands of messages while it runs. (Consider that every keystroke and mouse-button click generates a message.) Furthermore, an application can have several windows, each with its own window procedure. How does the program receive all of these messages and deliver them to the right window procedure? The application needs a loop to get the messages and distpatch them to the correct windows.

For each thread that creates a window, the operating system creates a queue for window messages. This queue holds messages for all of the windows that are created on that thread. The queue itself is hidden from your progam. You can't manipulate the queue directly, but you can pull a message from the queue by calling the GetMessage function.

MSG msg;
GetMessage(&msg, NULL, 0, 0);

This function removes the first message from the head of the queue. If the queue is empty, the function blocks until another message is queued. The fact that GetMessage blocks will not make your program unresponsive. If there are no messages, there is nothing for the program to do. If you need to perform background processing, you can create additional threads that continue to run while GetMessage waits for another message. (See Avoiding Bottlenecks in Your Window Procedure.)

The first parameter of GetMessage is the address of a MSG structure. If the function succeeds, it fills in the MSG structure with information about the message, including the target window and the message code. The other three parameters give you the ability to filter which messages you get from the queue. In almost all cases, you will set these parameters to zero.

Although the MSG structure contains information about the message, you will almost never examine this structure directly. Instead, you will pass it directly to two other functions.

TranslateMessage(&msg); 
DispatchMessage(&msg);

The TranslateMessage function is related to keyboard input; it translates keystrokes (key down, key up) into characters. You don't really need to know how this function works; just remember to call it right before DispatchMessage. The link to the MSDN documentation will give you more information, if you're curious.

The DispatchMessage function tells the operating system to call the window procedure of the window that is the target of the message. In other words, the operating system looks up the window handle in its table of windows, finds the function pointer associated with the window, and invokes the function.

For example, suppose the user presses the left mouse button. This causes a chain of events:

  1. The operating system places a WM_LBUTTONDOWN message on the message queue.
  2. Your program calls the GetMessage function.
  3. GetMessage pulls the WM_LBUTTONDOWN message from the queue and fills in the MSG structure.
  4. Your program calls the TranslateMessage and DispatchMessage functions.
  5. Inside DispatchMessage, the operating system calls your window procedure.
  6. Your window procedure can either respond to the message or ignore it.

When the window procedure returns, it returns back to DispatchMessage, which returns to the message loop for the next message. As long as your program is running, messages will continue to arrive on the queue. Therefore, you need a loop that continually pulls messages from the queue and dispatches them. You can think of the loop as doing the following:


// WARNING: Don't actually write your loop this way.

while (1)      
{
    GetMessage(&msg, NULL, 0,  0);
    TranslateMessage(&msg); 
    DispatchMessage(&msg);
}

As written, of course, this loop would never end. That's where the return value for the GetMessage function comes in. Normally, GetMessage returns a non-zero value. Whenever you want to quit the application and break out of the message loop, simply call the PostQuitMessage function.


        PostQuitMessage(0);


The PostQuitMessage function puts a WM_QUIT message on the message queue. WM_QUIT is a special message: It causes GetMessage to return zero, signaling the end of the message loop. Here is the revised message loop.


// Correct.

MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}


As long as GetMessage returns a non-zero value, the expression in the while loop evaluates to true. After you call PostQuitMessage, the expression becomes false and the program breaks out of the loop. (One interesting consequence of this behavior is that your window procedure never receives a WM_QUIT message, so do not need a case statement for this message in your window procedure.)

The next obvious question is: When should you call PostQuitMessage? We'll return to this question in the topic Closing the Window, but first we need to write our window procedure.

Posted Messages versus Sent Messages

The previous section talked about messages going onto a queue. In some situations, the operating system will call a window procedure directly, bypassing the queue.

The terminology for this distinction can be confusing:

  • Posting a message means the message goes on the message queue, and is dispatched through the message loop (GetMessage and DispatchMessage).
  • Sending a message means the message skips the queue, and the operating system calls the window procedure directly.

For now, the distinction is not very important. The window procedure handles all messages, but some messages bypass the queue and go directly to your window procedure. However, it can make a difference if your application communicates between windows. You can find a more thorough discussion of this issue in the topic About Messages and Message Queues.

Next

Writing the Window Procedure

 

 

Send comments about this topic to Microsoft

Build date: 10/5/2010

Community Additions

ADD
Show:
© 2014 Microsoft