Sending Messages Yourself
The last subject I want to talk about is sending messages yourself. There are two ways to do this:
SendMessage()—
Sends a message to the window immediately for processing. The function returns after the WinProc() if the receiving window has processed the message.
PostMessage()—
Sends a message to the window's message queue and returns immediately. Use this if you don't care if there's a delay until your message is processed, or your message is a low priority.
The prototypes for both functions are similar, as shown here:
LRESULT SendMessage(HWND hWnd, // handle of destination window
UINT Msg, // message to send
WPARAM wParam, // first message parameter
LPARAM lParam); // second message parameter
The return value of SendMessage() is the value returned by the WinProc() of the window you sent it to.
BOOL PostMessage(HWND hWnd, // handle of destination window
UINT Msg, // message to post
WPARAM wParam, // first message parameter
LPARAM lParam ); // second message parameter
If PostMessage() is successful, it returns a nonzero value. Notice that this is different than SendMessage(). Why? Because SendMessage() actually calls the WinProc(), whereas PostMessage() simply places a message in the message queue of the receiving window without any processing.
You might be wondering why you would ever want to send a message yourself. There are millions of reasons—literally. This is something that the designers of Windows want you to do, and it's how you make things happen in a windowed environment. For example, in the next chapter, when I talk about window controls like buttons, sending messages is the only way to talk to a control window! But if you're like me, you like something a little more concrete.
In all of the demos thus far, you've terminated them by double-clicking the close box or pressing Alt+F4. Wouldn't be nice if you could programmatically kill the window?
You know that either a WM_CLOSE or WM_DESTROY will do the job. If you use WM_CLOSE, it gives your application a little warning, whereas WM_DESTROY is a little tighter. But either way you go, you just do something like this:
SendMessage(hwnd, WM_DESTROY,0,0);
Or if you want a little delay and don't mind if your message is queued, use PostMessage():
PostMessage(hwnd, WM_DESTROY,0,0);
In both cases, the application will terminate—unless there is steering logic in the WM_DESTROY handler, of course. But the next question is when to launch the message. Well, that's up to you. In a game, you might track the Esc key and exit on that. Here's how you would do that using the KEYDOWN() macro in the main event loop:
if (KEYDOWN(VK_ESCAPE))
SendMessage(hwnd,WM_CLOSE,0,0);
For an example of the preceding code in action, take a look at DEMO3_14.CPP and the executable DEMO3_14.EXE on the CD-ROM. The program implements the logic in the preceding code exactly. As an experiment, try changing the message to WM_DESTROY and using PostMessage(), too.
WARNING
Sending messages out of the main event loop can cause unforeseen problems. For example, in the preceding case, you're killing the window out of the main event loop by sending a message directly to the WinProc() with SendMessage(). However, if you normally assume that the event handling is done in the main event loop, you might create an out-of-execution-order bug. This means that you assume that event B happens after event A, but in some cases event B happens before event A. Whammo! This is a typical problem when you're sending messages, so make sure to think it out. PostMessage() is usually safer because it doesn't leapfrog the event queue.
Finally, there is also a way to send your own custom messages called WM_USER. Simply send a message with SendMessage() or PostMessage(), using WM_USER as the message type. You can put whatever you want in the wparam and lparam values. For example, you might want to use the WM_USER message to create a number of virtual messages for a memory management system that you have. Take a look:
// defines for memory manager
#define ALLOC_MEM 0
#define DEALLOC_MEM 1
// send WM_USER message, use the lparam as amount of memory
// and the wparam as the type of operation
SendMessage(hwnd, WM_USER, ALLOC_MEM, 1000);
Then, in your WinProc(), you might have
case WM_USER:
{
// what is the virtual message
switch(wparam)
{
case ALLOC_MEM: {} break;
case DEALLOC_MEM: {} break;
// .. more messages
} // end switch
} break;
As you can see, you can encode whatever you want in the wparam and lparam and do something as stupid as I just did for this example, or something that is more interesting!
|