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

Messages

When you were programming in DOS, you didn't need to worry about other programs that might be running, because DOS is not a multitasking OS. When programming in Windows, however, you must take this into consideration. For this and other reasons, Windows uses what are called messages to communicate with applications and tell them what's going on. Messages serve a variety of purposes. They tell an application when its window is being resized, moved, or closed. They signal to a program that it is about to be closed. They inform a program when part of its window must be refreshed. They can be used to track mouse movement and button presses. The list goes on. In any case, your Windows program must be able to handle these messages.

The way this is done is through the use of a special type of function called a callback function. Callback functions are functions which you don't actually call from your code; rather, certain events cause them to be called. You create such a function by using the CALLBACK calling convention, much like the WINAPI convention is used for WinMain(). I'm going to leave this topic for a minute, though, because before you can process messages for your window, you have to be able to create the window in the first place.

Window Classes

Here's where it helps to know a little about C++, because the first thing you must do to create a window is to create a window class. The class contains all the information about the window such as what icon it uses, the menu attached to it (if any), etc. In just about every Windows program you create, you'll need to create a window class to meet your needs. In order to do this, you need to fill out a WNDCLASSEX structure. The "EX" part of the name stands for "extended," as there is an older version of this structure called WNDCLASS. We'll be using the extended version. Here's what it looks like:

typedef struct _WNDCLASSEX { UINT cbSize; UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HANDLE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; HICON hIconSm; } WNDCLASSEX;

This structure has quite a few members to it, and you must set them all in order to create your window class. It's not so bad, though. Let's run through a brief description of all the fields.

UINT cbSize: This is the size of the structure, in bytes. You'll see this a lot, especially if you get into DirectX. It's included so that if a structure of this type (or rather, a pointer to that structure) is passed as a parameter to a function, the structure size can simply be looked up rather than having to be computed. Always set it to sizeof(WNDCLASSEX).

UINT style: This is the window style, which takes constants prefixed by CS_. Furthermore, you can combine several of these constants by using the | (bitwise OR) operator. Most times there are only four you'll use. For the sake of keeping the length of this article down, I'll show you those four. You can always look up the rest on your MSDN Help. You did remember to get Visual C++, didn't you?

CS_HREDRAW Specifies that the window should be redrawn if it is horizontally resized.
CS_VREDRAW Specifies that the window should be redrawn if it is vertically resized.
CS_OWNDC Allows each window to have a unique device context or DC (not covered in this article).
CS_DBLCLKS Discerns between single- and double-clicks while the mouse is in this window.

WNDPROC lpfnWndProc: A pointer to the callback function that handles messages sent to this window. If you've never used function pointers, the address of a function is simply the function's name, without the parenthesis afterwards.

int cbClsExtra: This is reserved for extra class info, which most programs don't need. Certainly you won't find many uses for this when writing games, so simply set it to 0.

int cbWndExtra: Basically the same as cbClsExtra, except for extra window information. You'll almost always be setting this one to 0 as well.

HANDLE hInstance: This is the instance of the application using the window class, which is one of the parameters passed to WinMain(). This should be set to hinstance.

HICON hIcon: This is a handle to the icon that represents the program, and will usually be set using the LoadIcon() function. Until you learn how to use resources in your programs, you can set this to a generic system icon by using LoadIcon(NULL, IDI_WINLOGO). There are other IDI_ constants representing Windows icons; you can find the list in the Help files for your compiler.

HCURSOR hCursor: This is a handle to the cursor used for the mouse while it is in your window. This is usually set using the LoadCursor() function. Again, you can use resources to load your own custom cursors, but until you learn that, or if you just want the standard Windows cursor, use LoadCursor(NULL, IDC_ARROW).

HBRUSH hbrBackground: When your window receives a message that it needs to be refreshed (or "repainted"), the least that will happen is that Windows will repaint the area with a solid color or "brush." That brush is defined by this parameter. There are several kinds of stock brushes you can load using the GetStockObject() function. Some of these are BLACK_BRUSH, WHITE_BRUSH, GRAY_BRUSH, etc. For now, you're safe using GetStockObject(BLACK_BRUSH). Sorry I'm touching on all of these functions so briefly, but I'm trying to keep the length down. I'll revisit them in future articles, I promise!

LPCTSTR lpszMenuName: If you want to create a window with pull-down menus, this parameter gives the name of the menu to load and attach to the window. Since you don't know how to create menus yet, you can specify no menu by setting this to NULL.

LPCSTR lpszClassName: This is simply a name by which you refer to the class. You can call it anything you want, so use a descriptive name. You might call it "Game_Class" or something like that.

HICON hIconSm: This is a handle to the small icon used on the window's title bar and on the Start Menu bar. You set this the same way you set hIcon -- by using the LoadIcon() function. For now, we'll use LoadIcon(NULL, IDI_WINLOGO) for the standard Windows logo icon.

That's it! Now that you're familiar with all the fields of the WNDCLASSEX structure, you can fill it out and you're ready to create a window. A sample class might look like this:

WNDCLASSEX sampleClass; // declare structure variable sampleClass.cbSize = sizeof(WNDCLASSEX); // always use this! sampleClass.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW; // standard settings sampleClass.lpfnWndProc = MsgHandler; // we need to write this! sampleClass.cbClsExtra = 0; // extra class info, not used sampleClass.cbWndExtra = 0; // extra window info, not used sampleClass.hInstance = hinstance; // parameter passed to WinMain() sampleClass.hIcon = LoadIcon(NULL, IDI_WINLOGO); // Windows logo sampleClass.hCursor = LoadCursor(NULL, IDC_ARROW); // standard cursor sampleClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); // a simple black brush sampleClass.lpszMenuName = NULL; // no menu sampleClass.lpszClassName = "Sample Class" // class name sampleClass.hIconSm = LoadIcon(NULL, IDI_WINLOGO); // Windows logo again

And you're all set. There is one thing I should mention, though. Notice the typecast to HBRUSH on the result of the GetStockObject() function. This is because GetStockObject() can be used to load many objects, not just brushes, and so it returns a variable of type HGDIOBJ, which is a bit more general. In older versions of Visual C++, you wouldn't need the typecast, but VC++ 6.0 is more picky about typecasting, so you'll get an error if you try to compile without it.

The last thing you need to do is register the new class with Windows so you can use it to create new windows. This is accomplished with a simple function call to RegisterClassEx(). It only takes one parameter: the address of your structure. So in the example listed above, you would register the class like this:

RegisterClassEx(&sampleClass);

Now that Windows is familiar with the new class you've created, you can use it to create a window. It's about time, hey?




Next : Creating Windows