Some Basic Concepts of a Message-Passing Architecture

Last reviewed: November 2, 1995
Article ID: Q74476
The information in this article applies to:
  • Microsoft Windows Software Development Kit (SDK) versions 3.0 and 3.1
  • Microsoft Win32 Application Programming Interface (API) included with:

        - Microsoft Windows NT versions 3.5 and 3.51
        - Microsoft Windows 95 version 4.0
    

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.

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.

NOTE: 16-bit Windows is non-preemptively multitasks 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.

32-bit Windows 95 and Windows NT are preemptive multitasking systems. Each thread has its own input queue. It is a good idea to create a separate thread of execution for the user interface so that it is responsive to user input.


Additional reference words: 3.00 3.10 3.50 4.00 95
KBCategory: kbui
KBSubcategory: UsrMsg


THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Last reviewed: November 2, 1995
© 1998 Microsoft Corporation. All rights reserved. Terms of Use.