Windows message loop messages

theForger’s Win32 API Programming Tutorial

Basics

  1. Getting Started
  2. A Simple Window
  3. Handling Messages
  4. The Message Loop
  5. Using Resources
  6. Menus and Icons
  7. Dialog Boxes
  8. Modeless Dialogs
  9. Standard Controls
  10. Dialog FAQ

Creating a simple application

  1. Creating controls at runtime
  2. Files and the common dialogs
  3. Tool and Status bars
  4. Multiple Document Interface

Graphics Device Interface

  1. Bitmaps and Device Contexts
  2. Transparency
  3. Timers and Animation
  4. Text, Fonts and Colours

Tools and Documentation

  1. References
  2. Free Visual C++

Appendices

  1. Solutions to Common Errors
  2. API vs. MFC
  3. Resource file notes

Understanding the Message Loop

Understanding the message loop and entire message sending structure of windows programs is essential in order to write anything but the most trivial programs. Now that we’ve tried out message handling a little, we should look a little deeper into the whole process, as things can get very confusing later on if you don’t understand why things happen the way they do.

What is a Message?

Each windows message may have up to two parameters, wParam and lParam . Originally wParam was 16 bit and lParam was 32 bit, but in Win32 they are both 32 bit. Not every message uses these parameters, and each message uses them differently. For example the WM_CLOSE message doesn’t use either, and you should ignore them both. The WM_COMMAND message uses both, wParam contains two values, HIWORD(wParam) is the notification message (if applicable) and LOWORD(wParam) is the control or menu id that sent the message. lParam is the HWND (window handle) to the control which sent the message or NULL if the messages isn’t from a control.

HIWORD() and LOWORD() are macros defined by windows that single out the two high bytes (High Word) of a 32 bit value ( 0x FFFF 0000 ) and the low word ( 0x0000 FFFF ) respectively. In Win32 a WORD is a 16bit value, making DWORD (or Double Word) a 32bit value.

To send a message you can use PostMessage() or SendMessage() . PostMessage() puts the message into the Message Queue and returns immediatly. That means once the call to PostMessage() is done the message may or may not have been processed yet. SendMessage() sends the message directly to the window and does not return untill the window has finished processing it. If we wanted to close a window we could send it a WM_CLOSE message like this PostMessage(hwnd, WM_CLOSE, 0, 0); which would have the same effect as clicking on the button on the top of the window. Notice that wParam and lParam are both 0 . This is because, as mentioned, they aren’t used for WM_CLOSE .

Dialogs

Once you begin to use dialog boxes, you will need to send messages to the controls in order to communicate with them. You can do this either by using GetDlgItem() first to get the handle to the control using the ID and then use SendMessage() , OR you can use SendDlgItemMessage() which combines the steps. You give it a window handle and a child ID and it will get the child handle, and then send it the message. SendDlgItemMessage() and similar APIs like GetDlgItemText() will work on all windows, not just dialog boxes.

What is the Message Queue

Lets say you were busy handling the WM_PAINT message and suddenly the user types a bunch of stuff on the keyboard. What should happen? Should you be interrupted in your drawing to handle the keys or should the keys just be discarded? Wrong! Obviously neither of these options is reasonable, so we have the message queue, when messages are posted they are added to the message queue and when you handle them they are removed. This ensure that you aren’t going to miss messages, if you are handling one, the others will be queued up untill you get to them.

What is a Message Loop


  1. The message loop calls GetMessage() , which looks in your message queue. If the message queue is empty your program basically stops and waits for one (it Blocks ).
  2. When an event occures causing a message to be added to the queue (for example the system registers a mouse click) GetMessages() returns a positive value indicating there is a message to be processed, and that it has filled in the members of the MSG structure we passed it. It returns 0 if it hits WM_QUIT , and a negative value if an error occured.
  3. We take the message (in the Msg variable) and pass it to TranslateMessage() , this does a bit of additional processing, translating virtual key messages into character messages. This step is actually optional, but certain things won’t work if it’s not there.
  4. Once that’s done we pass the message to DispatchMessage() . What DispatchMessage() does is take the message, checks which window it is for and then looks up the Window Procedure for the window. It then calls that procedure, sending as parameters the handle of the window, the message, and wParam and lParam .
  5. In your window procedure you check the message and it’s parameters, and do whatever you want with them! If you aren’t handling the specific message, you almost always call DefWindowProc() which will perform the default actions for you (which often means it does nothing).
  6. Once you have finished processing the message, your windows procedure returns, DispatchMessage() returns, and we go back to the beginning of the loop.

This is a very important concept for windows programs. Your window procedure is not magically called by the system, in effect you call it yourself indirectly by calling DispatchMessage() . If you wanted, you could use GetWindowLong() on the window handle that the message is destined for to look up the window’s procedure and call it directly!

I tried this with the previous example code, and it does work, however there are various issues such as Unicode/ANSI translation, calling timer callbacks and so forth that this method will not account for, and very likely will break all but trivial applications. So do it to try it, but don’t do it in real code 🙂

Notice that we use GetWindowLong() to retreive the window procedure associated with the window. Why don’t we just call our WndProc() directly? Well our message loop is responsible for ALL of the windows in our program, this includes things like buttons and list boxes that have their own window procedures, so we need to make sure that we call the right procedure for the window. Since more than one window can use the same window procedure, the first parameter (the handle to the window) is used to tell the window procedure which window the message is intended for.

As you can see, your application spends the majority of it’s time spinning round and round in this message loop, where you joyfully send out messages to the happy windows that will process them. But what do you do when you want your program to exit? Since we’re using a while() loop, if GetMessage() were to return FALSE (aka 0 ), the loop would end and we would reach the end of our WinMain() thus exiting the program. This is exactly what PostQuitMessage() accomplishes. It places a WM_QUIT message into the queue, and instead of returning a positive value, GetMessage() fills in the Msg structure and returns 0 . At this point, the wParam member of Msg contains the value that you passed to PostQuitMessage() and you can either ignore it, or return it from WinMain() which will then be used as the exit code when the process terminates.

IMPORTANT: GetMessage() will return -1 if it encounters an error. Make sure you remember this, or it will catch you out at some point. even though GetMessage() is defined as returning a BOOL , it can return values other than TRUE or FALSE , since BOOL is defined as UINT ( unsigned int ). The following are examples of code that may seem to work, but will not process certian conditions correctly: The above are all wrong! It may be of note that I used to use the first of these throughout the tutorial, since as I just mentioned, it works fine as long as GetMessage() never fails, which when your code is correct it won’t. However I failed to take into consideration that if you’re reading this, your code probably won’t be correct a lot of the time, and GetMessage() will fail at some point 🙂 I’ve gone through and corrected this, but forgive me if I’ve missed a few spots. This, or code that has the same effect should always be used.

Window Messages (Get Started with Win32 and C++)

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

  • Events from the user include all the ways that someone can interact with your program: mouse clicks, key strokes, touch-screen gestures, and so on.
  • 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 that has the following message code.

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.) Additionally, an application can have several windows, each with its own window procedure. How does the program receive all these messages and deliver them to the correct window procedure? The application needs a loop to retrieve the messages and dispatch 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 the windows that are created on that thread. The queue itself is hidden from your program. You cannot manipulate the queue directly. However, you can pull a message from the queue by calling the GetMessage function.

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 have 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. This includes the target window and the message code. The other three parameters let you 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.

The TranslateMessage function is related to keyboard input. It translates keystrokes (key down, key up) into characters. You do not really have to know how this function works; just remember to call it before DispatchMessage. The link to the MSDN documentation will give you more information, if you are 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 that the user presses the left mouse button. This causes a chain of events:

  1. The operating system puts 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. This 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 must have a loop that continually pulls messages from the queue and dispatches them. You can think of the loop as doing the following:

As written, of course, this loop would never end. That is where the return value for the GetMessage function comes in. Normally, GetMessage returns a nonzero value. When you want to exit the application and break out of the message loop, call the PostQuitMessage function.

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.

As long as GetMessage returns a nonzero 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 result of this behavior is that your window procedure never receives a WM_QUIT message. Therefore, you do not have to have a case statement for this message in your window procedure.)

The next obvious question is when to call PostQuitMessage. We’ll return to this question in the topic Closing the Window, but first we have to write our window procedure.

Posted Messages versus Sent Messages

The previous section talked about messages going onto a queue. Sometimes, 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 difference is not very important. The window procedure handles all messages. However, 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.

Читайте также:  Windows 10 pro workstations x64
Оцените статью