Upravit

Sdílet prostřednictvím


About Messages and Message Queues

Unlike MS-DOS-based applications, Windows-based applications are event-driven. They do not make explicit function calls (such as C run-time library calls) to obtain input. Instead, they wait for the system to pass input to them.

The system passes all input for an application to the various windows in the application. Each window has a function, called a window procedure, that the system calls whenever it has input for the window. The window procedure processes the input and returns control to the system. For more information about window procedures, see Window Procedures.

If a top-level window stops responding to messages for more than several seconds, the system considers the window to be not responding. In this case, the system hides the window and replaces it with a ghost window that has the same Z order, location, size, and visual attributes. This allows the user to move it, resize it, or even close the application. However, these are the only actions available because the application is actually not responding. When in the debugger mode, the system does not generate a ghost window.

This section discusses the following topics:

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

This section describes the two types of 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, the WM_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.

Prefix Message category Documentation
ABM and ABN Application desktop toolbar Shell Messages and Notifications
ACM and ACN Animation control Animation Control Messages and Animation Control Notifications
BCM, BCN, BM, and BN Button control Button Control Messages and Button Control Notifications
CB and CBN ComboBox control ComboBox Control Messages and ComboBox Control Notifications
CBEM and CBEN ComboBoxEx control ComboBoxEx Messages and ComboBoxEx Notifications
CCM General control Control Messages
CDM Common dialog box Common Dialog Box Messages
DFM Default context menu Shell Messages and Notifications
DL Drag list box Drag List Box Notifications
DM Default push button control Dialog Box Messages
DTM and DTN Date and time picker control Date and Time Picker Messages and Date and Time Picker Notifications
EM and EN Edit control Edit Control Messages, Edit Control Notifications, Rich Edit Messages, and Rich Edit Notifications
HDM and HDN Header control Header Control Messages and Header Control Notifications
HKM Hot key control Hot Key Control Messages
IPM and IPN IP address control IP Address Messages and IP Address Notifications
LB and LBN List box control List Box Messages and List Box Notifications
LM SysLink control SysLink Control Messages
LVM and LVN List view control List View Messages and List View Notifications
MCM and MCN Month calendar control Month Calendar Messages and Month Calendar Notifications
PBM Progress bar Progress Bar Messages
PGM and PGN Pager control Pager Control Messages and Pager Control Notifications
PSM and PSN Property sheet Property Sheet Messages and Property Sheet Notifications
RB and RBN Rebar control Rebar Control Messages and Rebar Control Notifications
SB and SBN Status bar window Status Bar Messages and Status Bar Notifications
SBM Scroll bar control Scroll Bar Messages
SMC Shell menu Shell Messages and Notifications
STM and STN Static control Static Control Messages and Static Control Notifications
TB and TBN Toolbar Toolbar Control Messages and Toolbar Control Notifications
TBM and TRBN Trackbar control Trackbar Control Messages and Trackbar Control Notifications
TCM and TCN Tab control Tab Control Messages and Tab Control Notifications
TDM and TDN Task dialog Task Dialog Messages and Task Dialog Notifications
TTM and TTN Tooltip control Tooltip Control Messages and Tooltip Control Notifications
TVM and TVN Tree-view control Tree View Messages and Tree View Notifications
UDM and UDN Up-down control Up-Down Messages and Up-Down Notifications
WM General
Clipboard Messages
Clipboard Notifications
Common Dialog Box Notifications
Cursor Notifications
Data Copy Message
Desktop Window Manager Messages
Device Management Messages
Dialog Box Notifications
Dynamic Data Exchange Messages
Dynamic Data Exchange Notifications
Hook Notifications
Keyboard Accelerator Messages
Keyboard Accelerator Notifications
Keyboard Input Messages
Keyboard Input Notifications
Menu Notifications
Mouse Input Notifications
Multiple Document Interface Messages
Raw Input Notifications
Scroll Bar Notifications
Timer Notifications
Window Messages
Window Notifications

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.

Message-identifier values are used as follows:

  • 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 the RegisterWindowMessage 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

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.

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.

With the exception of the WM_PAINT message, the WM_TIMER message, and the WM_QUIT message, the system always posts messages at the end of a message queue. This ensures that a window receives its input messages in the proper first in, first out (FIFO) sequence. The WM_PAINT message, the WM_TIMER message, and the WM_QUIT message, however, 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, consolidating all invalid parts of the client area into a single area. Combining WM_PAINT messages reduces the number of times a window must redraw the contents of its client area.

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 the PostMessage 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 MSG that 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 the GetMessage 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.

Some functions that send nonqueued messages are BroadcastSystemMessage, BroadcastSystemMessageEx, SendMessage, SendMessageTimeout, and SendNotifyMessage.

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

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.

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 the WM_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, DispatchMessage does 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 the DefWindowProc 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, the GetMessage function does not return. This effectively "hangs" the application.

Posting and Sending Messages

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 the SendMessage, BroadcastSystemMessage, SendMessageCallback, SendMessageTimeout, SendNotifyMessage, or SendDlgItemMessage function.

Posting Messages

An application typically posts a message to notify a specific window to perform a task. PostMessage creates an MSG structure for the message and copies the message to the message queue. The application's message loop eventually retrieves the message and dispatches it to the appropriate window procedure.

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 hwnd parameter.

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. The SendMessage 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 the SendAsyncProc 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 a WM_TIMECHANGE message. An application can send a message to all top-level windows by calling SendMessage 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 the lpdwRecipients parameter.

By using the InSendMessage or InSendMessageEx function, a window procedure can determine whether it is processing a message sent by another thread. This capability is useful when message processing depends on the origin of the message.

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.

To avoid potential deadlocks in your application, consider using the SendNotifyMessage or SendMessageTimeout functions. Otherwise, a window procedure can determine whether a message it has received was sent by another thread by calling the InSendMessage or InSendMessageEx function. Before calling any of the functions in the preceding list while processing a message, the window procedure should first call InSendMessage or InSendMessageEx. If this function returns TRUE, the window procedure must call the ReplyMessage function before any function that causes the thread to yield control.

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.