Adi Levin's Blog for programmers

June 19, 2009

Messages and Message Queues

Messages are one of the foundations of Windows programming. A Windows application is event-driven. Then means that it doesn’t call functions to get user-input. It waits for input to arrive, and responds to it. In fact, the typical situation is that there are hundreds of threads in the system at any moment, of which almost all are in a waiting state. Many of them are waiting for Messages.

The system communicates with applications via messages. For example, every mouse movement or mouse click is translated into a message, and posted to the message queue of the relevant window. Applications can use messages for inter-process and inter-window communication. Messages can also be posted to specific threads, and can be used as a means of cooperation between threads of a process.

The contents of a message

A message contains three elements: The message identifier (unsigned int), and two parameters. The message identifier tells what kind of message this is: For example, WM_PAINT is a message ordering a window to be drawn. WM_QUIT orders a window to close itself. The meaning of the parameters varies, and it depends on the kind of message being processed.

Message Queue

A message typically cannot be treated immediately when it is sent, because the recieving thread is busy doing other things. Messages stand in line to be treated at a First-In-First-Out order, with the exception of certain messages that have higher priority or lower priority than others. This queue of messages is handled by the system. Every thread potentially has a message queue attached to it. The message queue is not created until it is accessed by the thread for the first time (typically by calling GetMessage or PeekMessage).

Message Loop / Message Pump

At the top of a Windows application you will always find a message loop (also called messaeg pump). A minimal message loop may look as follows:

MSG msg;

while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)  {
        if (bRet == -1) {
            // handle the error and possibly exit
        } else {
             DispatchMessage(&msg);
        }
}

The function GetMessage waits until the is a message in the message queue, and returns the message contents. The function DispatchMessage sends the message contents to the appropriate function that performs actions in response to the message.

The contents of an MSG is as follows:

typedef struct {
    HWND hwnd;
    UINT message;
    WPARAM wParam;
    LPARAM lParam;
    DWORD time;
    POINT pt;
} MSG;

hwnd is the handle of the window to which the message is targeted. It is NULL when the message is posted to a thread rather than a window. The next 3 parameters message, wParam and lParam are the message identifier and the two parameters. time and pt signify the time the message was posted and the mouse cursor location at that time.

In the case of messages posted to a thread (i.e. hwnd is NULL), DispatchMessage is useless. You need to write your own switch-case section that will handle the message recieved from GetMessage, instead of calling DispatchMessage.

Window Procedures

There are two kinds of messages: Messages posted or sent to windows, and messages posted to threads that are not associated with any window. Most messages in the system belong to the first group (i.e. the hwnd parameter is not NULL). The purpose of DispatchMessage is to send the contents of the message to the appropriate function that should deal with it, depending on the window (hwnd) associated with this message. The system registers a specific procedure (called Window Procedure) for every type of window in the system (called Window Class). This is the prototype of a window procedure:

LRESULT CALLBACK WindowProc(      
    HWND hwnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
);

In MFC you don’t directly deal with this basic window procedure. MFC is arranged such that the window procedure calls the function CWnd::WindowProc, that has the following prototype:

virtual LRESULT CWnd::WindowProc( UINT message, WPARAM wParam, LPARAM lParam );

You typically don’t even need to override this function. It does the work of dispatching the message to specific message-handler functions, such as CWnd::OnMouseMove, CWnd::OnPaint etc…, where you implement application-specific behavior.

Posting and Sending messages to windows

The most important functions for pushing messages to the message queue are PostMessage and SendMessage. Their prortotypes are:

BOOL PostMessage(HWND hWnd,    UINT Msg,    WPARAM wParam,    LPARAM lParam);
LRESULT SendMessage(HWND hWnd,    UINT Msg,    WPARAM wParam,    LPARAM lParam);

PostMessage adds the message to the given window’s message queue, and returns immediately, without waiting for the message to be processed. SendMessage does not return until the message has been handled by the window procedure (i.e. the recieving Windows procedures has called ReplyMessage).

Messages posted through PostMessage are called queued messages. Messages sent through SendMessage are called non-queued messages, and they are first to be retrieved by GetMessage. This means that they will be treated first, before the queued messages, regardless of the order in which the messages were posted/sent.

When calling SendMessage(hWnd,…)  from the same thread that manages the window of hWnd, the system will directly call that window’s window procedure. The message will not enter the message queue at all. When SendMessage is called from a different thread, the message is written to the message queue, but has precedence over all queued messages. SendMessage will not return until the recieving thread treats the message.

Things to notice when using SendMessage

Because SendMessage blocks execution, you should use it with care. If two windows of different threads (or even different applications) communicate carelessly using SendMessage, they can reach a dead-lock, when each thread waits for the other one to complete its task. When using SendMessage you have to be sure that the recieving window is not waiting for the calling thread, and is not too busy to handle the sent message.

A way to avoid deadlocks

Suppose that a worker thread wants to run an operation on the main thread (because it update GUI elements that should only be accessed by the main thread), and it uses SendMessage() to do so. If the main thread, at that time, is waiting for some event to be signaled by the worker thread, it will result in a deadlock. One solution, is to replace the wait from WaitForSingleObject(event,INFINITY) to the following loop:

While (WaitForSingleObject(event,50)=WAIT_TIMEOUT)

PeekMessage(&msg,…);

WaitForSingleObject waits for the event to be signaled. After 50 milliseconds, if the event has not been signaled, we call PeekMessage, which will respond to sent messages (not to queued messages), thereby preventing the deadlock.

Before using SendMessage, you should consult the MSDN documentation of this function.

Things to notice when using PostMessage

When you use PostMessage there is no risk of a dead-lock, since you’re not blocking the execution. However, there is a different risk: You don’t know when the message will eventually be treated. It can be far in the future – depending on how busy the application is. By that time, the parametes lParam and wParam may become invalid (if they are pointers to data), or the entire message may be irrelevant. If such a risk exists in your application, you have to find a way to signify to the function that responds to the message, that the message is not relevant any more.

Posting messages to threads

You can also use messages as a means of inter-thread communication. It is a bit like using events, in the sense that one thread is waiting for an event, and another thread signals the event, thereby causing the first thread to react. But it enables more detailed control due to the fact that messages have three parameters – the message type, and two parameters. In order to exploit this mechanism, you need to write your own message loop in one thread, and use PostThreadMessage to post messages to the thread’s message queue.

BOOL PostThreadMessage(      
    DWORD idThread,
    UINT Msg,
    WPARAM wParam,
    LPARAM lParam
);

The thread that handles the messages should run a message loop such as the following:

while(GetMessage( &msg, -1, 0, 0 ))  {
        switch (msg.message) {
            case WM_APP: do_some_action(msg.wParam,msg.lParam); break;
            case WM_APP+1: do_some_action1(); break;
            case WM_QUIT: return;
        }
}

IMPORTANT: Before you post a message to a thread, you should make sure that the thread has a message queue. When the thread is created, it doesn’t have a message queue, until the first call to PeekMessage, or other message-queue functions. To make sure the message queue is ready, call PeekMessage before the message loop, to create the message queue, and then signal an event to let other threads know that the message queue is ready.

Notice that PeekMessage and GetMessage have similar functions. The main difference between them is that PeekMessage returns immediately, and GetMessage waits for a message to be present in the message queue.

Ranges of message identifiers

It is important to know which numbers can be used as message identifiers, if you are going to use your own messages for communicating among windows or threads. The following ranges have different meanings:

0 to WM_USER-1: Reserved for use by the system. For example, WM_PAINT, WM_MOUSEMOVE, WM_CHAR. Send messages in this range to mimic system behavior. For example – to cause a window to paint itself, or to cause a dialog to react as if the user pressed a certain button, or to cause a window to close.

WM_USER to WM_APP-1: For use by private window classes. Don’t use it to send messages to other applications unless both applications are defined to understand them.

WM_APP to 0xBFFF: Available for use by applications. These will not conflict with system messages. To use them, you need to send WM_APP+x and in the message loop or window procedure, respond to messages with the identifier WM_APP+x. This is good for any x < 16384.

0xC000 to 0xFFFF: Named messages (called “string messages”) for use by applications. Like CreateEvent is used to create a named event for inter-process communication, RegisterWindowMessage is used to get the message identifier for a given name of message. The returned number won’t be the same in different sessions.

Greater than 0xFFFF: Reserved by the system for future use.

The WM_COPYDATA message

Normal messages send only three parameters. The WM_COPYDATA message has a special behavior that enables to send larger amounts of data between processes. When sending a WM_COPYDATA message, the lParam points to data, which will be copied to the address space of the recievnig process.

To send a WM_COPYDATA, you have to use SendMessage. PostMessage will not work. The wParam and lParam have special roles: wParam is a handle to the sender window, and lParam is a poiniter to a COPYDATASTRUCT structure:

typedef struct tagCOPYDATASTRUCT {
    ULONG_PTR dwData; // Some description of the data
    DWORD cbData; // Size, in bytes, of the data.
    PVOID lpData; // Pointer to data.
} COPYDATASTRUCT, *PCOPYDATASTRUCT;

The recieving application should consider the data read-only. The recieved data in lParam is only valid in the recieving application during the processing of the message.

The WM_TIMER message

Timers also work through the message loop. A timer is a mechanism that enables you run a prescribed operation every X milliseconds. To define a timer, you use the function SetTimer:

UINT_PTR SetTimer(      
    HWND hWnd,
    UINT_PTR nIDEvent,
    UINT uElapse,
    TIMERPROC lpTimerFunc
);

The most common way to work with a timer is to associate it with a window (by sending hWnd in the first parameter). When you do so, the window procedure associated with that window will recieve WM_TIMER messages every uElapse milliseconds. The default windows procedure calls the callback function lpTimerFunc, if it is not NULL.

In MFC, you use CWnd::SetTimer:

UINT_PTR SetTimer(
   UINT_PTR nIDEvent,
   UINT nElapse,
   void (CALLBACK* lpfnTimer)(HWND,   UINT,   UINT_PTR,   DWORD)
);

which is exactly the same, but saves you the need of knowing hWnd.

In MFC, the most convenient thing is to send a NULL lpfnTimer, and treat the message using the OnTimer() event handler of the relevant window.

A WM_TIMER message is generated when the time elapses by GetMessage or PeekMessage of the appropriate thread, which is the thread in which the relevant window was created, or, in case hWnd is NULL, the thread that called SetTimer().

In the case the hWnd parameter in SetTimer() is NULL, this means that the timer is not associated with any window. It is handled in one of two ways: Either you write your own switch-case statement in the message loop, that handles WM_TIMER messages, or the default window procedure is called, and it invokes the given callback function lpfnTimer.

It is important to know that WM_TIMER is a low-priority message. It won’t be generated if there are messages waiting in the message queue, or unqueued (sent) messages. If a timer is designed to send a WM_TIMER message every 1 second, and the thread is busy doing some long computation 10 seconds, then immediately after the thread finishes the long computation, a WM_TIMER will be sent. During these 10 seconds, 9 scheduled WM_TIMER messages have been lost (or, more precisely, they have not been generated). For the exact order or priority at which messages get treated, see the remarks section of the MSDN entry on PeekMessage.

To stop a timer from working, call KillTimer. If KillTimer is called from a different thread from the thread that responds to the WM_TIMER message, it is possible that the OnTimer event handler will work once after KillTimer has finished. If this is hazardeous to your application, you have to write your own synchronization code to prevent it from happening.

Advertisements

3 Comments »

  1. […] Depending on the circumstances of your application, you should decide which of them to use (see my post on messages and message loop for the issues related to SendMessage and […]

    Pingback by Multithreading Patterns #3 – Call In Main Thread « Adi Levin's Blog for programmers — August 28, 2009 @ 12:53 pm | Reply

  2. […] the master thread there should be a message loop (see my post on messages and message queues). Whenever a task finishes its work in a worker thread, it sends a message to the master thread […]

    Pingback by Constrained Parallelism « Adi Levin's Blog for programmers — August 30, 2009 @ 9:14 pm | Reply

  3. Hi Adi, Here’s something you could add about SendMessage: It unblocks for incoming SendMessages sent from other threads. I just uncovered a bug/crash where SendMessage unblocked for an incoming SendMessage that resulted in the deletion of a resource that was in use by the code that had sent the outgoing SendMessage. When the outgoing SendMessage eventually completed and processing continued the attempt to use the deleted resource caused an exception. The original author probably assumed that SendMessage would always block. The alternative I used is SendMessageTimeout with SMTO_BLOCK set. That inhibits SendMessage from unblocking for SendMessages arriving from other threads.

    Comment by Pete Brunet — April 8, 2013 @ 10:58 pm | Reply


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: