Message queues

Each thread in a Windows process may have a message queue. Each message queue is associated with a single thread, and each thread can only have a maximum of one message queue.

When a thread is created, it is initially a "worker" thread, i.e. it does not have a message queue. When the thread first calls a messaging or windowing API function, a message queue is automatically created for it. A "user-interface" thread typically creates its message queue by calling PeekMessage or GetMessage.

Windows messages

All Windows messages (posted into message queues) are of a fixed size and structure, specified by the MSG structure:

HWND  Handle of the window to whom the message applies, or NULL if the message isn't specific to a particular window.
UINT  32-bit code number identifying the message's purpose.
WPARAM  32-bit message specific parameter.
LPARAM  32-bit message specific parameter.
DWORD  Time when the message was posted.
POINT  Position of the mouse pointer when the message was posted.

Messages are posted into a queue using:

Message handling

A "user-interface" thread typically contains a message loop (sometimes called a message pump).

The message loop reads the first message from the message queue, for example by calling GetMessage (which blocks until a message is available). A common alternative is to call PeekMessage, which is a non-blocking version; this is often used to allow idle-time processing to be performed when the message queue is empty.

The loop performs any message specific processing. Typically, it may call TranslateMessage and TranslateAccelerator.

If the loop didn't handle the message (which is usually the case), it passes it back to Windows by calling DispatchMessage. This Windows function then calls the window procedure for the window identified by the HWND argument of the message.

Window procedures

Every window has an associated window procedure for handling messages directed to that window.

LRESULT WindowProc(HWND, UINT, WPARAM, LPARAM)

By default, the address of the window procedure is specified by the window class of that window, so all windows of one window class typically share a single window procedure. However, it is possible to change the window procedure of an existing window - a process called sub-classing the window - and hence change how the window responds to messages.

Any message that the window procedure does not handle must be passed back to Windows, by calling DefWindowProc (the default window procedure). Many window messages are only handled by Windows itself, but they are always passed through the window procedure to give it the opportunity of overriding the default Windows behvaiour.

A message can be passed directly to the window procedure of a specific window using:

LRESULT SendMessage(HWND, UINT, WPARAM, LPARAM)
This call completely bypasses the message queue, and directly calls the window procedure. It does not return until the called window procedure has returned. It returns the 32-bit result code (LRESULT) from the window procedure.