Top

C++/Win32: Creating a Win32 Window Wrapper Class I

March 2, 2008

One thing to keep in mind when coding against the Win32 API is that it was meant for C programming.  This presents a couple problems when working in C++ and attempting to keep things as object oriented as possible.

In this lesson, we’ll be covering a lot of information really quickly. Most of it you may already know, but it’s never a bad idea to go over old material again. As this was written for someone who knew C programming for Unix, it assumes some programming knowledge, while covering a lot of C++ fundamentals. So we’ll be jumping from C++ concepts to Win32 concepts pretty rapidly.To make it through this, I recommend printing out the source code and reading over it first. I have explained the code here by analyzing each of the files in the project, covering points I felt needed explanation. If something is missed or too obvious, please feel free to leave a comment at the end of the page to that effect.

Our goal is to take what we did in the last lesson — namely, creating and showing a basic window — and wrap it up in a class. This would generally be pretty simple, since we could just make member functions of our class with names like Register(), Create() and Show() to handle these tasks, and call them by way of our window wrapper class object. But the message handler function, remember, has to be declared in a very specific way, because it is called directly by Windows. When a member function of an object is called, the system invisibly passes in the this pointer, which will confuse everything. Our WinProc function will be expecting this, Windows won’t know it needs to pass anything in (and won’t be able to, anyways) and the function call will fail. Well, it would fail if the compiler allowed this to happen, which, fortunately, it won’t. So we need a way to declare the WinProc function inside our class without it expecting this.

The answer is to make the function static. When you declare a member function as static, you are telling the compiler not to make a unique copy of this function for each object — they will all share the same function. But because the this pointer is not passed in, your static function will not have access to the member variables of the object (they will have access to static variables, however). This is because the compiler uses the this pointer to figure out which objects variables are local to the function. This causes a problem, which we will discuss later on and solve in the next lesson.

But we have to make our message handler function static, and in this part of the creation of our window wrapper class, that’s all the new work we’ll do besides segregating our window creation code into member functions. So we’ll be at liberty to discuss some C++ concepts for those unfamiliar or only slightly familiar with C++.

Win32WrapperClass.h

===================

In dissecting this, start by looking at Win32WrapperClass.h, where you’ll find the class declaration. In here, we’re basically telling the compiler in very general terms what this class looks like. We give the class a name, list its data members (variables) and separate them into public and private access levels. We also list the methods, and separate them into public and private also.

There are a few things to notice here. First, you see this at the top of the file:


#ifndef _WIN32WRAPPERCLASS_H_
#define _WIN32WRAPPERCLASS_H_

and this at the bottom:


#endif // _WIN32WRAPPERCLASS_H_

These are directives to the preprocessor telling the compiler to only include this file if it hasn’t been included already. When you break your project into a bunch of smaller files, you run the risk of including a file more than once. This is a problem because the same code will appear more than once. How it works is this: The compiler picks a starting point and begins generating code as it reads through the file. When it hits an #include line, it goes to that file, and inserts that code into the master file its building. If it finds the same #include in another file, and there is no protection in place, it will insert that code in a new place. Now, you’ll have two definitions of the same class. The compiler is stupid, so it won’t check to see if they are the same. Instead, it freaks out and tells you that the class was defined twice.

To correct that, you insert a #ifndef line at the top and an #endif to close it at the end. In english, this is what it does:

if IDENTIFIERNAME isn’t defined, define it, and then run this code.

if IDENTIFIERNAME _is_ defined, everything is ignored.

You can pick whatever you want for IDENTIFIERNAME, but to make sure that it isn’t accidentally used elsewhere (thus preventing the declaration to appear even once) the standard is to use _FILENAME_FILETYPE_, thus here we use _WIN32WRAPPERCLASS_H_ since our filename is Win32WrapperClass.h

That’s very simple, but really important.

Back to the class declaration. Notice we basically announce variables and methods, but don’t define them. We _could_ define them here if we wanted, but in order to make your code more manageable, it is good practice to put your declaration in one place, and your definition in another.

Now this stuff is important:

Notice these two lines:

Win32WrapperClass(char *ClassName, char *WindowTitle, DWORD dwStyles,
                               RECT *rect, HINSTANCE hInstance);
virtual ~Win32WrapperClass();

We are declaring two functions, one with the same name as the class, and one with the same name except with a ~ at the front. The first is called a constructor, and the second is a destructor. The constructor is called automatically whenever you create an object of this class, and the destructor is called automatically when you destroy it. They are used, obviously, for initializing and cleaning up. You can only have one destructor, but you can have several constructors.

Imagine we have a class called Integer, which is a wrapper class for the int type. This is common actually, when you want to use a simple type in an object oriented way, you will often make a class that wraps it, and make the actual int private, then write a bunch of public methods for manipulating the data. Here’s a simple class declaration for an Integer class:

class Integer {

     public:
	     Integer();
	     Integer(int initialValue);
	     virtual ~Integer();
     private:
	     int data;
}

Notice there are two constructors. One takes no arguments and one takes an integer. The first might set data to 0, as a default. The second would most likely set data to whatever int gets passed in at the time of construction.

To illustrate that, here’s how we might create two Integer objects in WinMain:

Integer *integer1 = new Integer();
Integer *integer2 = new Integer(5);

In the first case, integer1 is created and the default (no argument) constructor is called automatically. The data variable in integer1 would be set to 0 by the constructor. In the second case, integer2 is created and the constructor that takes an int is called automatically. The data variable in integer2 would be set to 5 by the constructor.

Because the compiler needs to be sure which constructor you mean to use, your parameter lists must be unique. In other words, you can only have one constructor for each unique parameter list.

Also notice that the destructor here is declared to be virtual. This means that any classes that derive from this class may redefine this destructor if they want to. Remember we talked about base classes and derived classes having a parent/child relationship. You can create a new class that extends a base class — it will have everything the base class has plus whatever you add to it. Well, if you declare a function in the base class as virtual, then in the deriving class you have the option of rewriting the function however you want. You can leave it alone and get the one that the base class has. Either way. Logically, destructors are usually virtual, because destructors, remember, are for cleanup. In C++, you are in charge of your own memory, so if you ask for a piece of RAM from the system, you must remember to give it back. If you forget, its called a memory leak. Imagine a class that asks for 32 bytes of RAM for storage, and the destructor fails to free it up. Not a big deal, unless you use objects of this class for a short time, then destroy them, and you use thousands of them per second. Everytime you create the object, use it and destroy it, you are leaving 32 bytes reserved. In a few seconds, the system will be out of memory, and your program will crash. You’ll see, as you go along with programming, why programs crash. This is the main reason. So, you’ll want to make your destructor virtual, so that if I derive a class based on your class, and I add some new memory need to it, I will be able to write code to free up the memory in my class. In my destructor, I will probably want to call your destructor first, to make sure your cleanup gets done before mine.

Our Win32WrapperClass constructor takes several arguments. Here’s the declaration:

Win32WrapperClass(char *ClassName, char *WindowTitle, DWORD dwStyles,
                               RECT *rect, HINSTANCE hInstance);

Upon creating an object of the Win32WrapperClass class, we will need to specify a class name (for use in the RegisterClass and CreateWindow calls), a string to use as the text in the title bar, a list of styles we want our window to have, and a RECT structure defining its position and dimensions.

Before leaving Win32WrapperClass.h, notice that the stWinProc function is static. This is to prevent the system from passing the this pointer to the function (which we talked about already).

Win32WrapperClass.cpp

=====================

Here is where we actually define the methods we saw in the class declaration. We had talked about making three methods, Register(), Create() and Show() to match what you did in the non-OOP version of this program, but you’ll notice I merged Register and Create into one function, Create (). Same difference. When you are designing your classes, you will want to be aware of the future, meaning that you might want to break things into smaller segments than seems natural, just in case down the road you want to make a change to a specific thing. In this case, we might, later, want to have different Register() functions — maybe a default one, maybe one that takes certain parameters, whatever. The point is that when we DO decide to do that, it would be nice if we could just add Register () functions to the one we already have. The way

I’ve written it, we can’t do that as easily. But, who cares really for this example.

Anyways, it should be pretty clear what’s going on here. I’ve made the functions return the BOOL type (either true or false, signifying success or failure) so that the WinMain function can bail out if Windows, for whatever reason, fails to do its job.

Method definitions are put together just like regular functions, but with the class name and the scope operator in front of the function name.

So, if we have a class declaration as follows:

class Easy {
     public:
	int myFunction (int a, int b);
}

we can define our function myFunction as follows:

int Easy::myFunction (int a, int b) {
	return a + b;
}

Really simple. Scope, as I told you, is important because it allows different classes to have functions of the same name. Then, when you go to define those functions, you have a way of telling the compiler which class’ function you’re talking about. Scope is way more complicated than that, of course, but for now that’s the important thing.

Also notice that there is a stWinProc function and a WinProc function. Your class, for now, should only have a stWinProc function that basically just calls the default windows message handler. At the end of this, I’ll explain why I have two (because its an interesting example of what a BS hack job Windows programming is).

Main.h

======

Main.h is pretty simple. It just includes Win32WrapperClass.h and windows.h. But eventually, you will have a Main.h that includes everything your program is going to need when WinMain starts.

Main.cpp

========

WinMain is the main entry point of the application.

First, we include Main.h, which in turn includes windows.h and Win32WrapperClass.h. Now we have introduced everything we will need to the compiler. Remember that you don’t know what order things will be compiled — it is very possible that Win32WrapperClass.cpp will be compiled first, which is why it includes Win32WrapperClass.h. Basically, every file should include what it needs (or it’s .h should include everything).

Next, we create two global variables:

HINSTANCE		g_hInstance;
Win32WrapperClass      *g_pWindow;

Now we have an HINSTANCE and a _pointer_ to a Win32WrapperClass. Do they need to be global? No way. Making them global makes them accessible to everything, anywhere. But WinMain only needs g_hInstance to pass it on to the Win32WrapperClass object’s constructor. And the pointer to the Win32WrapperClass object could be declared inside WinMain, since that’s the only function that uses it. Basically, making them global was pure laziness on my part.

Now here’s a good opportunity to talk about pointers. Remember that there is a difference between an object, and the pointer to the object. The object is the actual code itself. The pointer is a long integer that represents the memory address where it resides (the address of the first byte in the chunk of RAM that it occupies). Pointers are so infinitely useful its ridiculous, although it takes some time to see why.

Anyways, if you have a Win32WrapperClass object called, say, Obj1, you can execute its Show function as follows:

Obj1.Show();

However, if we Obj1 is a pointer to the object rather than an object itself, we must dereference it first. Dereference means what it sounds like. It returns the object that is pointed to. The shortcut for executing a class method in an object for which we have a pointer is this:

Obj1->Show();

Its a shortcut, because it actually is two steps: first, you would dereference a pointer using the * operator, then you would call its method using the . operator, like this:

(*Obj1).Show();

That’s ugly, so they made the -> operator to make it prettier.

So, * takes a pointer and returns its object. Then, since every operator has an inverse, we have &, which takes an object and returns its memory address (i.e. a pointer to it). No one ever explained it to me that way, but I think if they had it would have made a lot more sense. One of the reasons why your knowledge of computer architecture will benefit you as a programmer.

As a little bonus nonsense, you can have pointers to pointers. The reason for this is that when you make a pointer to an object, guess what? The memory address that the pointer points to is stored somewhere in memory itself. So you can declare pointers to pointers, and dereference them all day long.

Example:


int **intpointer;		// a pointer to a pointer to an int
**intpointer=5;			// the integer object at the end now = 5

*integer++;			// the pointer to the integer now points to
                                //the next space in memory.
				// what's there?  who knows, that's
                                // why hackers love C/C++

So the main difference between declaring pointers and objects is that when we declare an object, we get the object. When we declare a pointer,

we either need to point it to an existing object or demand a new object for it to point to. To do the latter, we use the new keyword.

Here’s an example of pointing a pointer to an existing object, and creating a new object:

MyClass myObject;			// Creates a MyClass object
MyClass *myPointer;			// Creates a pointer to a MyClass object

myPointer = &myObject;			// Points myPointer to myObject.  Note the use of the
                                        // & (address of) operator.	

myPointer = new MyClass ();		// Creates a new MyClass object and points myPointer
                                        // at it.  The MyClass object is created
					// using the default constructor.

Now, just real quickly, remember I said in C++ whenever you allocate memory for something, its your responsibilty to free it. So, when you are done with the new MyClass object you created, you can free it like this:

delete myPointer;

That’s it. The object is deleted. BUT the pointer still remains (because, after all, its just a long (32 bit) integer) so you can point it to something else or allocate a new object with the new keyword.

Back to Main.cpp, the next thing to notice is this loop:

while(1) {

   if (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE)) {
     if (Msg.message == WM_QUIT)
     break;
              TranslateMessage(&Msg);
              DispatchMessage(&Msg);

         }
}

Hopefully you got deep enough into loops to understand infinite loops. This loop is infinite, until it reaches the break; statement.

In order to keep your program alive, you need an infinite loop like this called a Message Pump. It will check to see what messages are in the message queue. Everything I said about Windows sending messages to your windows is true, but first they go in a queue and must be translated and dispatched by you. Not important now, but this loop should be easy to understand. It peeks at the next message, and if it is a WM_QUIT message, it breaks out of the loop and WinMain ends. If not, it translates it, dispatches it, and starts the loop again.

Why Are There Two WinProcs?

If you understood all that, this part is pretty cool.

Let’s review how Windows handles everything:

Every application has an associated HINSTANCE, which is just an integer that Windows assigns the instance of your app to keep track of it. You pass this thing all over the place in your program so everything knows which running program we’re dealing with. So if you run two instances of the same program at the same time, each will have its own unique HINSTANCE. Windows is nice enough to assign this for you, all you have to do is keep track of it in your program.

Windows also assigns an HWND to every window you have created, whether it is visible or not. An HWN also is just an integer that uniquely identifies the window. The tricky thing is to remember that in Windows, EVERYTHING is considered a window, whether its an actual, real window like you usually think of it, or a component in the window. For example, buttons are windows. Text labels are windows. Text entry boxes and radio & checkboxes are all windows. Like I said, basically everything. So each of them needs an HWND. The point of calling CreateWindow, actually, is to ask windows to assign a HWND to your window. That’s why you can CREATE a window before you SHOW it. The CreateWindow and CreateWindowEx functions both return the HWND that Windows assigns your window.

Now, whenever something happens to your window, like the user clicks on it, or even waves his mouse over it, Windows generates a message. Messages are the most important thing in Windows programming, for obvious reasons. A Windows message is stored in the MSG type.

Obviously, Windows needs a way to communicate with your program if it has a message to deliver to one of your windows. It does this by calling your windows message processor. So when you register your window, you need to tell windows the function it should call to pass the message to. Because you can’t hack the windows code to make it work with your program, you have to follow Windows’ rules. They say that your message processor has to look like this:

LRESULT CALLBACK stWinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

You can give it a name other than stWinProc, but otherwise, it has to look exactly like this. Windows expects to execute your function and pass it a HWND, a UINT, a WPARAM and a LPARAM. We’ll get to those in a minute. It also expects to pass those parameters to the function in a certain way, which is why you specify the CALLBACK protocol. It returns an LRESULT, which can either be failed or successful. This way Windows knows whether you got it or not.

But, as we talked about, if a method is a member of a class, the first parameter passed in is actually this. Since Windows doesn’t know or care

that we are working in C++, it won’t pass in the this pointer, and the method we’ve written will freak out. To avoid this, we make the method static. Static methods are shared. Every object of the class uses the same copy of the function, rather than its own, and so there is no need for it to receive the this pointer, and it doesn’t expect it. Problem solved. The new declaration looks like this:

static LRESULT CALLBACK stWinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

There’s still one problem left, though. We have wrapped our window in a class, and classes are meant to be reused. What if we want to spawn another window using this class? If we do this, Windows will call the same message handler, regardless of which window its intended for. This is kind of confusing, but just remember that we’ve basically said “if you get a message for this window, call this function.” Now we have two windows asking that their messages be sent to the same place.

Next: Creating a Win32 Window Wrapper Class Part 2.


Source Code: win32tut_part2.zip [486kb zipped]Additional Information:· Creating a Win32 Wrapper Class· A Simple Win32 Wrapper Class

Further Reading:

· Nitty Gritty Windows Programming with C++ by Henning Hansen.

· Thinking in C++ by Bruce Eckel.

Comments

2 Responses to “C++/Win32: Creating a Win32 Window Wrapper Class I”

  1. Garage Door Hardware on March 25th, 2008 12:57 pm

    Since I will be home this week, I plan to catch up on my paperwork, housework - and alll of those little projects that we always keep putting off - if you know what I mean… LOL!!!!!! Heck, I even looked up that today! Now tell me - how funny is that?????

  2. Ralf on June 20th, 2008 2:21 am

    Nice tut!

Got something to say?





Bottom