In this lesson we will cover a single function, PeekMessage(), and how this function
differs from its evil twin, GetMessage().
Actually, there's nothing wrong with GetMessage(), the way it works just doesn't
have spectacular results on games and their continuous activity. We'll go
over how this is and how PeekMessage() is a solution.
In the previous lesson, we built a simple Windows application while using the function
GetMessage(). We used GetMessage() and two other functions to create a loop that
handled all the Windows message sent. However, there was a catch we didn't talk
about at the time.
The following diagram shows how the event loop we wrote works:

Image 4.1 - The Structure of a GetMessage() Loop
Once we create the window, we get into the event loop, where we see the function
GetMessage(). GetMessage() then waits for a message and, upon recieving one, sends
it to the next step, TranslateMessage(). This is perfectly logical for Window programming,
because generally speaking Windows applications, Word for example, tend to sit and
do nothing until you make a move.
However, this doesn't work well for us. While all this waiting is going on, we need
to be creating thirty to sixty fully-rendered 3D images per second and putting them
on the screen without any delay at all. And so we are presented with a rather interesting
problem, because Windows, if it sends any messages, will most definitely not be
sending thirty of them per second.
What we will do to solve this dilema is replace our current GetMessage() function
with a new function, PeekMessage(). This function does essentially the same thing,
but with one important difference: it doesn't wait for anything. PeekMessage() just
looks into the message queue and checks to see if any messages are waiting. If not,
the program will continue on, allowing us to do what we need.

Image 4.2 - The Structure of a PeekMessage() Loop
Before
we go any further, let's take a good look at PeekMessage(). Here is it's
prototype.
BOOL PeekMessage(LPMSG lpMsg,
HWND hWnd,
UINT wMsgFilterMin,
UINT wMsgFilterMax,
UINT wRemoveMsg);
The first four parameters should be familiar to you. They are identical to
the four parameters of GetMessage(). However, the fifth one, wRemoveMsg, is
new.
What it does is indicate whether or not the message retrieved from the event queue
should stay on the event queue of come off. We can put either PM_REMOVE or
PM_NOREMOVE. The first one take the messages off the queue when they are read,
while the second one leaves the messages there for later retrieval. We will
use the PM_REMOVE value here, and keep things simple.
So how do we implement this into our program? Following is the main loop from the
last program we made, modified to use PeekMessage().
// Enter the infinite message loop
while(TRUE)
{
// Check to see if any messages are waiting in the queue
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// If the message is WM_QUIT, exit the while loop
if (msg.message == WM_QUIT)
break;
// Translate the message and dispatch it to WindowProc()
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Run game code here
// ...
// ...
}
Now our program can handle things as timely as we please, without having to worry
about Windows and its tedious messages. Let's quickly go over the changes
we made.
Naturally, this creates an infinite loop. We will escape out of it later when
it comes time to quit the game.
Because we are no longer waiting for a message, but rather just peeking in to see
what's there, we will use an 'if' statement around PeekMessage(). PeekMessage()
returns TRUE if there is a message and FALSE if there isn't. So, if there
is a message, it runs the contents of the 'if', and just continues on to the game
code if there isn't.
If our message turns out to be a WM_QUIT, that means its time to exit our "infinite"
loop and return to
Windows. Remember, we didn't have this before because GetMessage()
would always return '0' if it was time to quit, thus breaking the while() loop.
We don't have that mechanism here unfortunately, and we have to look for it ourselves.
Here is our new code, after we've modified the program with PeekMessage().
The parts that changed are now in bold.
[
Show Code]
// include the basic windows header file
#include <windows.h>
#include <windowsx.h>
// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam);
// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// the handle for the window, filled by a function
HWND hWnd;
// this struct holds information for the window class
WNDCLASSEX wc;
// clear out the window class for use
ZeroMemory(&wc, sizeof(WNDCLASSEX));
// fill in the struct with the needed information
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC)WindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpszClassName = L"WindowClass1";
// register the window class
RegisterClassEx(&wc);
// create the window and use the result as the handle
hWnd = CreateWindowEx(NULL,
L"WindowClass1", // name of the window class
L"Our First Windowed Program", // title of the window
WS_OVERLAPPEDWINDOW, // window style
300, // x-position of the window
300, // y-position of the window
500, // width of the window
400, // height of the window
NULL, // we have no parent window, NULL
NULL, // we aren't using menus, NULL
hInstance, // application handle
NULL); // used with multiple windows, NULL
// display the window on the screen
ShowWindow(hWnd, nCmdShow);
// enter the main loop:
// this struct holds Windows event messages
MSG msg;
// Enter the infinite message loop
while(TRUE)
{
// Check to see if any messages are waiting in the queue
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// If the message is WM_QUIT, exit the while loop
if (msg.message == WM_QUIT)
break;
// translate keystroke messages into the right format
TranslateMessage(&msg);
// send the message to the WindowProc function
DispatchMessage(&msg);
}
// Run game code here
// ...
// ...
}
// return this part of the WM_QUIT message to Windows
return msg.wParam;
}
// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// sort through and find what code to run for the message given
switch(message)
{
// this message is read when the window is closed
case WM_DESTROY:
{
// close the application
entirely
PostQuitMessage(0);
return 0;
} break;
}
// Handle any messages the switch statement didn't
return DefWindowProc (hWnd, message, wParam, lParam);
}
When you run this program, you will find that there is actually no difference in
the outcome. The truth is, you will not see the difference until you use both
functions in game programming.
Well, we have one last step, and we're ready to start programming games!
Next Lesson: Building a Basic Timer
GO! GO! GO!
© 2006-2009 DirectXTutorial.com. All Rights Reserved.