Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Understanding message queue and the different types of Win32 messages

0.00/5 (No votes)
2 Jun 2010 1  
A simple way to understand message queue and different types of Win32 messages.

Introduction

In this article, I will explain all about Win32 messages.

Terminology

  • System - Operating System
  • Applications - Microsoft Word or any other application

Contents

  1. Windows Messages
  2. Message Types
    1. System-Defined Messages
    2. Application-Defined Messages
  3. Message Routing
    1. Queued Messages
    2. Non-queued Messages
  4. Message Handling
    1. Message Loop
    2. Window Procedure
  5. Posting and Sending Messages
    1. Posting Messages
    2. Sending Messages
    3. Message Deadlocks
  6. Broadcasting Messages

Windows Messages

The system passes all input (from input devices) to the various windows in an application. Each window has a function called a window procedure; the system calls this function whenever it has input. The window procedure function processes the input and returns control to the system. The system passes input to the window procedure function in the form of messages.

Messages are generated by both system and applications. The system generates a message at each input event - e.g., when the user types, moves the mouse, or clicks a control. An application can generate messages - e.g. when a dialog control (sel_change of Edit ctrl) creates a message and notifies its own window (dialog).

The system sends a message to a window procedure function with a set of four parameters: a window handle, a message identifier, and two values called message parameters. The values of the message parameters depend on the message.

Example:

int WINAPI WinMain(HINSTANCE hThisInst, HINSTANCE hPrevInst, LPSTR lpszArgs, int nWinMode)
{
    ....
    ....
  // Create the message loop. 
  while(GetMessage(&msg, NULL, 0, 0))
  {
    TranslateMessage(&msg); // translate keyboard messages 
    DispatchMessage(&msg);  // return control to Windows 2000
  }

  return msg.wParam;
}

/* This Window procedure function is called by Windows 2000 
  (Operating System) and is passed messages from the message queue. */
LRESULT CALLBACK WindowFunc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  switch(message) 
  {
  }
  return 0;
}

Message Types

There are two types of messages:

  1. System-Defined Messages
  2. Application-Defined Messages

System-Defined Messages

The (Operating) System sends or posts a system-defined message when it communicates with an application.

An application can also send or post system-defined messages (don't confuse these with those sent from the System Queue). E.g., when we change something in an edit control, a dialog sends EM_SELCHANGE to the edit control.

The prefix of the constant identifies the type of the window (or type of the control) that can interpret and process the message. Following are the prefixes and their related message categories.

Prefix Message category 

ABM    Application desktop toolbar  
BM     Button control  
CB     Combo box control  
CBEM   Extended combo box control 
CDM    Common dialog box  
DBT    Device  
DL     Drag list box  
DM     Default push button control  
DTM    Date and time picker control 
EM     Edit control  
HDM    Header control  
HKM    Hot key control 
IPM    IP address control 
LB     List box control  
LVM    List view control  
MCM    Month calendar control 
PBM    Progress bar  
PGM    Pager control 
PSM    Property sheet  
RB     Rebar control 
SB     Status bar window  
SBM    Scroll bar control  
STM    Static control  
TB     Toolbar  
TBM    Trackbar  
TCM    Tab control  
TTM    Tooltip control  
TVM    Tree-view control  
UDM    Up-down control 
WM     General window        (***)

General window messages (***) cover a wide range of information and requests, including messages for mouse and keyboard input, menu and dialog box input, window creation and management, and Dynamic Data Exchange (DDE).

Range of system-defined message-identifier values:

0x0000        -    WM_USER-1 (0x03FF – 1 = 1023)

Application-Defined Messages

An application can create messages to be used by its own windows or to communicate with windows in other processes. If an application creates its own messages, the window procedure function that receives them must interpret the messages and do the appropriate processing.

Range of Application-defined message-identifier values:

Range                                Description 
-------------------------------------------------------------------------------------

WM_USER       -    WM_APP - 1             Message-Identifier values for use 
(1024)             (0x7FFF - 1 = 32767)   by private window classes. 

WM_APP        -    0xBFFF                 Messages available for use by applications. 
(32768)            (49151)

0xC000        -    0xFFFF                 String messages for use by applications. 
(49152)            (65535)

Values greater than 0xFFFF are reserved by the system for future use.

The message ID range in the first row (WM_USER - 0x7FFF) can be defined and used by an application to send messages within a private window class. These values cannot be used throughout an application, because some predefined window classes already define values in this range. For example, predefined control classes such as BUTTON, EDIT, and LISTBOX may use these values. (This range should not be sent to other applications unless the applications have been designed to exchange messages.)

Message ID range in the second row (0x8000 - 0xBFFF) is available for applications as private messages. This range does not conflict with system messages.

Message ID range in the third row (0xC000 - 0xFFFF) is defined at run time when an application calls the RegisterWindowMessage() function to retrieve a message number for a string.

If a different application registers the same string, then they can exchange messages. In different sessions, the message ID will be different. See RegisterWindowMessage().

Examples of application-defined messages range will be given in the next section.

Message Routing

The system uses two methods to route messages to a window procedure:

  1. Posting messages to a first-in, first-out queue called a message queue (this queue is an Operating System defined memory object that temporarily stores messages).
  2. Sending messages directly to a window procedure.

Messages posted to a message queue are called "queued messages". Most other messages, which are sent directly to a window procedure, are called "non-queued messages".

Queued Messages

The system maintains a "single system message queue" and a "thread-specific message queue" for each graphical user interface (GUI) thread. (The system creates a thread-specific message queue only when the thread makes its first call to one of the User or Windows Graphics Device Interface (GDI) functions.)

Whenever the user moves the mouse, clicks the mouse buttons, or types on the keyboard, the device driver for the mouse or keyboard converts the input into messages and places them in the "system message queue". The system removes the messages, one at a time, from the "system message queue", then examines them to determine the destination window, and then posts them to a "thread-specific message queue" of the destination window. A "thread-specific message queue" of the destination window receives all the mouse and keyboard messages. The thread removes messages from its queue and directs (requests) the Operating System (using DispatchMessage()) to send them to the appropriate window procedure for processing.

But there is an exception: the system always posts - WM_PAINT, WM_TIMER, WM_QUIT messages at the end (last) of the (thread's) message queue. The WM_PAINT, WM_TIMER, and WM_QUIT messages are kept in the queue and are forwarded to the window procedure only when the queue contains no other messages. In addition, multiple WM_PAINT messages for the same window are combined into a single WM_PAINT message. Combining WM_PAINT messages reduces the number of times messages need to be sent.

The system posts a message from the "system message queue" to a "thread's message queue" by filling an MSG structure and then copying it. A thread can post a message to its own (thread's) message queue or to the queue of another thread by using the PostMessage() or PostThreadMessage() function.

An application can remove a message from its (thread's) queue by using the GetMessage() function. But to examine a message without removing it from its queue, an application can use the PeekMessage() function. This function fills the MSG structure with information about the message.

After removing a message from its queue, an application can use the DispatchMessage() function to direct the Operating System to send the message to a window procedure for processing. DispatchMessage() takes a pointer to the MSG struct, and this pointer has the window handle, the message identifier, and the two message parameters to the window procedure, but it does not pass the message posted time or the mouse cursor position. An application can retrieve this information by calling the GetMessageTime() and GetMessagePos() functions while processing a message.

See - WaitMessage(), SetMessageExtraInfo(), GetMessageExtraInfo().

Non-queued Messages

Non-queued messages are sent immediately to the destination window procedure, bypassing (avoiding) the "system message queue" and the "thread message queue". The system typically sends non-queued messages to notify a window of events that affect it.

For example, when the user activates a new application window, the system sends the window a series of messages, including WM_ACTIVATE, WM_SETFOCUS, and WM_SETCURSOR. These messages notify the window that it has been activated, the keyboard input is being directed to the window, and the mouse cursor has been moved within the borders of the window.

Non-queued messages can also result when an application calls certain system functions (like SetWindowText() = WM_SETTEXT).

For example, the system sends the WM_WINDOWPOSCHANGED message after an application uses the SetWindowPos() function to move a window. See - BroadcastSystemMessage(), BroadcastSystemMessageEx(), SendMessage(), SendMessageTimeout(), SendNotifyMessage().

Message Handling

An application must remove and process messages from the thread's message queues. A single-threaded application usually uses a message loop in its WinMain function to remove and send messages to the appropriate window procedures for processing. Applications with multiple threads can include a message loop in each thread (that is a window thread).

The following sections describe how a message loop works, and explains the role of a window procedure:

  1. Message Loop
  2. Window Procedure

Message Loop

A simple message loop consists of three functions: GetMessage, TranslateMessage, and DispatchMessage. But you can arrange the message loop in your own way. And it all depends on your requirements.

//-----
MSG msg;
BOOL bRet;

while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{ 
    if (bRet == -1)
    {
        // handle the error and possibly exit
    }
    else
    {
        TranslateMessage(&msg); 
        DispatchMessage(&msg); 
    }
}
//-----
  • GetMessage - The GetMessage function retrieves a message from the queue and copies it to a structure of type MSG. It returns a non-zero value, except for the WM_QUIT message. An application can end its own loop by using the PostQuitMessage function while handling the WM_DESTROY message in the window procedure.
  • With GetMessage, you can also retrieve a message for a particular window, filter messages for a specified range, or search for specific messages.

    See - Message Filtering.

  • TranslateMessage - A thread's message loop must include TranslateMessage() to receive keyboard input and translate it. For every key press, the Operating System generates virtual-key messages (WM_KEYDOWN and WM_KEYUP). A virtual-key code identifies which key was pressed, but not its character value. The TranslateMessage() function translates the virtual-key message into a character message (WM_CHAR) and places it back into the thread's message queue. And with subsequent iterations of the message loop, the "character message" is removed from the queue.
  • DispatchMessage - The DispatchMessage() function sends a message to the window procedure associated with the window handle specified in the MSG structure.
  • If the window handle is HWND_TOPMOST, DispatchMessage() sends the message to the window procedures of all top-level windows in the system. If the window handle is NULL, DispatchMessage() does nothing with the message. The message loop, in a non-stop way, retrieves messages from the thread's message queue and dispatches them to the appropriate window, until it gets a WM_QUIT. Only one message loop is needed for a message queue, even if an application contains many windows.

    An application that uses accelerator keys must call the TranslateAccelerator() function in the message loop that will translate keyboard messages into command messages. To handle message for a modeless dialog box, the message loop must include IsDialogMessage().

Window Procedure

A window procedure is a function that receives and processes all messages sent to the window. Every window class has a window procedure, and every window created with that class uses the same window procedure to respond to messages.

  • DefWindowProc - A window procedure does not usually ignore a message. If it does not process a message, then the message must be sent to the System for default processing by calling the DefWindowProc() function. And, the return value of DefWindowProc() must return from the window procedure as the message is handled by this window procedure.
  • Example: here, the WindowFunc function is a window procedure.

    /* This function is called by Windows (2000) and is passed messages 
       from the message queue, when DispatchMessage() called. */
    LRESULT CALLBACK WindowFunc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
      switch(message) 
      {
        case WM_DESTROY:    // terminate the program 
    
          PostQuitMessage(0);
          break;
    
        default:
                /* Let Windows (2000) process any messages not 
                   specified in the preceding switch statement. */
          return  DefWindowProc(hwnd, message, wParam, lParam);
      }
    
      return 0;
    }

Posting and Sending Messages

Any application can post and send messages.

PostMessage() is used for posting a message, and for sending a message, the SendMessage(), BroadcastSystemMessage(), SendMessageCallback(), SendMessageTimeout(), SendNotifyMessage(), and SendDlgItemMessage() functions are used.

Posting Messages

PostMessage() creates an MSG structure for the message and copies the message to the (thread's) message queue (not to the window procedure).

If an application posts a message without specifying a window handle (=NULL), the message is posted to the current thread's queue. This is one way to create a message that applies to the entire application, instead of to a specific window.

Common programming error: We assume that the PostMessage() function will always post a message in the queue, but this is not true when the message queue is full. An application should check the return value of the PostMessage() function to determine whether the message has been posted and, if not, repost it.

Sending Messages

The SendMessage() function is used to send a message directly to a window procedure to perform a task immediately (not to queue).

The function waits until the window procedure completes processing. Parent and child windows often communicate by sending the SendMessage() function to each other.

For example, a parent window (dialog) that has an edit control as its child window can set the text of the control by sending a message to the edit control. The control can notify the parent window about changes to the text (in the edit ctrl) by sending messages back to the parent window.

  • SendMessageCallback - The SendMessageCallback() function also sends a message to the window procedure. However, this function returns immediately. After processing the message in the window procedure function, the system calls the specified callback function.
  • See - SendAsyncProc() (callback function).

  • BroadcastSystemMessage - To broadcast messages to all applications.
  • InSendMessage
  • InSendMessageEx

    The InSendMessage() and InSendMessageEx() functions are useful when you need to determine which thread a message is coming from.

Message Deadlocks

If you send a message to another thread and the receiving thread yields (gives up) the control while processing the message, the sending thread cannot continue executing, because it is waiting for SendMessage() to return. This situation is called a message deadlock.

Calling any of the following functions can cause a thread to yield control implicitly (hidden).

  • DialogBox
  • DialogBoxIndirect
  • DialogBoxIndirectParam
  • DialogBoxParam
  • GetMessage
  • MessageBox
  • PeekMessage
  • SendMessage

There are two ways to avoid application deadlocks:

  • To use SendNotifyMessage() or SendMessageTimeout() to send a message.
  • Before calling any of the functions in the preceding list, you should call InSendMessage() or InSendMessageEx(). If they return TRUE, the window procedure must call the ReplyMessage().

Example::

LRESULT CALLBACK WindowFunc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  switch(message) 
  {
    case WM_USER + 5:        //Application defined message        
    if (InSendMessage()) 
    {
        //Message received from another thread.
        ReplyMessage(TRUE); 
    }     
    DialogBox(hinst, "MyDialogBox", hwndMain, (DLGPROC) MyDlgProc); 

    break;
  }

  return 0;
}

Broadcasting Messages

A message broadcast is simply sending a message to multiple recipients in the system, and for this, the BroadcastSystemMessage() function is used. Recipients can be applications, installable drivers, network drivers, and system-level device drivers.

FAQ

  1. What is a message queue?
  2. A queue is an Operating System defined memory object that temporarily stores messages.

  3. What are SendMessage() and PostMessage()?
  4. The SendMessage() function sends a message to a specified window's window procedure (not in the system message queue) and the PostMessage() function places a message in thread's message queue of a specified window (not in the system message queue).

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here