Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
88 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

Contents
 Introduction
 WinMain()
 Hungarian Notation
 Messages and
 Classes

 Creating Windows
 Handling Messages
 Program Flow

 Printable version
 Discuss this article
 in the forums



The Series
 Beginning Windows
 Programming

 Using Resources
 in Win32 Programs

 Tracking Your
 Window/Using GDI

 Introduction
 to DirectX

 Palettes and Pixels
 in DirectDraw

 Bitmapped Graphics
 in DirectDraw

 Developing the
 Game Structure

 Basic Tile Engines
 Adding Characters
 Tips and Tricks

Handling Messages

I've already explained some of the things messages are used for in Windows. Now I'll go over how to make use of them. The prototype for a message handling function looks like this:

LRESULT CALLBACK MsgHandler( HWND hwnd, // window handle UINT msg, // the message identifier WPARAM wparam, // message parameters LPARAM lparam // more message parameters };

The LRESULT type of the return value is used specfically for message processing functions like the one we're going to write. I talked about the CALLBACK convention a little bit earlier. The parameters are very simple:

HWND hwnd: This is the handle of the window that sent the message currently being processed.

UINT msg: This is a message identifier. The values for this parameter are constants beginning with WM_ (for "Windows message"). The number of different messages that can be sent is ridiculously high, but here are some important ones:

WM_ACTIVATE A new window is receiving the focus.
WM_CLOSE A window is being closed.
WM_COMMAND A menu option has been selected.
WM_CREATE A window has been created.
WM_LBUTTONDBLCLK Left mouse button has been double-clicked.
WM_LBUTTONDOWN Left mouse button has been pressed.
WM_MOUSEMOVE The mouse has been moved.
WM_MOVE A window has been moved.
WM_PAINT Part of a window needs to be repainted.
WM_RBUTTONDBLCLK Right mouse button has been double-clicked.
WM_RBUTTONDOWN Right mouse button has been pressed.
WM_SIZE A window has been resized.
WM_USER Use this for whatever you want.

WPARAM wparam, LPARAM lparam: The exact use of these parameters depends on which message is being sent, but they are used to further specify the meaning of the message.

If you had to write code to handle every message that your window might receive, you'd probably go insane. I know I would! Thankfully, Windows provides a default message handler. If you don't have any special instructions for handling certain messages, you can always call DefWindowProc(). With that in mind, here is the simplest, fully functional message handler that you could possibly write:

LRESULT CALLBACK MsgHandler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { return(DefWindowProc(hwnd, msg, wparam, lparam)); }

Simple, hey? Usually you'll want to handle some of these messages yourself. In that case, you can write your own code, and return 0 to tell the program that you've dealt with the message. Here's an example of a message handler that calls an initialization function when the window is created, and calls the default handler for anything else.

LRESULT CALLBACK MsgHandler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { if (msg == WM_CREATE) { Initialize_Game(); return(0); } return(DefWindowProc(hwnd, msg, wparam, lparam)); }

Your message handler will probably end up being a big switch statement to accommodate the messages you want to handle manually, followed by a call to the default handler for everything else. Now there's just one more thing I need to show you before everything is working smoothly, and that's how to make sure your message handler is getting called when it has work to do.

Reading the Message Queue

Near the beginning of your program's main loop, you need to see if the message queue -- where all pending messages are stored -- has anything waiting for you. If so, there are a few things you need to do in order for your handler to do its job correctly. The function you need here is PeekMessage(). Here is its prototype:

BOOL PeekMessage( LPMSG lpMsg, // pointer to structure for message HWND hWnd, // handle to window UINT wMsgFilterMin, // first message UINT wMsgFilterMax, // last message UINT wRemoveMsg // removal flags );

The return type, BOOL, is really just an int, but it takes only two values: TRUE or FALSE. If a message is waiting on the queue, the function returns TRUE. Otherwise, it returns FALSE. The parameters are pretty straightforward:

LPMSG lpMsg: This is a pointer to a variable of type MSG. If a message is waiting, this variable will be filled with the message information.

HWND hWnd: The handle of the window whose queue you want to check.

UINT wMsgFilterMin, wMsgFilterMax: The indices of the first and last messages in the queue to check. Most of the time, you'll only be interested in the first message on the queue, so you would set both of these parameters to 0.

UINT wRemoveMsg: Generally this takes only two values, PM_REMOVE or PM_NOREMOVE. Use the former if you want to remove the message from the queue after reading it, and the latter if you want to leave the message on the queue. Usually, if a message is waiting, you'll prepare it to be handled right away, in which case you should use PM_REMOVE.

If a message is waiting, you need to do a few things to get your handler to kick in. Don't worry, it's only two simple calls: one to TranslateMessage() and one to DispatchMessage(). Their prototypes are very similar:

BOOL TranslateMessage(CONST MSG *lpmsg); LONG DispatchMessage(CONST MSG *lpmsg);

The first call performs a bit of translation on the message, as you may have guessed, and the second call invokes your message handler and sends it the appropriate information from the MSG structure. That's all you need to know! With every iteration of your main loop, if a message is waiting, you call these two functions and your MsgHandler() function takes care of the rest. Here's a code example:

if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); }

No problem! Now you can write a Windows program that creates and registers a window class, and creates a window with a valid message handler. That wasn't so bad, was it? There are just a few more things I'd like to mention before I wrap this article up. While we're on the topic of messages, there will come a time when you want to send messages manually. Here's how.

Sending Messages

There are actually two ways to go about doing this. You can either call PostMessage() or SendMessage(). Their prototypes are very similar:

BOOL PostMessage( HWND hWnd, // handle of destination window UINT Msg, // message to post WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ); LRESULT SendMessage( HWND hWnd, // handle of destination window UINT Msg, // message to post WPARAM wParam, // first message parameter LPARAM lParam // second message parameter );

The parameters are the same as those taken by the MsgHandler() function we wrote, so I won't go over them again. The only thing you need to know is the difference between the two functions, so I'll go over each one briefly.

PostMessage() is used when you simply want to add a message to the queue and let your program logic take care of it. The function returns a nonzero value (TRUE) if it succeeds, or zero (FALSE) if it fails. It simply adds the message you specify to the queue, and returns immediately. In most cases, a call to PostMessage() will get the job done.

SendMessage() is a bit different. Notice that it returns an LRESULT, which is used for message processing functions. That's because SendMessage() doesn't post a message to the queue -- it translates the message and invokes the message handler immediately, and doesn't return until the message handler has finished processing the message. SendMessage() is used rather than PostMessage() for events of higher priority that need to occur quickly. Use it when you want something done immediately.

Now that you know that, the topic of messages leads into the last topic I need to mention for now, and that is a major difference between Windows programming and DOS programming.




Next : Program Flow