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))
{
if (fctMan=(*this)[msg.message])
fctMan((void *)msg.wParam);
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
EndQueue
- terminates the message loop by posting WM_QUIT
PostMessage(NULL, WM_QUIT, 0, 0);
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 drop
WM_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;