INF: Some Basic Concepts of a Message-Passing Architecture

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.