Introduction
In this article, I will explain all about Win32 messages.
Terminology
- System - Operating System
- Applications - Microsoft Word or any other application
Contents
- Windows Messages
- Message Types
- System-Defined Messages
- Application-Defined Messages
- Message Routing
- Queued Messages
- Non-queued Messages
- Message Handling
- Message Loop
- Window Procedure
- Posting and Sending Messages
- Posting Messages
- Sending Messages
- Message Deadlocks
- Broadcasting 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)
{
....
....
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg); DispatchMessage(&msg); }
return msg.wParam;
}
LRESULT CALLBACK WindowFunc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
}
return 0;
}
There are two types of messages:
- System-Defined Messages
- 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.
The system uses two methods to route messages to a window procedure:
- 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).
- 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()
.
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:
- Message Loop
- 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)
{
}
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.
LRESULT CALLBACK WindowFunc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
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: if (InSendMessage())
{
ReplyMessage(TRUE);
}
DialogBox(hinst, "MyDialogBox", hwndMain, (DLGPROC) MyDlgProc);
break;
}
return 0;
}
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
- What is a message queue?
A queue is an Operating System defined memory object that temporarily stores messages.
- What are
SendMessage()
and PostMessage()
?
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).