Windows Messages
Windows Messages
The system passes input to a window procedure in the form of a message. Messages are generated by
both the system and applications. The system generates a message at each input event — for example,
when the user types, moves the mouse, or clicks a control such as a scroll bar. The system also generates
messages in response to changes in the system brought about by an application, such as when an
application changes the pool of system font resources or resizes one of its windows. An application can
generate messages to direct its own windows to perform tasks or to communicate with windows in other
applications.
The system sends a message to a window procedure with a set of four parameters: a window handle, a
message identifier, and two values called message parameters. The window handle identifies the window
for which the message is intended. The system uses it to determine which window procedure should
receive the message.
A message identifier is a named constant that identifies the purpose of a message. When a window
procedure receives a message, it uses a message identifier to determine how to process the message. For
example, the message identifier WM_PAINT tells the window procedure that the window's client area has
changed and must be repainted.
Message parameters specify data or the location of data used by a window procedure when processing a
message. The meaning and value of the message parameters depend on the message. A message
parameter can contain an integer, packed bit flags, a pointer to a structure containing additional data, and
so on. When a message does not use message parameters, they are typically set to NULL. A window
procedure must check the message identifier to determine how to interpret the message parameters.
Message Types
System-Defined Messages
Application-Defined Messages
System-Defined Messages
The system sends or posts a system-defined message when it communicates with an application. It uses
these messages to control the operations of applications and to provide input and other information for
applications to process. An application can also send or post system-defined messages. Applications
generally use these messages to control the operation of control windows created by using preregistered
window classes.
Each system-defined message has a unique message identifier and a corresponding symbolic constant
(defined in the software development kit (SDK) header files) that states the purpose of the message. For
example, theWM_PAINT constant requests that a window paint its contents.
Symbolic constants specify the category to which system-defined messages belong. The prefix of the
constant identifies the type of window that can interpret and process the message. Following are the
prefixes and their related message categories.
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).
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 that receives them
must interpret the messages and provide appropriate processing.
The system reserves message-identifier values in the range 0x0000 through 0x03FF (the value
of WM_USER – 1) for system-defined messages. Applications cannot use these values for private
messages.
Values in the range 0x0400 (the value of WM_USER) through 0x7FFF are available for message
identifiers for private window classes.
If your application is marked version 4.0, you can use message-identifier values in the range
0x8000 (WM_APP) through 0xBFFF for private messages.
The system returns a message identifier in the range 0xC000 through 0xFFFF when an application
calls theRegisterWindowMessage function to register a message. The message identifier
returned by this function is guaranteed to be unique throughout the system. Use of this function
prevents conflicts that can arise if other applications use the same message identifier for different
purposes.
Message Routing
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, a system-defined memory object that temporarily stores
messages, and sending messages directly to a window procedure.
A messages that is posted to a message queue is called a queued message. These are primarily the result
of user input entered through the mouse or keyboard, such
as WM_MOUSEMOVE, WM_LBUTTONDOWN,WM_KEYDOWN, and WM_CHAR messages. Other
queued messages include the timer, paint, and quit messages:WM_TIMER, WM_PAINT, and WM_QUIT.
Most other messages, which are sent directly to a window procedure, are called nonqueued messages.
Queued Messages
Nonqueued Messages
Queued Messages
The system can display any number of windows at a time. To route mouse and keyboard input to the
appropriate window, the system uses message queues.
The system maintains a single system message queue and one thread-specific message queue for each
GUI thread. To avoid the overhead of creating a message queue for non–GUI threads, all threads are
created initially without a message queue. The system creates a thread-specific message queue only when
the thread makes its first call to one of the specific user functions; no GUI function calls result in the
creation of a message queue.
Queued Messages
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,
examines them to determine the destination window, and then posts them to the message queue of the
thread that created the destination window. A thread's message queue receives all mouse and keyboard
messages for the windows created by the thread. The thread removes messages from its queue and
directs the system to send them to the appropriate window procedure for processing.
The system posts a message to a thread's message queue by filling an MSG structure and then copying it
to the message queue. Information in MSG includes: the handle of the window for which the message is
intended, the message identifier, the two message parameters, the time the message was posted, and the
mouse cursor position. A thread can post a message to its own message queue or to the queue of
another thread by using thePostMessage or PostThreadMessage function.
An application can remove a message from its queue by using the GetMessage function. To examine a
message without removing it from its queue, an application can use the PeekMessage function. This
function fills MSG with information about the message.
After removing a message from its queue, an application can use the DispatchMessage function to direct
the system to send the message to a window procedure for processing. DispatchMessage takes a pointer
to MSGthat was filled by a previous call to
the GetMessage or PeekMessage function. DispatchMessage passes the window handle, the message
identifier, and the two message parameters to the window procedure, but it does not pass the time the
message was posted or mouse cursor position. An application can retrieve this information by calling
the GetMessageTime and GetMessagePos functions while processing a message.
A thread can use the WaitMessage function to yield control to other threads when it has no messages in
its message queue. The function suspends the thread and does not return until a new message is placed
in the thread's message queue.
You can call the SetMessageExtraInfo function to associate a value with the current thread's message
queue. Then call the GetMessageExtraInfo function to get the value associated with the last message
retrieved by theGetMessage or PeekMessage function.
Nonqueued Messages
Nonqueued messages are sent immediately to the destination window procedure, bypassing the system
message queue and thread message queue. The system typically sends nonqueued 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, that keyboard input
is being directed to the window, and that the mouse cursor has been moved within the borders of the
window. Nonqueued messages can also result when an application calls certain system functions. For
example, the system sends the WM_WINDOWPOSCHANGED message after an application uses
the SetWindowPos function to move a window.
Message Handling
An application must remove and process messages posted to the message queues of its threads. 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 creates a window. The following sections describe how a message loop
works and explain the role of a window procedure:
Message Loop
Window Procedure
Message Loop
A simple message loop consists of one function call to each of these three
functions: GetMessage,TranslateMessage, and DispatchMessage. Note that if there is an
error, GetMessage returns -1 -- thus the need for the special testing.
Copy
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);
}
}
The GetMessage function retrieves a message from the queue and copies it to a structure of type MSG. It
returns a nonzero value, unless it encounters the WM_QUIT message, in which case it returns FALSE and
ends the loop. In a single-threaded application, ending the message loop is often the first step in closing
the application. An application can end its own loop by using the PostQuitMessage function, typically in
response to theWM_DESTROY message in the window procedure of the application's main window.
If you specify a window handle as the second parameter of GetMessage, only messages for the specified
window are retrieved from the queue. GetMessage can also filter messages in the queue, retrieving only
those messages that fall within a specified range. For more information about filtering messages,
see Message Filtering.
A thread's message loop must include TranslateMessage if the thread is to receive character input from
the keyboard. The system generates virtual-key messages (WM_KEYDOWN and WM_KEYUP) each time
the user presses a key. A virtual-key message contains a virtual-key code that identifies which key was
pressed, but not its character value. To retrieve this value, the message loop must
contain TranslateMessage, which translates the virtual-key message into a character message
(WM_CHAR) and places it back into the application message queue. The character message can then be
removed upon a subsequent iteration of the message loop and dispatched to a window procedure.
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, DispatchMessagedoes nothing with the message.
An application's main thread starts its message loop after initializing the application and creating at least
one window. After it is started, the message loop continues to retrieve messages from the thread's
message queue and to dispatch them to the appropriate windows. The message loop ends when
the GetMessage function removes the WM_QUIT message from the message queue.
Only one message loop is needed for a message queue, even if an application contains many
windows.DispatchMessage always dispatches the message to the proper window; this is because each
message in the queue is an MSG structure that contains the handle of the window to which the message
belongs.
You can modify a message loop in a variety of ways. For example, you can retrieve messages from the
queue without dispatching them to a window. This is useful for applications that post messages not
specifying a window. You can also direct GetMessage to search for specific messages, leaving other
messages in the queue. This is useful if you must temporarily bypass the usual FIFO order of the message
queue.
An application that uses accelerator keys must be able to translate keyboard messages into command
messages. To do this, the application's message loop must include a call to
the TranslateAccelerator function. For more information about accelerator keys, see Keyboard
Accelerators.
If a thread uses a modeless dialog box, the message loop must include the IsDialogMessage function so
that the dialog box can receive keyboard input.
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 that same window
procedure to respond to messages.
The system sends a message to a window procedure by passing the message data as arguments to the
procedure. The window procedure then performs an appropriate action for the message; it checks the
message identifier and, while processing the message, uses the information specified by the message
parameters.
A window procedure does not usually ignore a message. If it does not process a message, it must send
the message back to the system for default processing. The window procedure does this by calling
theDefWindowProc function, which performs a default action and returns a message result. The window
procedure must then return this value as its own message result. Most window procedures process just a
few messages and pass the others on to the system by calling DefWindowProc.
Because a window procedure is shared by all windows belonging to the same class, it can process
messages for several different windows. To identify the specific window affected by the message, a
window procedure can examine the window handle passed with a message. For more information about
window procedures, see Window Procedures.
Message Filtering
An application can choose specific messages to retrieve from the message queue (while ignoring other
messages) by using the GetMessage or PeekMessage function to specify a message filter. The filter is a
range of message identifiers (specified by a first and last identifier), a window handle, or
both. GetMessage and PeekMessage use a message filter to select which messages to retrieve from the
queue. Message filtering is useful if an application must search the message queue for messages that
have arrived later in the queue. It is also useful if an application must process input (hardware) messages
before processing posted messages.
The WM_KEYFIRST and WM_KEYLAST constants can be used as filter values to retrieve all keyboard
messages; the WM_MOUSEFIRST and WM_MOUSELAST constants can be used to retrieve all mouse
messages.
Any application that filters messages must ensure that a message satisfying the message filter can be
posted. For example, if an application filters for a WM_CHAR message in a window that does not receive
keyboard input, theGetMessage function does not return. This effectively "hangs" the application.
Any application can post and send messages. Like the system, an application posts a message by copying
it to a message queue and sends a message by passing the message data as arguments to a window
procedure. To post messages, an application uses the PostMessage function. An application can send a
message by calling
theSendMessage, BroadcastSystemMessage, SendMessageCallback, SendMessageTimeout, SendNot
ifyMessage, or SendDlgItemMessage function.
Posting Messages
An application can post a message without specifying a window. If the application supplies a NULL
window handle when calling PostMessage, the message is posted to the queue associated with the
current thread. Because no window handle is specified, the application must process the message in the
message loop. This is one way to create a message that applies to the entire application, instead of to a
specific window.
Occasionally, you may want to post a message to all top-level windows in the system. An application can
post a message to all top-level windows by calling PostMessage and specifying HWND_TOPMOST in
the hwndparameter.
A common programming error is to assume that the PostMessage function always posts a message. 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 it has not been,
repost it.
Sending Messages
An application typically sends a message to notify a window procedure to perform a task immediately.
TheSendMessage function sends the message to the window procedure corresponding to the given
window. The function waits until the window procedure completes processing and then returns the
message result. Parent and child windows often communicate by sending messages to each other. For
example, a parent window that has an edit control as its child window can set the text of the control by
sending a message to it. The control can notify the parent window of changes to the text that are carried
out by the user by sending messages back to the parent.
The SendMessageCallback function also sends a message to the window procedure corresponding to
the given window. However, this function returns immediately. After the window procedure processes the
message, the system calls the specified callback function. For more information about the callback
function, see theSendAsyncProc function.
Occasionally, you may want to send a message to all top-level windows in the system. For example, if the
application changes the system time, it must notify all top-level windows about the change by sending
aWM_TIMECHANGE message. An application can send a message to all top-level windows by
callingSendMessage and specifying HWND_TOPMOST in the hwnd parameter. You can also broadcast a
message to all applications by calling the BroadcastSystemMessage function and specifying
BSM_APPLICATIONS in thelpdwRecipients parameter.
Message Deadlocks
A thread that calls the SendMessage function to send a message to another thread cannot continue
executing until the window procedure that receives the message returns. If the receiving thread yields
control while processing the message, the sending thread cannot continue executing, because it is waiting
for SendMessage to return. If the receiving thread is attached to the same queue as the sender, it can
cause an application deadlock to occur. (Note that journal hooks attach threads to the same queue.)
Note that the receiving thread need not yield control explicitly; calling any of the following functions can
cause a thread to yield control implicitly.
DialogBox
DialogBoxIndirect
DialogBoxIndirectParam
DialogBoxParam
GetMessage
MessageBox
PeekMessage
SendMessage
Broadcasting Messages
Each message consists of a message identifier and two parameters, wParam and lParam. The message
identifier is a unique value that specifies the message purpose. The parameters provide additional
information that is message-specific, but the wParam parameter is generally a type value that provides
more information about the message.
A message broadcast is simply the sending of a message to multiple recipients in the system. To broadcast
a message from an application, use the BroadcastSystemMessage function, specifying the recipients of
the message. Rather than specify individual recipients, you must specify one or more types of recipients.
These types are applications, installable drivers, network drivers, and system-level device drivers. The
system sends broadcast messages to all members of each specified type.
The system typically broadcasts messages in response to changes that take place within system-level
device drivers or related components. The driver or related component broadcasts the message to
applications and other components to notify them of the change. For example, the component
responsible for disk drives broadcasts a message whenever the device driver for the floppy disk drive
detects a change of media such as when the user inserts a disk in the drive.
The system broadcasts messages to recipients in this order: system-level device drivers, network drivers,
installable drivers, and applications. This means that system-level device drivers, if chosen as recipients,
always get the first opportunity to respond to a message. Within a given recipient type, no driver is
guaranteed to receive a given message before any other driver. This means that a message intended for a
specific driver must have a globally-unique message identifier so that no other driver unintentionally
processes it.
You can also broadcast messages to all top-level windows by specifying HWND_BROADCAST in
the SendMessage,SendMessageCallback, SendMessageTimeout, or SendNotifyMessage function.
Applications receive messages through the window procedure of their top-level windows. Messages are
not sent to child windows. Services can receive messages through a window procedure or their service
control handlers.
Note System-level device drivers use a related, system-level function to broadcast system messages.
Query Messages
You can create your own custom messages and use them to coordinate activities between your
applications and other components in the system. This is especially useful if you have created your own
installable drivers or system-level device drivers. Your custom messages can carry information to and from
your driver and the applications that use the driver.
To poll recipients for permission to carry out a given action, use a query message. You can generate your
own query messages by setting the BSF_QUERY value in the dwFlags parameter when
calling BroadcastSystemMessage. Each recipient of the query message must return TRUE for the
function to send the message to the next recipient. If any recipient returns BROADCAST_QUERY_DENY,
the broadcast ends immediately and the function returns a zero.