win32 window wrapper tutorial

6
Home C++ Win32 API Simple GUI Wrapper 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 ()); 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, Forum Community 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 IT Technology Humor Programming Web Dev CS Design C++ Win32 API Simple GUI Wrapper | Inferno Development http://www.infernodevelopment.com/c-win32-api-simple-gui-wrapper 1 of 6 26-Oct-15 21:25

Upload: nopafix

Post on 03-Dec-2015

21 views

Category:

Documents


1 download

DESCRIPTION

A tutorial about creating a C++ class for Win32 window management.

TRANSCRIPT

Page 1: Win32 window wrapper tutorial

Home

C++ Win32 API Simple GUI WrapperOct 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 asthis?

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 TutorialThe ProblemWindow WrapperThe Window Class WrapperYour New Simple WinMain

Simple C++ Win32 API Wrapper TutorialYou 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 abasic understanding of Win32 API.You'll notice the use of the C++ scope resolution operator when calling Win32 API functionssuch as ::CreateWindowEX () or ::RegisterWindowEx(). That's just a habit of mine whencalling these kind of functions, basically it tells C++ to search the functions in the globalnamespace.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 ());wnd.Create ();wnd.Show ();/* message loop */

Everyone knows that creating a window is not an simple task but a rather tedious one, so I'mgoing 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 yourselfwas: "What am I going to do with the window procedure?". I also bet this was the first answerthat crossed your mind: "I'm just going to declare it as a member function within my windowwrapper class and then I'm gonna pass it to the WNDCLASSEX structure as thelpfnWndProc field."Well, unfortunately that won't work. Why? Because member functions have an additionalhidden parameter, the this pointer, so instead of LRESULT CALLBACK wndProc (HWND,UINT, WPARAM, LPARAM) your member function will be LRESULT CALLBACKmemberWndProc (YourClassName *this, HWND, UINT, WPARAM, LPARAM).Obviously you won't be able to do the assignment wcx.lpfnWndProc = memberWndProcbecause 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 messagerouter. A static member function can be called without creating an instance of the class,

Forum Community

Search

Popular contentAll Time:

45 Incredible Futuristic Scifi 3DCity IllustrationsC++ Win32 API TutorialSingleton C++How to Make a Wireframe Renderin Cinema 4D (C4D)Harvard scientists discoverprotein GDF-11 that reversesheart disease in old mice

Get Free UpdatesEnter email to receive new posts:

SubscribeWe will never distribute your email address.

Updated ContentC++ Tutorials

Sign Up! | Login

IT Technology Humor Programming Web Dev CS Design

C++ Win32 API Simple GUI Wrapper | Inferno Development http://www.infernodevelopment.com/c-win32-api-simple-gui-wrapper

1 of 6 26-Oct-15 21:25

Page 2: Win32 window wrapper tutorial

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

First we create an abstract window class, let's call it AbstractWindow. Later, we are going tocreate 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;// ... other members ...

public: AbstractWindow () {}

// this will be WNDCLASSEX::lpfnWndProcstatic 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 msgRoutervirtual LRESULT CALLBACK wndProc (HWND, UINT, WPARAM, LPARAM) = 0;// calls CreateWindowEx, creating the windowvirtual bool Create ();// ... other member functions ...

};

Notice the pure virtual function wndProc, this is the actual window procedure that will beimplemented in derived classes, the static msgRouter function only determines the instanceof AbstractWindow to send the message to and calls its wndProc. How?

In the AbstractWindow::Create () method, we're gonna pass the this pointer as the lastparameter of CreateWindowEx().

1. Then, CreateWindowEx() sends it to msgRouter through a WM_NCCREATE message.(Details: the pointer is stored in the LPARAM parameter as a LPCREATESTRUCTstructure, in the lpCreateParams field. Not important)

2.

We then handle the WM_NCCREATE message and associate the AbstractWindowpointer with the window's HWND using SetWindowLong ().

3. Then, we retrieve the AbstractWindow pointer using GetWindowLong () and call itswndProc method that will do the actual processing.

4.

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 toretrieve the pointer to its AbstractWindow object that contains its window procedure, thenwe'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,

C++ Win32 API Simple GUI Wrapper | Inferno Development http://www.infernodevelopment.com/c-win32-api-simple-gui-wrapper

2 of 6 26-Oct-15 21:25

Page 3: Win32 window wrapper tutorial

// so we'll asociate its handle with its AbstractWindow instance pointer::SetWindowLong (hwnd, GWL_USERDATA,

long((LPCREATESTRUCT(lParam))->lpCreateParams));}// --- messages different from WN_NCCREATE / or WM_NCCREATE was just processed ---// we retrieve the instance of AbstractWindow that corresponds to the destination window's

HWND wnd = (AbstractWindow *) (::GetWindowLong (hwnd, GWL_USERDATA));

// we then route the message to the wndProc method defined in the derived AbstractWindowclass

if (wnd) wnd->wndProc (hwnd, message, wParam, lParam);

else// for messages that arrive prior to WM_NCCREATE// and the HWND <-> AbstractWindow * association was not madereturn ::DefWindowProc (hwnd, message, wParam, lParam);

}

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

Before creating a window we must first create a Window Class. I'm hoping that by this timeyou know what I mean by a Window Class. The following C++ class will wrap a WindowClass more precisely the WNDCLASSEX structure, the other one above wrapped awindow.There are several ways of doing this, but the only thing each implementation must have incommon 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' constructorneeds 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 classvirtual bool Register ();//retrieve the class namevirtual 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 thatretrieves the Window Class' name, a Register() member function that calls theRegisterClassEx() Win32 function to register the window class, and a constructor that will fillin 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 fromWNDCLASSEX

return true;

C++ Win32 API Simple GUI Wrapper | Inferno Development http://www.infernodevelopment.com/c-win32-api-simple-gui-wrapper

3 of 6 26-Oct-15 21:25

Page 4: Win32 window wrapper tutorial

elsereturn false;

}

Great! We've just simplified the Window Class creation from manually filling aWNDCLASSEX structure and calling RegisterClassEx() to declaring a SimpleWindowClassobject and calling its Register() member function.Now let's create a SimpleWindow class that will inherit from AbstractWindow and implementthe wndProc member function allowing our window to "work".4. Create windows by subclassing AbstractWindowThe only thing left to do now is to inherit from AbstractWindow and create our window'sbehavior by defining the wndProc pure virtual function.Let's start:

// This is "SimpleWindow.h"#include "AbstractWindow.h" // include the AbstractWindow header file, needed for inheritanceclass 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);

// this is our window's procedure, you're gonna implement it like any other windowprocedure

virtual LRESULT CALLBACK wndProc (HWND, UINT, WPARAM, LPARAM);// shows the window on the screen and updates its client areavoid Show () {

::ShowWindow (_hwnd, SW_SHOW);::UpdateWindow (_hwnd);

}};

Now we're gonna define the constructor and the window procedure. In this case theconstructor 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 asprotected --- _windowName = windowName; _className = className; _hInstance = ::GetModuleHandle (NULL); // or you could've passed the HINSTANCE as aconstructor 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 behaviorLRESULT 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 SimpleWindowClassobject and calling its Register() member function, then we're gonna create a window bycreating a SimpleWindow object, and calling its Create() and Show() member functions. Ofcourse we're also going to need a message loop.

#include "SimpleWindowClass.h" // create a Window Class#include "SimpleWindow.h" // create a windowint APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){

// create the Window Class SimpleWindowClass wndClass (hInstance, TEXT ("My window class")); wndClass->Register ();

C++ Win32 API Simple GUI Wrapper | Inferno Development http://www.infernodevelopment.com/c-win32-api-simple-gui-wrapper

4 of 6 26-Oct-15 21:25

Page 5: Win32 window wrapper tutorial

// 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, breakbreak;

}::TranslateMessage (&msg);::DispatchMessage (&msg);

}return msg.wParam;

}

That's all there is to it. From now on there are endless possibilities. You can modify yourwindow 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 theAbstractWindow class and then in the msgRouter you would forward each message to itshandler. Then in your derived classes you would only define handlers for the messagesyou'd like to handle, the rest will be handled by the base class, that is, AbstractWindow. Thefinal 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: // for the messages you did not define any handlers

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

}else // for messages that arrive prior to WM_NCCREATE

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

}}

In your derived class instead of defining the old long window procedure you will only definehandlers for the messages you'd like to handle, the rest of the messages will either behandled 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!

C++ Win32 API Simple GUI Wrapper | Inferno Development http://www.infernodevelopment.com/c-win32-api-simple-gui-wrapper

5 of 6 26-Oct-15 21:25

Page 6: Win32 window wrapper tutorial

Mar 8, 2010Jesse (notverified)

May 8, 2010Anonymous(not verified)

Bookmark/Search this post with:

Quote

Thanks, This Helped A Lot!

Thanks, this helped a lot!

Reply QuoteIt'd Be Nice If There's A

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

Reply QuotePost new commentYour name:AnonymousE-mail:

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

Comment: *

Save Preview

Home | Forum | Contact | AboutCopyright © Inferno Development 2008-2013. All Rights Reserved.

C++ Win32 API Simple GUI Wrapper | Inferno Development http://www.infernodevelopment.com/c-win32-api-simple-gui-wrapper

6 of 6 26-Oct-15 21:25