[ Next ] [ Previous ] | Chapter 11 | [ Contents ] [ Chapter 10:Window Management ] [ Chapter 12: Resources ]

### 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 );`

[ Next ] [ Previous ] | Chapter 11 | [ Contents ] [ Chapter 10:Window Management ] [ Chapter 12: Resources ]