ID Number: Q74476
1.x 2.x 3.00
WINDOWS
Summary:
The following is excerpted from an article in the April 1991 issue of
Software Design (Japan).
More Information:
Asynchronous message-passing means that Windows will send an
application messages to act on and that these messages may come in any
order. At present, all messages are sent to specific windows. Every
window has a function that Windows calls to send that window a
message. This function processes the message and returns to Windows.
When the function returns to Windows, Windows may then send messages
to other windows in the same program, other programs, or to the same
window again.
Because most of these messages are generated by user actions (picking
an item in a menu, moving a window, and so forth), the specific
messages a window receives will differ each time the program is run.
This is what makes the messages asynchronous.
This message passing is what makes Windows programming difficult for
many programmers. The programmer is no longer writing a program in
which he or she controls the flow from beginning to end. Rather, a
Windows program is written as a large number of objects, each one
designed to handle a specific message from Windows.
Understanding message passing is critical and because it leads to so
much confusion, the concept will be explained in greater detail in
this article. If message passing is understood, the remainder of
Windows can be learned fairly easily. However, it is very unlikely
that a Windows program or other graphical user interface (GUI) program
can be successfully developed without a thorough understanding of
message passing.
In a message-passing system, the focus changes from being proactive
(the programmer controls the program flow) to being reactive (Windows
controls the program flow). [Or as it has been put by some Macintosh
programmers, "Don't call us, we'll call you."] For example, consider
the situation where a user chooses an action from a menu in a program.
In a proactive program, the program reads the keyboard, determines
that the key(s) pressed are meant to run the action, and calls the
function that performs that action. In a reactive system, the program
is sent a message indicating that the user chose that item from a
menu. When the program receives the message, it calls the function
that performs the action. When this function is done, control returns
to the system. Although the reactive approach is substantially
different from the proactive approach, it is also simpler.
In Windows, every window (including dialog boxes) has a "response
function" registered to it. When Windows sends a message to a window,
it calls the response function for that window and passes it the
message. All messages from Windows are passed to window response
functions; there is no other way for Windows to send a message to a
program. Therefore, all messages are for a specific window or group of
windows.
However, there are four considerations involved with this method:
1. Messages are sent in two distinct ways. The first method consists
of messages that are posted to a first-in, first-out queue
(PostMessage). The second method consists of messages that are sent
(SendMessage). Posted messages, aside from PAINT messages, are
serialized, meaning that messages cannot be posted anywhere except
to the end of the queue, and the application is sent messages only
from the beginning of the queue. Posted PAINT messages are an
exception. They are added together and sent only when there is
nothing else in the queue. This is done to reduce the number of
times a window has to paint itself.
Messages that are sent are passed to the application immediately,
and the send function does not return until the message is
processed. However, when a message is posted, an indeterminate
number of messages and amount of time will pass before the message
is actually sent to a window and acted on. Also, when a message is
sent, an message posted earlier may not yet have been acted on.
2. Sending messages or calling functions (which may, as part of their
actions, also send messages), can lead to additional messages being
generated. The most dangerous situation is where the action for a
message generates the same message again. If, while processing a
message, an application sends the same message to itself, the
application will run out of stack space quite quickly. If an
application POSTS the same message to itself, the application will
not run out of stack but it will generate an unending stream of
messages.
3. If, while processing a message, an application calls a function
that sends a message, the application will process the second
message in the middle of processing the first message. Therefore,
each window response function MUST be fully re-entrant. It is even
possible for a function to be re-entered to process the same
message as the message currently being processed. For this reason,
using global or static variables in a response function is very
dangerous.
Also, if the application uses properties, scratch files and/or
other data storage mechanisms, extreme caution is required.
Consider the situation where one message reads in data from a file,
then a second message reads in the same data, makes changes to that
data, and writes it back to the file. If the first message makes
additional changes to the data and writes its new data back to the
file, the changes caused by the second message are completely lost.
With files, each application must implement its own sharing
mechanism. For properties, allocated memory and other memory
storage, there is a simple solution: lock the item and use the
pointer returned. Because only one lock is allowed, this prevents
contention. Never copy the data into a scratch buffer to copy back
later.
4. Because messages come in due to user interaction, the application
cannot be written to assume that when a particular message is
received that another message has already been processed and
performed its functions. While, for a given action, a specific
sequence of messages may occur, in the interest of remaining
completely compatible with potential changes in future versions of
Windows, it is recommended that no message ordering dependencies be
introduced unless absolutely necessary. The best example of this is
that the first message a window gets when it is being created is
not WM_CREATE, rather it is WM_GETMINMAXINFO. When one message must
logically follow a second (such as WM_CREATE always preceding
WM_DESTROY), then it is fine to depend on the specific ordering of
those two messages.
The asynchronous, reactive nature of Windows programming can cause
confusion. Because the program has no control over the order that
messages arrive, the response to ANY specific message CANNOT depend on
other messages having been processed or NOT been processed.
To confuse matters even further, an application may be in the middle
of processing one message when it calls a Windows function that sends
the application another message. When processing this second message,
some dependent processing may be only half finished. If an application
will check and only do some processing if another message has not
already performed it, the application must be prepared for the case
where another message has begun the processing, but has not completed
it.
Do not be confused into thinking that a Windows task is preemptively
multitasked; it is not. As a matter of fact, Windows is non-
preemptive between all of its Windows tasks. Therefore, an application
generally does not need to be designed to process a user-originated
message in the middle of processing another message. However, when an
application calls a Windows function, the application may then get a
set of specific messages sent to it by Windows before the called
function returns.
Further, when an application sets up a modal dialog box, the
DialogBox() function will not return until after the dialog box is
dismissed and processing completed. Therefore, after calling the
dialog box function, all combinations of user-generated messages may
be received before the function returns.