About the Windows Message Queue






3.67/5 (5 votes)
Apr 18, 2002
3 min read

173001

1654
This article is written for the Win32 API programmers on the subject of Windows message queues.
Introduction
Everybody knows MFC as the most powerful instrument for developing applications on the Windows system. Its classes are specialized on the entire family of difficult tasks you can imagine and the flexibility of the chosen solutions is remarkable. It's the best approach on developing large software and keeps the programmer away from several kind of detail problems. This should be the greatest advantage when having to deal with a window-based system but...
The Thread
We're not talking about a window-based system here. "The atomic working entity of a program is the thread", says the MSDN book. Among many background details like stack and paths of execution, it also has to cover the implementation of a message queue. It's not a rule but you will find it on every thread that sustains a window. This is the reason for a very popular confusion: the window has a message queue. Wrong! The message queue belongs to the thread and the messages posted to the window are dispatched to it by the message loop in the thread. I will illustrate this by the program attached to this article.
The Program
Is a console based application; the modeless dialog manages its messages through the console's message queue, so it's not about having two queues at a time. The code offers you a class and the main function.
CConsoleQueue
This class implements the message loop and a primitive system of handling messages. It has the following methods:
RunQueue
- runs the classical message loop
while (GetMessage(&msg, NULL, 0, 0)) // extract the message // from the queue { if (fctMan=(*this)[msg.message]) fctMan((void *)msg.wParam); else { TranslateMessage(&msg); // necessary to listen the keyboard DispatchMessage(&msg); // message goes to the default // procedure } }
EndQueue
- terminates the message loop by posting WM_QUIT
PostMessage(NULL, WM_QUIT, 0, 0); // message to interrupt // the loop by returning // FALSE on GetMessage
RegisterMessageHandler
- registers a handler function to a particular
message. When the message is received in the message loop, the handler is called.
To pass a value for the handle, you should put it through the WPARAM
when calling
PostMessage
.
Note: this could be considered as a skeleton of the reactor concept proposed by The Adaptive Communication Environment (ACE).
RemoveAll
- removes all the associations (from the reactor)
main
The purposes of this function are - step by step - the following:
1. Creating a modeless dialog box used to send messages to the queue:
hWndDialog=CreateDialog(NULL, MAKEINTRESOURCE(IDD_DIALOG_TEST), NULL, DialogProcedure); ShowWindow(hWndDialog, SW_SHOW);
2. Setting a timer on the thread:
nTimer=SetTimer(NULL, 0, nDelay*1000, TimerProcedure);
3. Registering a handler for an user-defined message:
queue.RegisterMessageHandler(WM_USER_DEFINED, UserDefined);
4. Running the queue:
queue.RunQueue();
Behavior
The purpose is to dropWM_TIMER
messages in the message queue; each
WM_TIMER
will
print to console a global buffer. If the buffer doesn't modify for the last
nKeepQueueSteps WM_TIMER
s, the last one will kill the timer and will end the
message queue. Modifying the buffer is done in the modeless dialog box by writing
in the edit box and pressing "send buffer". Pressing "send user-defined" will post
the WM_USER_DEFINED
message to the queue.
Final
Observe that calling PostMessage
with NULL
for the first
parameter will drop the message in the current thread's message queue.
Another observation is about setting the timer. This one lives only among the
queue's events and is not a window's resource. It belongs to the thread and could
reach a window only if is set to and only by a call of DispatchMessage
.
The last word is about the message loop. This expression
is a source of limitation for the programmer. In fact you could have more
message loops in a single thread's execution. To test this, simply multiply the
actual content of main
in the function's body like this:
hWndDialog=CreateDialog(NULL, MAKEINTRESOURCE(IDD_DIALOG_TEST), NULL, DialogProcedure); // ... cout<<endl<<"program ends here..."<<endl; hWndDialog=CreateDialog(NULL, MAKEINTRESOURCE(IDD_DIALOG_TEST), NULL, DialogProcedure); // ... cout<<endl<<"program ends here..."<<endl; // ... hWndDialog=CreateDialog(NULL, MAKEINTRESOURCE(IDD_DIALOG_TEST), NULL, DialogProcedure); // ... cout<<endl<<"program ends here..."<<endl;