Click here to Skip to main content
Licence 
First Posted 20 Feb 2006
Views 30,220
Downloads 237
Bookmarked 18 times

A Minimal Windows Wrapper

By RandomMonkey | 20 Feb 2006 | Unedited contribution
An overview of a basic Windows wrapper

1

2

3
1 vote, 20.0%
4
4 votes, 80.0%
5
4.52/5 - 5 votes
μ 4.52, σa 0.78 [?]

Introduction

This article shows and discusses a minimal and functional Windows wrapper.  The wrapper presented in this article is the core upon which DWinLib is built, but shows nothing beyond that core.  This article is therefore only useful as an introduction to the inner workings of DWinLib, as well as showing how Windows may be wrapped.

The above zip file contains a 'Tic-Tac-Toe' game built with this bare wrapper.  The code in the zip file shows a method of extending the code presented in this article, but that method is not the method DWinLib took.

This article is meant to be read as part of the DWinLib tutorial of the above link, but will stand upon its own, and does not require other background material.

Other Wrappers

There are three other Windows API wrapping tutorials that I am aware of: Relisoft's, Oluseyi Sonaiya's, and David Nash's SDI Framework.  There is also the Win32 Generics wrapper, but that site does not appear to try to show you how it is accomplished.  In addition, it requires high-level template magic to accomplish its tricks.

The Differences

For those of you who have read the previously mentioned articles, DWinLib differs from them in insignificant to significant ways.  (For those who don't have any background creating Window wrappers, skim through and pick back up at the next section.)

DWinLib differs from all of the previous wrappers in the manner in which the window class instance is stored during the creation of the window.  There will be a little more on this later in the article.

Of the previously mentioned wrappers, DWinLib works in a manner that is most similar to Oluseyi's wrapper, but DWinLib uses the GetWindowLong/SetWindowLong approach to determining the appropriate window class to call for any given message, rather than searching a std::map.

DWinLib (and Oluseyi's method, for that matter) is much more flexible then Relisoft's method, in the fact that DWinLib does not require you to define handlers for each individual Windows message in the primary static window procedure.  Rather, you can override the entire non-static, member window procedure for a window, and add the desired functionality to the derived class without the parent class knowing anything about the additional messages you may be handling.  (It is easier to handle it other ways in DWinLib, but it *is* possible.)

(David Nash's method is also more flexible than Relisoft's method, but David's CWin::WndProc does not handle very many messages, meaning that you will have to look up many more WPARAM and LPARAM meanings each time you override that procedure.)

And compared to David Nash's SDI framework, DWinLib handles MDI applications.  DWinLib could be pared down to handle SDI applications, although this has not been needed so far, so it has not been done yet.

Wrapping a Window

The basic problem you will encounter when wrapping a Window procedure in an object oriented language is that of going from a static function to a non-static member function of an object.  This is necessitated by the fundamental design of Windows itself, and its 'C' legacy.  Windows is designed so that the function Windows calls in order to tell your program that things are happening must be a static, non-member function of your program.

Let us examine a window program that illustrates this difficulty, and see how it is solved in DWinLib.  The following is one of the simplest of window programs - it simply displays a window until the 'Close' button is pressed.  It doesn't use a wrapper of any nature.  You will note that I have outlined five distinct steps that take place in the code via comments.

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE hInst;

// **************************************
// Step 1: Create main program entrance:
// **************************************
int WINAPI WinMain(HINSTANCE instance, HINSTANCE , PSTR , int show) {
   // ***********************************
   // Step 2: Register the Window Class:
   // ***********************************
   static TCHAR appName[] = TEXT("MinApplication");
   hInst = instance;
   WNDCLASS wndclass;
   wndclass.style         = CS_HREDRAW | CS_VREDRAW;
   wndclass.lpfnWndProc   = WndProc;
   wndclass.cbClsExtra    = 0;
   wndclass.cbWndExtra    = 0;
   wndclass.hInstance     = instance;
   wndclass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
   wndclass.hCursor       = LoadCursor(NULL, IDC_ARROW);
   wndclass.hbrBackground = (HBRUSH) GetStockObject(GRAY_BRUSH);
   wndclass.lpszMenuName  = NULL;
   wndclass.lpszClassName = appName;

   if (!RegisterClass(&wndclass)) {
      MessageBox(NULL, TEXT("Oh no, Mr. Bill...."), appName, MB_ICONERROR);
      return 0;
      }

   // ***************************
   // Step 3: Create the window:
   // ***************************
   HWND hwnd = CreateWindowEx(WS_EX_APPWINDOW, appName, TEXT("MinApplication"),
               WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW | WS_BORDER,
               CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
               NULL, NULL, instance, NULL);

   ShowWindow(hwnd, show);
   UpdateWindow(hwnd);

   // ********************************
   // Step 4: Enter the message loop:
   // ********************************
   MSG msg;
   while (GetMessage(&msg, NULL, 0, 0)) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
      }
   return msg.wParam;
   }


// ******************************************
// Step 5: Handle the messages from Windows:
// ******************************************
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
   switch (message) {
      case WM_DESTROY :
         PostQuitMessage(0);
         return 0;
      }
   return DefWindowProc(hwnd, message, wParam, lParam);
   }

One of the difficulties in programming a non-trivial application in the above manner is that a great deal of time is spent creating and managing the static WndProc procedures.  It is also necessary to tie your application's logic and data into the WndProc procedures, and this can be quite a task for a non-trivial application.

Below is the previous program re-written using the same basic mechanism that DWinLib uses.  It is almost four times larger than the preceding, code-wise, although it compiles to 52 Kb, vs. 48 Kb for the non-wrapped version.  It can be cut-and-pasted into a blank project and compiled as-is.

As you peruse the code, keep the following in mind:

The code works by creating a WinApp unit to hold the main static callback.  A WinBaseO unit is defined as a virtual base class from which to derive actual windows.  (No member function screams out to be made into a pure virtual, therefore only common sense is to keep you from instantiating a WinBaseO object.)

When a WinBaseO object is instantiated, it registers the fact that it is being instantiated with the WinApp from within the WinBaseO constructor.  The classes derived from WinBaseO override any WinBaseO functions required, in order to handle the Windows specific functions (WM_PAINT, etc.) in a manner you desire.  You can also add functions of your own to the derived classes, to handle your own logic unrelated to the window logic if you need to.

In this example, only a WinMainO unit is derived from WinBaseOWinMainO is a required unit within DWinLib applications, and contains the main window to your application.  In a non-trivial application you may have many more windows derived from WinBaseO.

All in all, this is a simple approach, and it works well.

You will note that the same five steps have been commented as were previously pointed out, although there are a couple more comments thrown in.

You will also note that the fifth step has been broken into three parts.  The first part (5 - just as in the previous code) is a static message handler that is part of the WinApp unit.  The second part (5a) is a non-static, member function of the WinBaseO unit, used to define default handlers for the Window messages.  The third part (5b) overrides any window message handlers for specific use by your window, when the default handler will not do the task you want it to.

The other comments are thrown in to indicate program structure unrelated to the outlined steps.

#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x0500
#define _WIN32_IE 0x0400

#include <tchar.h>
#include <windows.h>
#include <string>

class WinBaseO;

// *************************************************************************
// Define a 'WinApp' class to handle the main static window procedure.
// There will only be one of these instantiated in the program as a global.
// *************************************************************************
class WinApp {
   private:
      static WinBaseO * winBeingCreatedC;
      HINSTANCE hInstC;
   public:
      WinApp(HINSTANCE hInst_, HINSTANCE, LPSTR, int);
 
      LRESULT static CALLBACK WindowProc(HWND window, unsigned int msg,
                  unsigned int wParam, long lParam);
      HINSTANCE hInst() { return hInstC; }
      void   setWinBeingCreated(WinBaseO * win) { winBeingCreatedC = win; }
      WPARAM run();
   };


// **********************************************************
// Define a base window class for deriving real windows from
// **********************************************************
class WinBaseO {
   //For dealing with the window handle and window procedure
      protected:
         HWND hwndC;
      public:
         WinBaseO();
         ~WinBaseO();
         
         //winProc can be overridden for each window, but it is easiest to just
         //override the individual functions.
         virtual LRESULT winProc(HWND window, UINT msg, WPARAM wParam, LPARAM lParam);
         HWND hwnd() { return hwndC; }
         
         void hwnd(HWND hwnd) { hwndC = hwnd; } //Only needed by WinApp::WindowProc

   //Routines to be inherited by derived windows
      public:
         virtual LRESULT wClose() { return DefWindowProc(hwndC, WM_CLOSE, 0, 0); }
   };


WinBaseO * WinApp::winBeingCreatedC = NULL;
WinApp * gWinApp; //A Global WinApp object for the entire program


// ************************************
// Now fill in the WinBaseO definition
// ************************************
WinBaseO::WinBaseO() : hwndC(NULL) {
   gWinApp->setWinBeingCreated(this);
   }


WinBaseO::~WinBaseO() {
   if (IsWindow(hwndC) == TRUE) DestroyWindow(hwndC);
   }
   
   
// ***************************************************************************
// Step 5a: Handle the messages from Windows in a non-static member function:
// ***************************************************************************
LRESULT WinBaseO::winProc(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) {
   switch (msg) {
      case WM_CLOSE:
         wClose();
         return 0;
      }
   return DefWindowProc(window, msg, wParam, lParam);
   }


// ***************************************************
// Now define and fill in the guts to a 'real' window
// that uses the above WinBaseO definitions.
// ***************************************************
class WinMainO : public WinBaseO {
   //Regular class stuff
      private:
         void registerWinClass();

      public:
         WinMainO();
         
   //WinBaseO overrides:
      public:
         virtual LRESULT wClose();
   };


namespace { //An unnamed namespace for this unit to use
   TCHAR * winCaption = TEXT("Bare Windows Wrapper Example");
   TCHAR * winClassName = TEXT("BareWindowApp");
   }


// ***********************************
// Step 2: Register the Window Class:
// ***********************************
void WinMainO::registerWinClass() {
   WNDCLASS wc;
   wc.style         = CS_HREDRAW | CS_VREDRAW;
   wc.lpfnWndProc   = gWinApp->WindowProc;
   wc.cbClsExtra    = 0;
   wc.cbWndExtra    = 0;
   wc.hInstance     = gWinApp->hInst();
   wc.hIcon         = NULL;
   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
   wc.hbrBackground = (HBRUSH)GetStockObject (GRAY_BRUSH);
   wc.lpszMenuName  = NULL;
   wc.lpszClassName = winClassName;
   if (!RegisterClass (&wc)) abort();
   }
 

WinMainO::WinMainO() {
   static bool registered = false;
   if (!registered) {
      registerWinClass();
      registered = true;
      }

   // ***************************
   // Step 3: Create the window:
   // ***************************
   hwndC = CreateWindow(winClassName, winCaption, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
               CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, gWinApp->hInst(), 
               NULL);
   if (!hwndC) abort();

   ShowWindow(hwndC, SW_SHOW);
   UpdateWindow(hwndC);
   }


// *************************************************
// Step 5b: Override any message handlers you want
// to handle differently than the WinBaseO handler:
// *************************************************
LRESULT WinMainO::wClose() {
   PostQuitMessage(0);
   return DefWindowProc(hwndC, WM_CLOSE, 0, 0);
   }
 

// **********************************
// Now fill in the WinApp definition
// **********************************
WinApp::WinApp(HINSTANCE hInst_, HINSTANCE, LPSTR, int) :
            hInstC(hInst_) {

   gWinApp = this;
   }
 

WPARAM WinApp::run() {
   WinMainO win;
   // ********************************
   // Step 4: Enter the message loop:
   // ********************************
   MSG msg;
   while (GetMessage(&msg, NULL, 0, 0)) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
      }
   return msg.wParam;
   } 


// ******************************************
// Step 5: Handle the messages from Windows:
// ******************************************
LRESULT CALLBACK WinApp::WindowProc(HWND window, unsigned int msg, unsigned int wParam,
            long lParam) {

   WinBaseO * win = reinterpret_cast<WinBaseO*>(GetWindowLong(window, GWL_USERDATA));
   if (win) return win->winProc(window, msg, wParam, lParam);
   SetWindowLong(window, GWL_USERDATA, reinterpret_cast<long>(winBeingCreatedC));
   winBeingCreatedC->hwnd(window);
   return winBeingCreatedC->winProc(window, msg, wParam, lParam);
   }


// ***************************************************************
// And finally, a WinMain that takes advantage of the above work:
// ***************************************************************
// **************************************
// Step 1: Create main program entrance:
// **************************************
int WINAPI WinMain(HINSTANCE inst1, HINSTANCE inst2, LPSTR str, int show) {
   WinApp app(inst1, inst2, str, show);
   return app.run();
   }

The above code has been kept to a minimal state, in that error handling is very minimal, and all extraneous messages are ignored.  The previous code is simply enough to show how a basic Windows wrapper can be accomplished.

Earlier I stated that there would be more information on how the window class instance is stored during the creation of the window.  As I stated before the second code block, the WinBaseO's constructor tells the global WinApp object that it is being constructed.  If you examine the WinApp::WindowProc function, you will see how this is actually accomplished, and how the incoming Window messages are routed to the appropriate member function.  Much thought was given to that aspect of the functioning of DWinLib before settling upon this method.

I also stated earlier that it can be quite painful to tie your application's logic and data into the WndProc procedures of a pure API program.  Using the above wrapper, it is no longer difficult to tie your data into a window: simply put it in the class definition of the window derived from WinBaseO and use it as you would use data in any C++ class.  The application logic also flows much easier in a program written using a wrapper, if you are conversant in object oriented techniques.

If you want to see the above method in action, you can either cut-and-paste the previous code into a blank project, or you can examine the above zip file, and see how a Tic-Tac-Toe program can be put together modularly with this wrapper.  The Tic-Tac-Toe code is significantly flushed out from the above code, in that many more messages are handled, exception handling has been included, and it is not as trivial as the above code.  The Tic-Tac-Toe program also wraps a couple of Windows Common Controls, and does so in one of the cleanest examples I have ever seen.  (I'm tooting my own horn here, as it was kinda painful extracting the information and getting it to work from the examples I could find.  Microsoft does not make great examples for some of the common controls, and I did not find much better in my limited search at the time I constructed that application back in about '02.)

Even though this is the framework that DWinLib is based upon at its core, DWinLib is far advanced from the preceding code.  There will be more on the internals of DWinLib, and what it does in addition to the above, in another article.

The End

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

RandomMonkey

Web Developer

United States United States

Member
Just another hardworking guy who also went by the name of David O'Neil once.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralWrapping Windows Common Controls PinmemberStone Free0:49 26 Sep '06  
GeneralRe: Wrapping Windows Common Controls PinmemberDavid O'Neil5:51 26 Sep '06  
GeneralNote about GWL_USERDATA PinsitebuilderMichael Dunn3:26 21 Feb '06  
GeneralRe: Note about GWL_USERDATA PinmemberRandomMonkey7:17 21 Feb '06  
GeneralRe: Note about GWL_USERDATA PinmemberJörgen Sigvardsson11:46 21 Feb '06  
GeneralRe: Note about GWL_USERDATA PinmemberRandomMonkey19:04 21 Feb '06  
GeneralRe: Note about GWL_USERDATA PinmemberJörgen Sigvardsson21:54 21 Feb '06  
GeneralRe: Note about GWL_USERDATA PinmemberRandomMonkey3:03 22 Feb '06  
Jörgen Sigvardsson wrote:
Safer in the sense of "The user will not take out his machete and cut you down like bamboo, by just fiddling with GWL_USERDATA".

 
They will know it fast enough Smile | :) It is one of the things pretty much guaranteed to show up on the very first debugging run! (Although there is an insignificant chance they would set it to the same value as the window object pointer, and walk away unscathed one time.)
 
Jörgen Sigvardsson wrote:
Here's something you might want to investigate...

 
FYI - you access it through Get/SetWindowLong(Ptr), specifying byte offset rather than a predefined constant. Yes, it could be very tricky subclassing this technique. I also don't know how it works with 64 bit apps, although I would 'assume' that MS made it read 64 bit values using Get/SetWindowLongPtr...
 
Thanks for the ideas (and laughs). Best wishes for your projects.
David
 

I've got a New Age attitude - @#&~ karma!

DWinLib - A neat little Windows wrapper
MEdit - A MIDI sequencer

GeneralRe: Note about GWL_USERDATA PinsitebuilderMichael Dunn12:36 21 Feb '06  
GeneralRe: Note about GWL_USERDATA PinmemberRandomMonkey19:10 21 Feb '06  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web01 | 2.5.120210.1 | Last Updated 20 Feb 2006
Article Copyright 2006 by RandomMonkey
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid