Window Messages and Queues
Window Messages and Queues
Presentation Manager windows communicate using a queue message
processing
system. All windows in a Presentation Manager thread share a single
message
queue for processing messages: however, all message queues are
descendants
of the Presentation Manager system message queue. This is the reason
that
one poorly designed Presentation Manager application can freeze up the
entire system The queuing mechanism is a very important concept to
understand.
Once a window has a message queue. it can communicate with any other
window in the entire system. All it needs is the window, or message
queue,
handle to send the message to.
A window can send or receive messages. Each message is used to signal
some sort of event. Each time a mouse is moved, a window is resized, or
a menu item is selected, messages are sent to a window. A window
procedure
operates like a massive sieve, filtering the messages of interest and
passing
through those messages that are unimportant. It is important to realize
that all messages must be processed and replied to, either through your
own window procedure or by passing the message to WinDefWindowProc
or WinDefDlgProc. This facility of using events to control
the programming flow is known as 'event-driven programming.'
This
style is common not only to Presentation Manager programming, but to
other
GUI programming environments as well.
Message Ordering
It is not a good idea to count on messages arriving in your message
queue
in a certain order; the purpose of event-driven programming is to be
flexible
and dynamic and respond only when asked; however, there are obviously
times
when it is important to understand the flow of messages the system
sends
to your queue.
The first message you can count on being sent to your client window
is the WM_CREATE message. At the time this message arrives, the window
handle exists, but has no size and is not visible. The WM_CREATE
message
can be used to do some application-specific initialization, for
instance,
allocating memory for window words; however, any queries specific to
size
or focus should be done after creation. One way to accomplish this is
by
posting a user- defined message to the client window in the WM_CREATE
processing.
The size and focus messages the system places in the queue are sent
messages,
and will be processed before a posted message. When you process the
user-defined
message, you will have a client area that has both size and focus, and
this information can be used in any initialization that needs to be
done.
The standard way to set size or focus is by using the respective API's
directly after WinCreateStdWindow or WinCreateWindow
call.
If you would like to change the size or position of window, there are
two
ways to do this. First, create the client window as not visible, and
use
the function WinSetWindowPos to size and show the window,
The second
method is to intercept the WM_ADJUSTWINDOWPOS message. This message is
sent before a window has been sized or moved. This gives the
application
a chance to override the new size and position with a size and position
of its own choosing. If modifications are made, the application
should
return TRUE instead of FALSE, and the new coordinates are used.
Focus Messages
When a window is gaining or losing focus, there are several messages
that
are sent by the system. It is not advisable to process any of these
messages
yourself, but it is useful to understand how Presentation Manager
handles
changing a windows focus.
When a user clicks the mouse on another window, the system first sends
a set of messages to the frame window that is losing the focus. A set
of
WM_QUERYFOCUSCHAIN messages are sent to the frame window and its
children
to help the system decide which windows will be involved in this focus
change operation. Next, a WM_FOCUSCHANGE message is sent to both the
frame
and its children to indicate they are all losing focus. The next
message
sent is the WM_SETFOCUS message. This message indicates the
window
is either about to lose or about to gain the input focus. In this case,
it would be losing input focus. Next, the WM_SETSELECTION message is
sent
This message is used to unhighlight or highlight any selected items in
the window, The client area does not do much with this message,
but
it is at this time that the titlebar window changes from a
highlighted
titlebar to unhighlighted titlebar. The last message sent when a
window is losing focus is the WM_ACTIVATE message. The message actually
takes away the focus from the active window.
When a window is gaining focus, the messages are sent in a similar
fashion.
First, the system queries the windows with the
WM_QUERYFOCUSCHAIN.
Then, a WM_FOCUSCHANGE message is sent to the frame and its children to
indicate they are gaining focus. Next, the focus change operations are
actually performed, with a WM_SETFOCUS being sent first, then the
WM_SETSELECTION,
and lastly, the WM_ACTIVATE.
Size and Paint Messages
An application receives three messages when a window is
sized,WM_CALCVALIDRECTS,
WM_SIZE and then WM_PAINT. The message WM_CALCVALIDRECTS is used to
communicate
the new window size and coordinates after the sizing operation. The
WM_CALCVALIDRECTS
is used only when CS_SIZEREDRAW style is not specified, as the whole
window
will be invalidated when a sizing operation is done on a window with
this
style.
The next message is the VM_SIZE message. This message gives the
application
a chance to reposition any other window that may be dependent on the
newly
sized windows position. The last message passed, if she style
CS_SIZEREDRAW
is set, is the WM_PAINT. If the WS_SYNCPAINT style is set, the message
will be sent, otherwise the message will be posted The system will pass
the rectangular coordinates that contain the area to be redrawn as a
parameter
in the WM_PAINT message.
The Last Messages a Window Receives
When a WM_CLOSE message is posted to a window (when the user selects
CLOSE
from the system menu), first a WM_SYSCOMMAND message is posted
with
the SC_CLOSE ID. Next, a WM_QUIT message is posted to the message
queue.
This is a very special message, because when WinGetMsg receives this
message,
the function returns FALSE, causing the WinGetMsg / WinDispatchMsg
loop
to terminate. A WM_SAVEAPPLICATION message is posted next. This gives
the
application a chance to prompt the user for any last minute clean-up
work;
for instance, saving a file, or disconnecting a communication
line.
When WinDestroyWindow is used to destroy the frame
window, the system will send the focus change messages to indicate this
frame window and all its children will be losing focus. The last
message
a window will receive is the WM_DESTROY. This is the place to control
any
application-specific cleanup. For example, freeing memory should be
done
in the WM_DESTROY processing.
When the user has selected "Shutdown" from the desktop menu or the
Warp
Launchpad, there is a little change in the messages that arrive in the
queue. The system bypasses the WM_CLOSE message, and sends two messages
to each thread that contains a message queue. The first is the
WM_SAVEAPPLICATION.
The next message issued is the WM_QUIT message. An application
will
usually not process the WM_QUIT message; however, in the case
when
it needs to interrupt or halt system shutdown, it must process WM_QUIT.
If the application wants to cancel the shutdown, it can call WinCancelShutdown.
If the application would like to do something else before shutting
down,
it can perform its closing work, and then call WinDestroyMsgQueue.
After processing, make sure you return FALSE implicitly, and do no
call
WinDefWindowProc
as the default window procedure does not know how to handle a WM_QUIT
message.
|
Gotcha!
For each thread that contains a message queue, make absolutely
sure
that you issue a WinCancelShutdown soon after the thread
is created if you do not want to process the WM_QUIT, or else be
prepared
to process the WM_QUIT message and destroy the message queue. A
thread
with a never-ending message queue can prevent the entire system from
shutting
down properly. Also, there is no guarantee that a secondary thread will
execute all function calls and return before the primary thread (and
thus
the application) exits. It is up to the developer to make sure all
clean-up
in secondary threads is complete before the application exits.
|
Sending Messages
When a message is sent, it is usually directed to a particular window.
For instance, a WM_CHAR message, indicating a key had been pressed,
would
be sent to the window that was currently active and had the keyboard
focus.
There are two ways a message can be dispatched. They can either be
sent,
using WinSendMsg, or posted, using WinPostMsg.
There is a very subtle difference between these two dispatch methods,
and
this could cause you problems somewhere down the road. When a message
is
sent, it is not put in a window's message queue; it is processed
the next time WinGetMsg is called, or immediately executed,
if no message is currently being processed. The thread containing WinSendMsg
blocks, and control is switched over to the thread containing the
receiving
message's window procedure.
|
Figure 11.1 WinSendMsg in a
multithread application.
A message should be sent when it absolutely, positively,. has to be
there
right now A good example of this is passing painters in messages when
there
is no guarantee that the pointer will point to something valid when the
message is up for processing. WinSendMsg should be
used in this situation.
One little bit of information about WinSendMsg: this
function will not return until that message has been processed. Yup,
that's
right. If you send a message from your window procedure to a window
procedure
that's asleep at the wheel, or even just a little slow to respond, your
window procedure will sit there and wait until it gets some response
back
from the other window procedure. If you send a message to some window
that
the system controls the window procedure for, you can pretty much
guarantee
a zippy response; however, .be very careful when using this function to
send messages to either your own window procedure or to some other
application's
window procedure. WinPostMsg is a much safer
method
of transmitting messages; however, the message is placed into the
receiving
window's message queue. It will be processed when that window gets
around
to it. WinPostMsg should be used when you want to communicate
some information and do not care about a reply. WinSendMsg should
be used when it is imperative that you gain some piece of information
and
have to respond to it now.
Broadcasting Messages
A window can communicate one to one with another window directly, or it
can broadcast a message to several windows at once. The function
BOOL APIENTRY WinBroadcastMsg(HWND
hwnd,
ULONG msg,
MPARAM mp1,
MPARAM mp2,
ULONG ulCmd);
can be used to send or post a message to the windows specified in the ulCmd
parameter.
This command contains two parts: who to communicate with, and what form
of communication to use. These flags are then ORed together The default
communication form is BMSG_POST. You can specify BMSG_POST, BMSG_SEND,
or BMSG_POSTQUEUE. The POSTQUEUE flag will post a message to all
threads
in the system that have a message queue, and the hwnd parameter
will be ignored. Only one of these three flags can be specified. The
second
part of the ulCmd parameter indicates who to communicate with.
The
choices are BMSG_DESCENDANTS, or BMSG_FRAMEONLY. DESCENDANTS will
communicate
with hwnd, and all of its descendants. FRAMLEONLY will
broadcast
a message to all frame windows that are descendants of hwnd. To
broadcast
to all frames in the system use HWND_DESKTOP for hwnd.
Peeking into the Message Queue
There are many instances when you do not want to retrieve a message
from
the message queue, instead you would rather just "peek into the queue,
and see if a message it waiting. The function:
BOOL APIENTRY WinPeekMsg(HAB hab,
PQMSG pqmsg,
HWND hwndFilter,
ULONG msgFilterFirst,
ULONG msgFilterLast,
ULONG fl);
inspects the message queue and returns back information about the
queue.
hwndFilter
narrows the search to a specific window or its children. The msgFilterFirst
and msgFilterLast parameters let you narrow the search
even
further to numerical range. If both these parameters are null, all
messages
are included in the search. The fl flag indicates whether the
message
is removed from the queue, or not. The default is to not remove the
message
from the queue. The return from this function indicates whether the
search
was successful, or not.
Finding More Message Queue Information
There are several functions to query information from the message
queue.
The following are these query functions:
Function |
Description |
WinQueryMsgPos |
Returns the pointer position when last message retrieve
from
the queue was posted. This is the ptl parameter in the
QMSG
structure. |
WinQueryMsgTime |
Returns the time in milliseconds when the last message
retrieved from
the queue was posted. This is the time parameter in the QMSG
structure. |
WinQueryQueueInfo |
Returns the MQINFO structure. This structure includes the
process ID,
thread ID, and message count |
WinQueryQueueStatus |
Returns information about what types of messages are in
the queue |
Message Priorities
When messages are retrieved from the message queue, they are not
necessary
retrieved on a "first in, first out" basis. Instead, messages are
retrieved
on the basis of priority, similar to threads. The following is a list
of
messages in order they will be retrieved:
- Sent messages
- WM_SEM1
- All other posted messages
- Keyboard or mouse messages
- WM_SEM2
- WM_PAINT
- WM_SEM3
- WM_TIMER
- WM_SEM4
Figure 11.2 represents a flow chart of how messages are retrieved
|
Figure 11.2 WinGetMsg message
processing order
You may be wondering. "What are these WM_SEM messages, and the
WM_TIMER
message?" Well, on to the next topic... WM_PAINT messages are
fairly
low on the message priority totem pole. The default window style causes
Presentation Manager to "group" invalidated regions together and
generate
one WM_PAINT message. The window style, WS_SYSPAINT, or the class style
CS_SYSCPAINT, wilt stop Presentation Manager from behaving in this
independent
manner, and each time a region invalidated, Presentation Manager
will very obediently call the WM_PAINT processing immediately by
sending
the WM_PAIN'T message. The system does not post this messages, it jumps
to the WM_PAINT processing and then, when painting is completed, jumps
back to the call following the region invalidation.
Messages and Synchronization of Events
Often an application wants to know when some event has occurred. One
way
to do this is the use of the WM_SEM1,2,3,4 messages. These messages are
totally for your application use. It these messages are passed to WinDefWindowProc
or WinDefDlgProc, it has no effect on the system.
For example, suppose you have a worker thread that has finished
processing.
That thread could post a WM_SEM2 to the main thread to indicate that
the
thread has finished its work. WM_SEM1 messages should really be
reserved
for very important. time-critical events.
A way to keep track of an event that is dependent on some function
of time is to use the functions WinStartTimer and WinStopTimer.
WinStartTimer starts an alarm clock
that is set to go off after some application- defined amount of time,
in
milliseconds. When the timer goes off, the system sends WM_TIMER
message
back to your window procedure.
You might consider using semaphores in a window procedure.
DON'T!! Instead, think of using WinRequestMutexSem,WinWaitEventSem,
or WinWaitMuxWaitSem. Waiting on a semaphore using the regular
DosWait...Sem functions can bring a window procedure to
a
screeching halt. Even the most well-behaved semaphore synchronization
can
develop a mind of its own every now and then. The special set of window
semaphore functions were created to provide the same functionality as
the
DosWait...Sem functions, but not to interrupt the
flow
of your window procedure completely.. The system will appear to
wait
in the message processing that this function is called from, but
messages
sent to the message queue will be
processed
synchronously. When the semaphore has been posted or the function times
out, the message processing resumes where the WinWait...
cal was executed. Note that messages that are posted will remain in the
message queue until after WinWait... call has completed.
User-Defined Messages
Presentation Manager also gives you the flexibility to add your own
messages
to the system. These are called user-defined messages, and are
numerically
represented by the range 0x1000 through 0xbfff. There are some
system-defined
messages that fall into this range, WM_USER+40 through WM_USER+55. This
is an area that may change in the future, so its a good idea to search
through the Toolkit header files to see if there are any new messages
defined
that fall into this range. Several examples in this book use
user-defined
messages.
Some words about using window messages with
non-PM threads.
You can post messages to windows from thread that is not initialised
for
PM via WinInitialize and WinCreateMsgQueue.
You also can use in such thread some Win... functions that
don't use
queue
, say WinAlarm. But sometimes it is
necessary
to use some kind of communication with user from thread and/or process
that don't use even-driven programming model. For example, you
want
to inform user on error condition with WinMessageBox function.
To do this you can use WinPostMsg with
user-defined
message to one of your client window or may simply initialize your
thread
to PM with WinInitialize and WinCreateMsgQueue,
but don't use standard message loop:
while( WinGetMsg( hab, &qmsg, 0UL, 0UL,
0UL ) )
WinDispatchMsg( hab, &qmsg );