How do I? - Part 11
by Eric Slaats
Hi, I'm back! I had a great time on vacation and my kids are satisfied with the amount of undivided attention they had in the last month. I also I had time to think some stuff over. One of the things that crossed my mind when lying on the beach was the direction of this column. Up until now I did one or two 'theoretical' articles on Presentation Manager and a lot of practical ones. The practical ones sometimes contained some theory on how things work. I guess it's always good to mix the theoretical stuff with the practical, so this approach will be continued. However, I decided to throw in a more theoretical article every now and then to add some deeper knowledge about PM. Making things work is important, but understanding things is an absolute must.
Last month a simple but essential typo slipped through. The text, "don't use WinDefDlgProc in dialogs," in the 5th paragraph of last month's article should have read, "don't use WinDefWindowProc." In dialogs you should always use WinDefDlgProc. (Thanks for the alertness Guillermo.)
Well, what are we going to do this month? There are lots of areas in PM that are used almost without thinking. The whole message model is an example of this (see the first article in this column). Other examples are threading, posting/sending and the WPS. Before I continue on the dialog stuff I started some months ago, we'll take a look once more at messaging.
A stream of messages
As discussed in the article about the event paradigm (Oct/96), all events (like a mouse click, a mouse move, a menu action, a keystroke, window resizing, etc.) are handled by a messaging mechanism. All these actions will generate a message that will be entered in the system queue. Every message contains a window handle so that the system knows to which window the message has to be forwarded. We have seen that every window has a so-called window procedure which handles the messages that are directed to that window. The bottom line of this thought is that if we want something from a window, we just send it a message and the window procedure of that window will handle the message and take action on it.
As I've said, every window has a window procedure. For our own applications we'll write the window procedure of the client window ourselves. Controls like buttons, MLE's etc. have their own automatic window procedures. So if we want some action from a certain control, we can ask for it by sending a message to that control.
The sheer amount of messages that the PM can handle can be seen in the OS/2 documentation where this info is divided among every standard control. The fact that we can send messages to a control means not only that events can generate messages, but also that programs will generate a generous load of messages. So we're looking at quite a swollen stream of messages! How can we influence things so they are handled in an optimal manner?
Sending and posting
There are two major ways to get a message delivered to a window procedure. We can "send" a message or "post" it. We usually talk about "sending" messages to a window without specifying what we actually do. Let's take a look at both methods.
When a message is "posted" to a window the WinPostMsg API call must be used. (There is another way, but it is not really relevant at the moment.) First the message is placed in the appropriate message queue (the message queue that is attached to the application that holds the window the message is posted to).
Posted messages are also referred to as "queued messages". Queued messages are used for asynchronous processing. Eh? Well, I must admit it sounds very difficult, but it's not really that hard to understand. When a message is posted, WinPostMsg returns immediately. The only action WinPostMsg takes is to place the message in the queue. It doesn't sit around waiting for the message to be handled, so the program will continue with the next statement directly after the WinPostMsg call.
The handling of the message is done by the "dispatcher" which will get a message from the queue and deliver it to the right window procedure. "Asynchronous" means that the program will not know when exactly the message is processed. The processing of the message is asynchronous to the function that posted the message.
We can also send a message to a window with the WinSendMsg API call. In this case the message will not be placed in the queue. It will advance to the head of the queue and be processed immediately. The WinSendMsg call will wait until the message is processed and return the return-value of the message call. This way of handling a message is referred to as "synchronous". The reason for this is that the calling program always knows when the message is processed. (Simply, when the call is placed it is immediately processed.)
When do we use what?
This is a simple question in one way and a tough one in another. The bottom line is that WinSendMsg will execute immediately and the WinPostMsg will be handled when the window gets around to it. There are some pitfalls here though.
Since a WinSendMsg waits around for the window procedure to complete, it will block your program from doing anything else while waiting for the message to be processed. If this is a very long task, it's not advisable to use WinSendMsg. Your application won't respond to user events as long as it is processing the message. (Note: Using WinSendMsg to one of the OS/2 controls is usually a safe method. The controls will handle the messages quickly.)
Messaging can also be used to get information from a control. Most of the messages will return a value or will assign something to a pointer carried in one of the message parameters. Especially when using pointers in message parameters it's advisable to use WinSendMsg. This way you can be fairly sure the pointer points to something valid when the message is handled.
WinPostMsg should be used when you don't care about the reply or when exactly the message is handled. (You can be fairly sure it won't take very long to handle the queued messages. This is what makes sure the PM stays snappy.)
In general, use WinPostMsg because it's a much safer procedure. However, reading the above, we'll find that in most cases WinSendMsg is used.
This Month's Sample and 'That Multithreaded Thing'
It's hard to dream up a sample that displays the behaviour of posting and sending in a proper way since I like to keep this column simple and we haven't talked about multithreading yet. Posting messages really has a big impact. I'll discuss this in a moment, but first the sample. In this month's sample (ZIP, 14k), I created a simple window with a menu. The menu consists of two menu items: one will "post" a user message, the other one will "send" it. The message that is sent is a so-called "user message". (Yes, we're able to define our own set of messages that we can use for whatever we want.) When we define a user message we use a base number (remember, a message is only a long number) that the PM has kept free for user messages. This number is defined as WM_ USER. So we define our user message like this:
#define UM_BEEP WM_USER + 1 // User message
To guess what this message will do in this month's program shouldn't be too hard. Indeed, it triggers a function that will produce a series of beeps.
To make clear what really happens, the code initiated by the Send or Post menu actions also draws some text in the titlebar of the application. Both execute the following lines after the message is posted or sent:
WinSetWindowText(hwndFrame, "This"); DosSleep(500); WinSetWindowText(hwndFrame, "This is"); DosSleep(500); WinSetWindowText(hwndFrame, "This is a"); DosSleep(500); WinSetWindowText(hwndFrame, "This is a test");
If you try this, you'll find that the post action will continue with the drawing of the titlebar. After this is done, the dispatcher gets its chance to execute the user message. So a series of beeps will be audible after the titlebar draw. When Send is used, exactly the opposite happens. The beeps come before the draws. This illustrates that the program is waiting for the user message to return.
You may also notice some other behaviour. In both cases you can't access the menu after Post or Send is activated. In fact, you can't access anything in the system when the code is executing. This can be effectively tackled by using multithreading. Every good OS/2 program is multithreaded!
Consider the following: if a program has a second thread for executing stuff like the beeps and the draws, the main thread can continue to process other messages, meaning that the interface can keep reacting to user events. Thinking about this in light of this month's sample, multithreading is a must. So in future columns I will look at simple ways to multithread your programs.
That's it for this month. I hope the extra theory I included this month didn't make it too stuffy. Next month we'll continue on our exploration concerning dialogs and somewhere this year we'll be going multithreaded. Take care.