Sunteți pe pagina 1din 6

C++ Win32 API Simple GUI Wrapper | Inferno Development

1 of 6

IT

Technology

Humor

Programming

Web Dev

http://www.infernodevelopment.com/c-win32-api-simple-gui-wrapper

CS

Design

Home

C++ Win32 API Simple GUI Wrapper

Forum Community

Oct 17, 2008 Tomescu Alin @ 5:01 pm GMT C++ Win32 API

How would you like to write your Windows Graphical (Win32 GUI) software as EASY as
this?
MyWindowClass wndClass (hInstance, TEXT("GenericWindowClass"));
MyWindow wnd(hInstance, TEXT("My Window Title"), wndClass.className ());
wnd.Create();
wnd.Show();

Easy programming in Win32 isn't it?


toc_collapse=0;
Contents [hide]
Simple C++ Win32 API Wrapper Tutorial
The Problem
Window Wrapper
The Window Class Wrapper
Your New Simple WinMain

Simple C++ Win32 API Wrapper Tutorial


You should understand C++ class inheritance and polymorphism, that is, abstract classes,
virtual functions, pure virtual functions.
You should know that this tutorial assumes you have a thorough understanding of C++ and a
basic understanding of Win32 API.
You'll notice the use of the C++ scope resolution operator when calling Win32 API functions
such as ::CreateWindowEX () or ::RegisterWindowEx(). That's just a habit of mine when
calling these kind of functions, basically it tells C++ to search the functions in the global
namespace.
How would you like to write your Win32 programs like this?
MyWindowClass wndClass (hInstance, TEXT("GenericWindowClass"));
MyWindow wnd (hInstance, TEXT("My Window Title"), wndClass.className ());

Search

Popular content
All Time:

45 Incredible Futuristic Scifi 3D


City Illustrations
C++ Win32 API Tutorial
Singleton C++

How to Make a Wireframe Render


in Cinema 4D (C4D)
Harvard scientists discover
protein GDF-11 that reverses
heart disease in old mice

Get Free Updates


Enter email to receive new posts:

Subscribe

We will never distribute your email address.

Updated Content
C++ Tutorials

Sign Up! | Login

wnd.Create ();
wnd.Show ();
/* message loop */

Everyone knows that creating a window is not an simple task but a rather tedious one, so I'm
going to try and make it easier. This method is very similar to Qt, Java, or WxWidgets.

The Problem
If you ever tried wrapping a window's functionality I bet the first thing you asked yourself
was: "What am I going to do with the window procedure?". I also bet this was the first answer
that crossed your mind: "I'm just going to declare it as a member function within my window
wrapper class and then I'm gonna pass it to the WNDCLASSEX structure as the
lpfnWndProc field."
Well, unfortunately that won't work. Why? Because member functions have an additional
hidden parameter, the this pointer, so instead of LRESULT CALLBACK wndProc (HWND,
UINT, WPARAM, LPARAM) your member function will be LRESULT CALLBACK
memberWndProc (YourClassName *this, HWND, UINT, WPARAM, LPARAM).
Obviously you won't be able to do the assignment wcx.lpfnWndProc = memberWndProc
because the two function pointers are of different types.
So how do we solve this? We'll use a static member function that will act as a message
router. A static member function can be called without creating an instance of the class,

26-Oct-15 21:25

C++ Win32 API Simple GUI Wrapper | Inferno Development

2 of 6

http://www.infernodevelopment.com/c-win32-api-simple-gui-wrapper

therefore it doesn't have the this pointer in its parameter list, allowing us to pass it as the
lpfnWndProc member in WNDCLASSEX.
Message router you say? Yes, because the window procedure is going to be a static
member function, we can't redefine its behavior in derived classes so we're gonna use a little
hack. We're gonna declare another window procedure function that will be implemented in
derived clasess and will do the actual message processing particular to each window. The
static message router is going to determine which instance's window procedure to call by
associating the window's HWND handle with a pointer to the derived class instance. This will
be explained later.

Window Wrapper
First we create an abstract window class, let's call it AbstractWindow. Later, we are going to
create our own windows by deriving this class and defining some of its member functions.
// This is "AbstractWindow.h"
class AbstractWindow {
protected:
// window handle
HWND _hwnd;
public:

// ... other members ...


AbstractWindow () {}
// this will be WNDCLASSEX::lpfnWndProc
static LRESULT CALLBACK msgRouter (HWND, UINT, WPARAM, LPARAM);
// this is the actual window procedure
// this will be implemented in derived classes and will be called by msgRouter
virtual LRESULT CALLBACK wndProc (HWND, UINT, WPARAM, LPARAM) = 0;
// calls CreateWindowEx, creating the window
virtual bool Create ();
// ... other member functions ...

};

Notice the pure virtual function wndProc, this is the actual window procedure that will be
implemented in derived classes, the static msgRouter function only determines the instance
of AbstractWindow to send the message to and calls its wndProc. How?
1. In the AbstractWindow::Create () method, we're gonna pass the this pointer as the last
parameter of CreateWindowEx().
2. Then, CreateWindowEx() sends it to msgRouter through a WM_NCCREATE message.
(Details: the pointer is stored in the LPARAM parameter as a LPCREATESTRUCT
structure, in the lpCreateParams field. Not important)
3. We then handle the WM_NCCREATE message and associate the AbstractWindow
pointer with the window's HWND using SetWindowLong ().
4. Then, we retrieve the AbstractWindow pointer using GetWindowLong () and call its
wndProc method that will do the actual processing.
So basically that is what will happen for every newly created window. For an existing one,
when messages arrive in msgRouter, we'll have the window's handle which we'll use to
retrieve the pointer to its AbstractWindow object that contains its window procedure, then
we'll forward the message there.
Here's the code:
// This is "AbstractWindow.cpp"
#include "AbstractWindow.h"
bool AbstractWindow::Create ()
{
// we'll just assume CreateWindowEx ()'s parameters are protected members of AbstractWindow
_hwnd = ::CreateWindowEx (
_styleEx,
_className,
_windowName,
_style,
_x,
_y,
_width,
_height,
_hwndParent,
_hMenu,
_hInstance,
this
// pointer to this class instance
);
if (!_hwnd) return false;
else return true;
}
LRESULT CALLBACK AbstractWindow::msgRouter (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
{
AbstractWindow *wnd = 0;
// pointer to the window that should receive the message
if (message == WM_NCCREATE) {
// if this message gets sent then a new window has just been created,

26-Oct-15 21:25

C++ Win32 API Simple GUI Wrapper | Inferno Development

3 of 6

http://www.infernodevelopment.com/c-win32-api-simple-gui-wrapper

// so we'll asociate its handle with its AbstractWindow instance pointer


::SetWindowLong (hwnd, GWL_USERDATA,
long((LPCREATESTRUCT(lParam))->lpCreateParams));
}

HWND

class

// --- messages different from WN_NCCREATE / or WM_NCCREATE was just processed --// we retrieve the instance of AbstractWindow that corresponds to the destination window's
wnd = (AbstractWindow *) (::GetWindowLong (hwnd, GWL_USERDATA));
// we then route the message to the wndProc method defined in the derived AbstractWindow
if (wnd)
wnd->wndProc (hwnd, message, wParam, lParam);
else
// for messages that arrive prior to WM_NCCREATE
// and the HWND <-> AbstractWindow * association was not made
return ::DefWindowProc (hwnd, message, wParam, lParam);

Now the only thing left is to derive a class from AbstractWindow and define its wndProc
method. Before doing that I'm gonna show you how to wrap the WNDCLASSEX structure so
you can easily create a Window Class that uses the AbstractWindow::msgRouter as its
window procedure.

The Window Class Wrapper


Before creating a window we must first create a Window Class. I'm hoping that by this time
you know what I mean by a Window Class. The following C++ class will wrap a Window
Class more precisely the WNDCLASSEX structure, the other one above wrapped a
window.
There are several ways of doing this, but the only thing each implementation must have in
common is setting the lpfnWndProc field of WNDCLASSEX to AbstractWindow::msgRouter,
because that's the place we want all the messages for our windows to go.
So let's call this class SimpleWindowClass. Basically, the only input the class' constructor
needs is the application's HINSTANCE and the Window Class name.
This would be one way of implementing it:
// This is "SimpleWindowClass.h"
class SimpleWindowClass : protected WNDCLASSEX {
public:
SimpleWindowClass (HINSTANCE hInst, const TCHAR * className);
// registers the class
virtual bool Register ();
//retrieve the class name
virtual const TCHAR * className () const { return lpszClassName; }
protected:
// --- all WNDCLASSEX's members are protected, so they can be inherited by derived

classes --};

The class inherits from the WNDCLASSEX structure, has a virtual member function that
retrieves the Window Class' name, a Register() member function that calls the
RegisterClassEx() Win32 function to register the window class, and a constructor that will fill
in the WNDCLASSEX structure.
Here's the implementation:
// This is "SimpleWindowClass.cpp"
// include the AbstractWindow header,
// we need it for the lpfnWndProc = AbstractWindow::msgRouter assignment
#include "AbstractWindow.h"
#include "SimpleWindowClass.h"
SimpleWindowClass::SimpleWindowClass (HINSTANCE hInst, const TCHAR *className)
{
// could've used GetModuleHandle (NULL) instead of passing the instance as a parameter
hInstance = hInst;
// all messages for windows belonging to this Window Class will get sent to msgRouter
lpfnWndProc = AbstractWindow::msgRouter;
lpszClassName = className;
// --- set default values for the rest of the WNDCLASSEX structure --// --- later you can derive your own class and modify this behavior --lpszMenuName = 0;
cbSize = sizeof (WNDCLASSEX);
cbClsExtra = 0;
cbWndExtra = 0;
style = 0;
hIcon = ::LoadIcon (NULL, IDI_APPLICATION);
hIconSm = ::LoadIcon (NULL, IDI_APPLICATION);
hCursor = ::LoadCursor (NULL, IDC_ARROW);
hbrBackground = (HBRUSH) ::GetStockObject (COLOR_BTNFACE);

// --- the constructor won't call the Register () member function --// --- that was my choice, again, you can change the behavior in your code ---

bool SimpleWindowClass::Register ()
{
if (::RegisterClassEx (this)) // we pass the this pointer because our class inherits from
WNDCLASSEX
return true;

26-Oct-15 21:25

C++ Win32 API Simple GUI Wrapper | Inferno Development

4 of 6

else
}

http://www.infernodevelopment.com/c-win32-api-simple-gui-wrapper

return false;

Great! We've just simplified the Window Class creation from manually filling a
WNDCLASSEX structure and calling RegisterClassEx() to declaring a SimpleWindowClass
object and calling its Register() member function.
Now let's create a SimpleWindow class that will inherit from AbstractWindow and implement
the wndProc member function allowing our window to "work".

4. Create windows by subclassing AbstractWindow

The only thing left to do now is to inherit from AbstractWindow and create our window's
behavior by defining the wndProc pure virtual function.
Let's start:
// This is "SimpleWindow.h"
#include "AbstractWindow.h"

// include the AbstractWindow header file, needed for inheritance

class SimpleWindow : public AbstractWindow {


public:
// the constructor takes two arguments, the window's title, and the Window Class'
name
// the Window Class must be registered using the SimpleWindowClass object
// you can retrieve the Window Class name using the SimpleWindowClass::className ()
method
SimpleWindow (const TCHAR *windowName, const TCHAR *className);
procedure

};

// this is our window's procedure, you're gonna implement it like any other window
virtual LRESULT CALLBACK wndProc (HWND, UINT, WPARAM, LPARAM);
// shows the window on the screen and updates its client area
void Show () {
::ShowWindow (_hwnd, SW_SHOW);
::UpdateWindow (_hwnd);
}

Now we're gonna define the constructor and the window procedure. In this case the
constructor is just going to set default values for CreateWindowEx()'s parameters.
// This is "SimpleWindow.cpp"
#include "SimpleWindow.h"
SimpleWindow::SimpleWindow (const TCHAR *windowName, const TCHAR *className)
:
AbstractWindow ()
{
// --- we're going to assume the following members are declared in AbstractWindow as
protected --_windowName = windowName;
_className = className;
_hInstance = ::GetModuleHandle (NULL); // or you could've passed the HINSTANCE as a
constructor parameter
_style = WS_OVERLAPPEDWINDOW | WS_VISIBLE;
_y = _x = CW_USEDEFAULT;
_height = _width = CW_USEDEFAULT;
_styleEx = 0;
_hwndParent = 0;
_hMenu = 0;
// --- again those were supposed to be protected members of AbstractWindow --}
// our window procedure, this is were we define our window's behavior
LRESULT CALLBACK SimpleWindow::wndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
::MessageBox (hwnd, TEXT("Window has been successfully created"),
TEXT("Succes"), MB_OK);
return 0;
case WM_DESTROY:
::PostQuitMessage (0);
return 0;
default:
return ::DefWindowProc (hwnd, message, wParam, lParam);
}
}

By this time we've drastically reduced WinMain's size.

Your New Simple WinMain


In the new WinMain we'll just create a Window Class by creating a SimpleWindowClass
object and calling its Register() member function, then we're gonna create a window by
creating a SimpleWindow object, and calling its Create() and Show() member functions. Of
course we're also going to need a message loop.
#include "SimpleWindowClass.h"
#include "SimpleWindow.h"

// create a Window Class


// create a window

int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
// create the Window Class
SimpleWindowClass wndClass (hInstance, TEXT ("My window class"));
wndClass->Register ();

26-Oct-15 21:25

C++ Win32 API Simple GUI Wrapper | Inferno Development

5 of 6

http://www.infernodevelopment.com/c-win32-api-simple-gui-wrapper

// create the window,


SimpleWindow wnd (TEXT ("Generic Window Title"), wndClass->className ());
wnd->Create();
// show the window on the screen
wnd->Show ();

// pump messages:
MSG msg;
int status;
while ((status = ::GetMessage (&msg, 0, 0, 0 )) != 0)
{
if (status == -1) {
// handle the error, break
break;
}
::TranslateMessage (&msg);
::DispatchMessage (&msg);
}
return msg.wParam;

That's all there is to it. From now on there are endless possibilities. You can modify your
window wrapper or your Window Class wrapper the way you want them to work.
An interesting thing you could do is add message handlers, such as OnCommand (),
OnCreate (), OnPaint () and so on. You would need to define their default behavior in the
AbstractWindow class and then in the msgRouter you would forward each message to its
handler. Then in your derived classes you would only define handlers for the messages
you'd like to handle, the rest will be handled by the base class, that is, AbstractWindow. The
final result would be an MFC-like window class, with member functions for each message,
this way you would get rid of the window procedure for ever.
Let me show you what I mean:
/* ---- AbstractWindow.h ---- */
class AbstractWindow {
// --- previous class definition here --public:

};

virtual bool OnCommand (int ctrlId, int notifyCode) { return false; }


virtual bool OnDestroy () { ::PostQuitMessage (0); return false; }
virtual bool OnClose () { return false; }
// --- and so on, add as many handlers as necessary --// --- and define their default behavior --// --- usually they return 0, but check MSDN for details ---

/* ---- AbstractWindow.cpp ---- */


LRESULT CALLBACK AbstractWindow::msgRouter (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
{
AbstractWindow *wnd = 0;
if (message == WM_NCCREATE) {
wnd = (AbstractWindow *) ((LPCREATESTRUCT(lParam))->lpCreateParams);
::SetWindowLong (hwnd, GWL_USERDATA, long(wnd));
wnd->OnNCCreate (/* params */);
} else {
wnd = (AbstractWindow *) (::GetWindowLong (hwnd, GWL_USERDATA));
if (wnd) {
switch (message)
{
case WM_COMMAND:
return OnCommand (LOWORD (wParam), HIWORD(wParam))
case WM_DESTROY:
return OnDestroy ();
case WM_CLOSE:
return OnClose ();
// --- and so on, it'll be a lot of work to do,
// but most apps don't use every window message --default:
}
else

// for the messages you did not define any handlers


return ::DefWindowProc (hwnd, message, wParam, lParam);

// for messages that arrive prior to WM_NCCREATE


// and the HWND -> AbstractWindow * association was not made
return ::DefWindowProc (hwnd, message, wParam, lParam);

In your derived class instead of defining the old long window procedure you will only define
handlers for the messages you'd like to handle, the rest of the messages will either be
handled by their default handler implementation in AbstractWindow or by DefWindowProc()
in msgRouter.
class SimpleWindowClass : public AbstractWindow {
// --- rest of code here --// --- constructors, private data, etc etc...
public:
// there is no need for a window procedure now, just add handlers.
virtual bool OnCommand (int ctrlId, int notify Code) { /* do stuff */ }
virtual bool OnCreate () { ::MessageBox (_hwnd, TEXT("Window created"), TEXT
("Success"), MB_OK); }
// and so on, every message you want to handle must have a default handler in
AbstractWindow
// and you're going to implement it here, change its behavior the way you needs
};

Enjoy!

26-Oct-15 21:25

C++ Win32 API Simple GUI Wrapper | Inferno Development

6 of 6

http://www.infernodevelopment.com/c-win32-api-simple-gui-wrapper

Bookmark/Search this post with:

Quote

Thanks, This Helped A Lot!


Mar 8, 2010
Jesse (not
verified)
Reply

Thanks, this helped a lot!

Quote

It'd Be Nice If There's A


May 8, 2010
Anonymous
(not verified)
Reply

it'd be nice if there's a downloadable code

Quote

Post new comment

Your name:
Anonymous
E-mail:

The content of this field is kept private and will not be shown publicly. If you have a Gravatar account
associated with the e-mail address you provide, it will be used to display your avatar.

Homepage:

Comment: *

Save

Preview
Home | Forum | Contact | About

Copyright Inferno Development 2008-2013. All Rights Reserved.

26-Oct-15 21:25

S-ar putea să vă placă și