C++/Win32: Creating a Win32 Window Wrapper Class II
March 2, 2008
Now to finish up what we started in part I. Let’s put the lid on our window wrapper class.
Let’s review real quick what we’ve done:
1.) We’ve created a class that has two public functions called Create() and Show(), each returning a BOOL (true/false) value indicating whether it was able to complete its task or not.
2.) We’ve built a static message handler called stWinProc to receive messages from Windows. Remember that we have to pass the name of this function to Windows when we call RegisterClassEx, and we cannot change it later. Also remember that this method has to be static because C++ passes in the this pointer invisibly when a class member method is invoked, which confuses Windows (since, again, Windows doesn’t know or care what language the function was written in, and expects to pass variables to it in a very specific way, defined by the CALLBACK protocol).
3.) We have created a pointer to a Win32WrapperClass object in WinMain called g_pWindow, created a new Win32WrapperClass object using the new keyword, and pointed our pointer at it. Then we invoked g_pWindow’s Create() and Show() methods to get the window on the screen, and entered an infinite loop to keep the window running, until the WM_QUIT message is received, at which time we break from the loop, delete our Win32WrapperClass object, and exit the application.
That should all make perfect sense before moving on.
So now, the problem is this: what if we make TWO Win32WrapperClass objects? Because they are both created from the same class definition, they will both be using the static message handler we’ve written, because static methods (and variables) are shared by all objects of the class. So how do we get messages to their proper window?
There are a few ways to do this. One would be to write a new class declaration/definition for each window, but that defeats the purpose
of classes, which is reusable code. We specifically WANT to be able to use this thing over and over again. Other solutions have the same result: you will basically be writing a bunch more code that doesn’t need to be written.
What would be nice is if we could use our stWinProc method to receive messages, determine where they are meant to go, and then invoke a non-static message handler that would be unique for every object. Windows needs a static method to pass messages to, but there’s no reason we can’t hand those messages off to any other method we want after Windows is done passing them in. This will work, but how will we know what window the message is intended for? If we had the this pointer of the Win32WrapperClass object, then we could just call its message handler from inside the static message handler like this:
// ourWindow is a Win32WrapperClass pointer pointing to the correct window ourWindow->WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
This just passes everything given to us by Windows to the correct message handler, which then does all the work.
Then the question is, how do we get the pointer to the window from what Windows gives us?
Look at the parameter list for stWinProc. This will tell you everything Windows is giving us when it receives a message for our window.
HWND hWnd // The window's HWND, which is just a number that
// Windows (and our app) uses to identify this window.
// Every window gets one of these from Windows upon
// creation.
UINT uMsg // The actual message. UINT means Unsigned INTeger.
// We'll look at messages later, but they are mapped
// to constants, such as WM_QUIT, which is the message
// for application quit requested (e.g. clicking the X
// at the top right of the window)
WPARAM wParam // Additional information passed in with the message.
LPARAM lParam // Additional information passed in with the message.
// Both wParam and lParam will generally have to be
// broken apart and examined by
// your message handler to see exactly what you need to
// know. For example,
// lets say uMsg == WM_KEYDOWN, which tells us that a
// key was pressed. Then somewhere in wParam and lParam
// will be the data telling you which key.
Most important for now is that Windows is giving us the HWND of the window in question. It would seem that we are done, since we now have at least some kind of handle on the window, we should now be able to call the window’s message handler. But, in order to do that, we will need a pointer to the Win32WrapperClass object, right? So how do we get from A to B here? Ponder it for a moment, and then stop, because you can’t easily. We could keep a list of all of our Win32WrapperClass objects and then search through it for our HWND every time we receive a message, but that is stupid and inefficient, as it will require memory and time that we’d rather not spend. It also could crash the system if we have too many windows open. That might seem ridiculous, but not when you consider that EVERYTHING is considered a window and has its own HWND, including text labels, text input boxes, buttons, etc. So what you consider a window is actually a window containing several windows, which in turn may contain several more windows, and on and on. There’s a lot of HWNDs floating around in an application, even though we’ve only dealt with one so far.
The answer lies in the structure of a window. Remember that an object is built based on a class definition. When the system creates your object for you, it is just allocating a block of RAM, and storing data in it. In C/C++, the most important thing for you to have a conceptual idea of is RAM. So let’s look at a simple example:
class TwoIntegers {
public:
TwoIntegers();
~TwoIntegers();
int a;
int b;
}
This class is ultra-simple, and just contains two integers. The space hasn’t been allocated yet, because this is just a template for the objects that our program will actually use. To create the object, we’ll do something like the following:
TwoIntegers *myPointer; myPointer = new TwoIntegers();
We’ve made a pointer to a TwoIntegers object, and then asked for a new TwoIntegers object for it to point to. Here’s what it looks like in
system memory:
--------------------------------------------------------------------------------------------------- | int a (16 bits) | int b (16 bits) | TwoIntegers() (constructor) | TwoIntegers() (constructor) | ---------------------------------------------------------------------------------------------------
That’s it. Classes and objects are just fruity ways for things to make more sense for programmers, but its all just memory allocation and memory addressing.
Now on to the point: the window class object (not our Win32WrapperClass, but the actual class that defines a window) contains a bit of memory called the WindowLong, which is a long integer (32 bits) that can be used however the programmer sees fit. This is where the absolute hackjob that is Windows programming becomes evident. This bit of memory is available to you so that you can workaround ridiculous problems like this one.
Anyways, to set the WindowLong value, you call the Windows function SetWindowLong, and to retrieve it, you call the Windows function GetWindowLong. All we need to get or set this value is the HWND of the window, which makes this a logical necessity, since that’s all Windows is going to give us along with the message. Conveniently, a pointer to a Win32WrapperClass (which, rememember, is just a memory address) will fit in this 32 bits of memory.
Here’s the complicated part: deciding when to Set the WindowLong. When your Window is created, Windows sends a message to your message handler immediately. The message is WM_NCCREATE. Remember that Windows also passes extra information in the wParam and lParam values along with your message. In the case of WM_NCCREATE, it passes in the last parameter you passed to windows when calling CreateWindowEx.
Confused? Go look at our Create() function in Win32WrapperClass.cpp. Look for the call to CreateWindowEx. It looks like this:
m_hWnd = CreateWindowEx (WS_EX_CLIENTEDGE, m_pClassName,m_pWindowTitle, m_dwStyles, m_pRect->left, m_pRect->top, m_pRect->right-m_pRect->left, m_pRect->bottom-m_pRect->top, NULL,NULL, m_hInstance, (void *)this);
Notice the last parameter passed is (void *)this. Here we are taking the this pointer, which you’ll remember points back at the object itself, and we cast it to type void *. void means nothing or who knows in C/C++. Why cast it to void? Long story, ask me later
Basically, though, we are making it fit into a long integer. We are insuring it is 32 bits.
Now, Windows takes that long and passes it to the message handler for the window, along with the message WM_NCCREATE, in the lParam value. This means that we can handle the WM_NCCREATE message in our static message handler and quickly grab the void * that points to the Win32WrapperClass object.
Look at our static message handler. This is it stripped down to basics:
Win32WrapperClass* pWnd;
if (uMsg == WM_NCCREATE) {
SetWindowLong(hWnd, GWL_USERDATA, (long)((LPCREATESTRUCT(lParam))->lpCreateParams));
}
pWnd = (Win32WrapperClass *)GetWindowLong(hWnd, GWL_USERDATA);
if (pWnd) return pWnd->WinProc(hWnd, uMsg, wParam, lParam);
else return DefWindowProc(hWnd, uMsg, wParam, lParam);
If the message we received is a WM_NCCREATE, we are being alerted that a window was just created. We take the pointer we gave to Windows, which Windows is now passing back to us as the lParam that comes with the WM_NCCREATE message, and use it to SetWindowLong the GWL_USERDATA (the user defined area of memory in the object) to this pointer. Now, whether or not this was the WM_NCCREATE message, we know for sure that our Win32WrapperClass object has this pointer stored in it, that we can retrieve using GetWindowLong. So that’s what we do. We
retrieve the pointer, cast it to the proper type (Win32WrapperClass *) and use that pointer to call the appropriate message handler. Just to be safe, we will call the DefWindowProc (default) if pWnd fails to point to anything. Remember, there are more windows than you might think, so this will happen a lot.
That’s it. We have now solved the problem of — you’ll like this — message routing. In order to show the usefulness of this, we will want to make a class that extends the Win32WrapperClass — one that is more specific to our needs. I’ll be doing that next, so for now I’ll preview what will happen.
When we extend a class, we are creating a new class that inherits from the base class. Any functions designated virtual in the base class will be overridable, meaning we can write a new one that does what we need it to do. Notice in Win32WrapperClass that the function WinProc is virtual. Naturally, we will want to write a custom message handler for our new window class, and virtual tells the compiler that this is ok.
So here we’ll have to discuss public, private and protected. When you extend a class, you need a way of designating which members (methods and variables) get inherited in the new class.
public: all public members are inherited and remain public in the new class private: all public members are inherited and become private in the new class
You can also derive from a class using protected, but don’t worry about that for now. If a member is declared protected in the base class, however, it will be inherited, and remain protected in the new class.
Here’s some examples to clear it up:
class Simple {
public:
int pubVar;
private:
int priVar;
protected:
int proVar;
}
class LessSimple: public Simple {
// this class will get pubVar and proVar as is, but priVar will not be inherited
}
class KindaHard: private Simple {
// this class will get pubVar, but it will be private.
// proVar will also be inherited, and remain protected.
// priVar will not be inherited
}
You will usually inherit using public. The important thing to remember here is that if you want your private members to be inherited by child classes, you should make them protected, as they will still be hidden from public view.
That’s it. Pretty soon, we’ll finally be able to start to turn this thing into a port scanner.
Next: Inheritance and Beginning GUI.
Source Code:Note: this is the same project file as the last lesson!
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
Got something to say?