Click here to Skip to main content
Click here to Skip to main content

DWinLib - The Guts

, 14 Feb 2013
Rate this:
Please Sign up or sign in to vote.
A little about how things work behind the scenes in DWinLib!

The following is an overview of the internal workings of DWinLib, a semi-simple wrapper for the Windows API. The other articles in this series include:

  1. DWinLib - An Overview
  2. Setting up Visual Studio and building a minimal Windows’ wrapper
  3. Compiling a Minimal DWinLib Program
  4. DWinLib - The Guts - You are here
  5. Modal Dialogs - A DWinLib Approach

Introduction

This writing illuminates some of the internal design of DWinLib, and discusses my immediate thoughts regarding the implementation after having used it in personal projects. I hope talking about these pros and cons enables others who are interested to go further in their own programming knowledge and expertise, and leads to better tools for everyone, even if they don’t make their way to DWinLib.

Of course my desire is for DWinLib to gains those benefits, but with all the options programmers now possess, and a fixed number of hours in a day, such may not be the case. For me, DWinLib came about because a bug in Borland Builder clobbered MEdit. I needed to understand enough of the Windows API to fix the problem and get back to adding features - something I have not done in a while, and I hope finishing these articles leads to more time for that.

(If you’re asking why I’m writing instead of programming, one answer is the effort of creating the sample executables has been a type of test, and proven that the underlying code is in good shape, and helped me flush out some bugs. Another is I want to finish what I started, and the state of the articles from 2006 has bothered me for far too long.)

In my mind the currently interesting task is the creations which can be made with this framework. The interest in DWinLib as a thing in itself is passing, and I hope to get back to MEdit. I will do my best to make any improvements I create during the process freely available, though, and if it catches on the library will be placed on SourceForge, or another code repository so others can more easily have fun.

The Heart of the Matter

As pointed out in an earlier article, the central problem encountered when creating a Window wrapper is going from a static function to a class member function. The linked writing used ‘GetWindowLong’ and ‘SetWindowLong’ to accomplish the magic, and some astute CodeProject members pointed out the approach is not thread safe.

Back then I didn’t have time to respond, but I did keep their feedback in mind as I continued on. Eventually I looked into the method David Nash used in his pretty awesome little wrapper, and adapted it to my needs. Of course, such an endeavor is never straightforward, and if you compare his to the following you will notice some changes.

// --------------------------------------------------------------------------------------- //
// The main application message loop callback.  This is used by almost every window.  A    //
// notable exception: all Windows controls use their own callback procedure.  See          //
// DwlControlWin and derived units for examples of how that processing is wrapped in       //
// DWinLib.  This is a static function.                                                    //
// --------------------------------------------------------------------------------------- //
LRESULT CALLBACK DwlBaseApp::WinProc(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) {
   try {
      #ifdef DWINLIB_THREADED
         WinCriticalSection cs;
         cs.lock();
         #endif
      std::map<HWND, DwlBaseWin*>::iterator it = gWinApp->windowsC.find(window);
      #ifdef DWINLIB_THREADED
         cs.release();
         #endif
      if (it!=gWinApp->windowsC.end())
         return it->second->winProc(window, msg, wParam, lParam);
      else {
         DwlBaseWin * tempWin = r_cast<DwlBaseWin*>(TlsGetValue(gWinApp->tlsIndexC));
         #ifdef DWINLIB_THREADED
            cs.lock();
            #endif
         gWinApp->windowsC.insert(std::make_pair(window, tempWin));
         #ifdef DWINLIB_THREADED
            cs.release();
            #endif
         return tempWin->winProc(window, msg, wParam, lParam);
         }
      }
   catch (DwlException & e) {
      wString str = _T("Programming Error: ") + e.strC;
      str += _T("\n\nPlease report this error and the steps taken to obtain");
      str += _T("\nit to the program's author.");
      if (e.continuableC)
         str += _T("\nWill attempt to continue (program may become unstable)");
      else str += _T("\n\nProgram must exit.  Sorry.");
      MessageBox(gDwlMainWin->hwnd(), str.c_str(), _T("Error"), MB_OK);
      if (!e.continuableC) exit(EXIT_FAILURE);
      }
   catch (std::exception & ) {
      int wish = MessageBox(gDwlMainWin->hwnd(),
                  _T("std::exception caught - Abort?\n(Program may be unstable if NO)"),
                  _T("Error"), MB_YESNO);
      if (wish == IDYES) abort();
      }
   catch (...) {
      wString str = _T("Exception caught\n");
      str += _T("Last Windows Error:\n  ");
      str += dwl::lastSysError();
      str += _T("\nAbort?\n(Program may be unstable, but we will try if you want!)");
      int wish = MessageBox(gDwlMainWin->hwnd(), str.c_str(), _T("Error"),
                  MB_YESNO);
      if (wish == IDYES) abort();
      }
   return 0;
   }

Looking at the code, I see I haven’t used the threaded version in a long time. In fact, DWINLIB_THREADED isn’t defined anywhere in the files, not even as a comment, so I will fix this. It has now been added to DwlTypedefs.h; if you wish to play with threading just remove the appropriate "//" in that file.

The previous snippet is copied from DwlApp.cpp. As you probably guess, DwlApp is the main dispatcher for almost all Window messages received by a DWinLib program. (The comment at the top of the code explains the ‘almost all’ stipulation.) This unit is instantiated in the WinMain routine:

...
DwlMdiApp app(inst1, inst2, str, show);
WPARAM ret = app.run();

CloseHandle(mutex);  //Technically, you don't need to do this. Windows will close it
                    //automatically upon process termination.
return s_cast<int>(ret);

If you peruse the DwlApp unit, you will find the DwlMdiApp is derived from a DwlBaseApp, as is a DwlSdiApp.

This dual-derivation for MDI/SDI programs (the acronym stands for Multiple Document Interface / Single Document Interface, for those who are unfamiliar with the terminology) is one of the portions of DWinLib I’m a little unsatisfied with. The reason for my unrest is I recall looking at the logic for the VCL (Borland’s Visual Class Library), and don’t believe it used inheritance in this manner. Also, from the memory of an old version of David Nash’s code, he didn’t either (although a look into a newer one indicates he does handle MDI a bit differently, and my recollection may be incorrect). So I might have missed a trick - something slightly irking to a perfectionist.

Another reason exists for unease. Windows (as in actual thingies you create) don’t work quite the same in MDI and SDI. More boilerplate logic is necessary to accomplish feature completion in both environments the way DWinLib is set up. This seems like a needless chore, and code duplication. Some of this repetition is seen in the DwlMdiBaseWin unit. It is basically a copy of DwlBaseWin. Another redundancy is witnessed in DwlApp.cpp, which contains message loops for MDI and SDI programs. They could be integrated into one routine with the use of some boring, ugly, if statements, or preprocessor #ifdefs.

I hear a few readers (or maybe just Microsoft) saying MDI is dead, and for those who are in that camp all I can do is point to Adobe’s results. In the link their spokesman said they were listening to their users who demanded MDI back, but Adobe never did, and more than one thread may be found complaining about the problem. I don’t think the issue has gone away, even with taskbar icon stacking.

Oh yeah, Microsoft Excel 2010 is also firmly MDI based, so the framework will not die soon.

In my own use, MDI is the cleanest method I’m aware of which does what I want. Perhaps this makes me an old-school curmudgeon, but I really dislike having windows floating all over the place, and don’t wish it upon my users. The gimp - once notorious for this - finally moved their windows into one master (or so some have said, but their screenshots don’t inspire confidence in a well-implemented rework). MDI is just a cleaner design, although I am aware of a few difficulties, and won’t claim the approach is perfect.

Anyway, getting back to programming, the primary reason for dually deriving MDI and SDI applications is MDI programs (as implemented by Windows) flow slightly differently, and some windows return different values.

When constructing an MDI program, during the initialization of the main window an MDICLIENT HWND must be created while processing the WM_CREATE message. The initiator also needs to return DefFrameProc results for the messages it process, whereas the MDI children have to return results from DefMDIChildProc. Everything gets rather convoluted and confusing, but concludes in the desired end product.

To make this aspect of DWinLib a bit easier to understand, the following is a chart showing the outline of an MDI program. To be honest, since MEdit uses MDI, this will be the area I concentrate my efforts on, even though at one time I pretty much had the same functionality using only an SDI framework.

(Interestingly, when that was working a hundred children could be opened, and upon closing, the program shut down almost instantly. With the Windows standard method, termination takes much longer. Unfortunately a few issues existed with the child window Minimize/Restore/Close buttons, and I couldn’t figure out how to overcome them without some serious and time-consuming hacking, as far as I could tell.)

If you wish to use DWinLib as the basis of an SDI program, the DwlMdiManager unit will need to be stripped out. Then everything which deals with DwlMainWin must be modified to work directly with WinMainO’s hwndC. Beyond that I cannot be of much more assistance until I come across a situation where I need to do so for myself, as I haven’t played with SDI in quite a while. My focus has been on MEdit, and it has always been MDI, even in an SDI framework.

Here’s the graphic:

DWinLib Class View

Associating Windows messages to specific windows

The next obstacle to overcome is linking messages to the correct window. DWinLib’s solution is slightly different than the previously mentioned wrappers.

As seen above, the actual forwarding occurs in the static Win[Base/Mdi/Sdi]App::WndProc function. This is the main callback for all your program’s windows. (Remember: standard Windows controls possess their own procedure, so they don’t call into WinApp::WndProc without additional work. And creating such code isn’t recommended.) The magic line in the previous function, which routes the message to the appropriate window within the program, is “return it->second->winProc(window, msg, wParam, lParam);”.

At that point the executable accesses the vtable for the correct DwlBaseWin object and executes its winProc procedure, sending along the message parameters.

Here is one area where DWinLib shines in comparison to some wrappers. Those messages are ‘unwrapped’ in the winProc procedures, making the code to handle them much simpler.

The trick behind this is simply a bunch of tedium. This snippet illustrates the technique:

LRESULT DwlMdiBase::winProc(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) {
   //Per Microsoft, all exceptions must be handled before returning control back to
   //the message pump.  For some information on this, see the bottom part of the article:
   //http://www.microsoft.com/msj/archive/S204D.aspx
   wParamC = wParam; //The windows derived from DwlBaseWin can access the most current
   lParamC = lParam; //wParam and lParam variables through the class members
   msgC = msg;
   switch (msg) {

      //Button routines
      case WM_LBUTTONDOWN:
         return wMouseDown(LeftButton, wParam, LOWORD(lParam), HIWORD(lParam));

      case WM_LBUTTONUP:
         return wMouseUp(LeftButton, wParam, LOWORD(lParam), HIWORD(lParam));
         
      ...

Then, wMouseDown is simply defined as:

virtual LRESULT wMouseDown(Button button, WPARAM flags, SHORT x, SHORT y);

Eureka! You no longer need to write your handlers using the arcane ‘LOWORD’ and ‘HIWORD’ syntax. Yay! ‘LeftButton’ sure beats ‘wParam’!

I suppose I should say a brief word (or three) about Hungarian notation. I hate it.

You might be getting a different impression from the previous code. But those who understand Hungarian know the ‘w’ in front of ‘MouseDown’ isn’t being used in a Hungarian manner.

Instead, it is only stating the function handles a ‘Windows’ message. The letter can also indicate the attached variable is tightly linked to Windows. To see the reason why, take a look at all the messages handled in DwlMdiBase. Try to imagine the appearance of your own code without a convention to set those functions apart. You would have an ‘enable’ routine floating alone in space, with very little information to convey purpose. Heaven forbid you want to later use a logical name like ‘enable’ for something else.

An option to eliminate the ‘w’ is to wrap DWinLib in a namespace, such as ‘dwl.’ Then everything may be prepended with ‘dwl::,’ (ugly) or a ‘using’ statement can be placed at the top of the .cpp file – and ‘enable’ once again becomes obfuscated.

My other standard is capital ‘C’s at the end of class member variable names. I agree with many coders that pointing these out is a necessity, but the prepended ‘m_’ is just an atrocious-looking monstrosity, and more difficult to type than a ‘C.’ Also, the member variableness isn’t so important it must come before the meaning. In other words my mind groks “I encapsulate the idea of wParam, and I just happen to be a member variable” better then “I’m a member variable! which encapsulates the idea of wParam.” (The bolding is an attempt to show the ‘m_’ jumps out that way to me, and drowns out the primary information.) But maybe my grey cells work differently other people’s. Regardless, the main item is the logic gets written, and is functional.

Of course, then the question becomes “Why doesn’t the ‘w’ come after the variable name?” That is probably a relic of Hungarian notation influence. When I adopted this convention I hadn’t thought through the issue. I also use a ‘g’ to indicate ‘globals,’ and with only those two prefixes the code seems clean to me. Perhaps someday I will move all these to the end of the word, but I must admit I like the alphabetization aspect to the current scheme, so maybe I won’t.

Responding to User Input

Callbacks

Let us return to code, rather than theory. We have seen how messages get dispatched to the appropriate classes, and the mechanism used to unpack WPARAM and LPARAM, to simplify the programming task. The next item to peruse is DWinLib’s callback feature. When you press a button in your program, this is the engine which routes the event to the approprate handler.

With or without a wrapper, the button’s parent window receives a WM_COMMAND message containing the ID of the control. In plain API code, that number must be assigned by you or your resource editor.

From there, just test for the value in the WM_COMMAND logic and respond appropriately. Pretty simple, except when the codebase gets big the entire process is orders of magnitude beyond cumbersome.

DWinLib vastly simplifies this part of the task. Buttons and other control items are wrapped in classes deriving from DwlControlWin. That class contains a DwlCallbackItem as a member. Whenever one of these objects is created it registers itself with a program-wide DwlCallbackList instantiated in the DwlBaseApp unit. Upon doing so it receives a control ID. You don’t need to think about these numbers any more.

The solution to the next part of the problem is heavily influenced by Borland Builder. In it, to make a button call into a class instance, just assign an ‘OnClick’ handler and you are done. Except for the actual coding of the function.

When I originally ported MEdit to resolve the bug I witnessed, I wanted something similar to Builder’s simple and easy-to-use callback mechanism so I didn’t have to totally redo this aspect of the processing along with everything else. Non-portable extensions, such as their ‘__closure’ (or whatever the term was) were unacceptable.

My first solution involved static functions and base class pointers, and I’m proud to say I got the design working, but I’m honest enough to admit it wasn’t very pretty. I’ve forgotten the details, but the method of non-static lookup, although doable, was not something I will expose your innocent eyes to.

Actually, pulling up some old code, it’s not quite as bad as I’m acting. I’ve changed my mind - your eyes can handle it. Here’s the beginning of the ‘redo’ process in MEdit long ago:

void WinMainO::redo(WinObject * ) {  //This is a static function
   Performance * perf(gWinMain->perfKeeperC->activePerf());
   if (perf) perf->redo();
   }

If a button press was being responded to, the ‘WinObject’ pointer could be dynamic_casted to a DwlButton, and if I needed additional items the button had a void * ‘user’ member variable to hold or point to whatever I wanted. I never liked the void * and static_casting aspects which were involved, but the combination solved my problems in the shortest amount of coding.

(Looking at the previous routine, the ugliest thing is the exposed ‘perfKeeperC’ pointer - something I would never do now.)

Eventually I discovered Sergey Ryazanov’s Impossibly Fast C++ Delegates, and after pondering them for a bit, figured out they would at last allow users to directly call back into an instantiated class without going through static functions and accessing a global pointer to initiate the actual function lookup. Three days later, and one false start, and this change worked on BCB, Dev-C++, and VS.

The solution involved creating a delegate which looked like the following:

class DwlDelegate {
   private:
      typedef void (*stub_type)(void* object_ptr, DwlWinObject * arg);
      stub_type stub_ptr;

      void * object_ptr;
      DwlWinObject * argC;

      template <class T, void (T::*TMethod)(DwlWinObject*)>
      static void method_stub(void* object_ptr, DwlWinObject * a1) {
         T* p = static_cast<T*>(object_ptr);
         return (p->*TMethod)(a1);
         }
   public:
      DwlDelegate() : object_ptr(0), argC(0) , stub_ptr(0) { }

      template <class T, void (T::*Method)(DwlWinObject*)>
      static DwlDelegate create(T* object_ptr, DwlWinObject * arg) {
         DwlDelegate d;
         d.stub_ptr = &method_stub<T, Method>;
         d.object_ptr = object_ptr;
         d.argC = arg;
         return d;
         }

      void operator()() const {
         return (*stub_ptr)(object_ptr, argC);
         }

      void object(DwlWinObject * p) { argC = p; }
   };

This code is fairly close to Sergey’s original design stripped to its bare essentials. It is far from Sergey’s complete work, though, which allows multi-signature delegates to be created without change to the codebase. BCB 4.0 could not compile the entirety of Sergey’s masterful creation. The modifications to the above allow you to make a delegate to a function with a signature of "void SomeClass::Function(DwlWinObject * sender);".

In addition to the Delegate class, it was necessary to create a WinCallbackList, which stores a list of control IDs and the delegate associated with each of them.

When a control is created, it contains a WinCallbackItem, which ‘gets’ the next available ID number from the WinCallbackList. The WinCallbackItem also handles registering and unregistering the control from the WinCallbackList.

As somewhat of a side note, if you ever need an algorithm to dole out numbers, keep track of the assigned ones, and reassign those which are no longer in use, take a look at the processing behind DwlCallbackList::insert and DwlCallbackList::remove. I don’t guarantee that is the fastest or best method available, but it has not given me any problems so far.

In addition to this, DwlBaseWin windows received the following wCommand processor:

LRESULT DwlBaseWin::wCommand(WORD notifyCode, WORD ctrlId, HWND ) {   //ctrlHWND
   //Sometimes, during creation of a control, Windows sends WM_COMMANDS with 'notifyCode'
   //== 1024 and other values.  What these are doing is unknown, although it is suspected
   //that they are 'commanding' the parent window to register this control in it's list
   //of children inside of Windows itself.
   if ((notifyCode == 0 || notifyCode == 1) && ctrlId < DWL_MDI_FIRSTWIN) {
      gWinApp->winCallBackListC.performCallBack(ctrlId);
      return 0;
      }
   return DefWindowProc(hwndC, msgC, wParamC, lParamC);
   }

You can see that when one of your windows derived from this class is called by Windows, the winCallbackListC is automatically invoked. As you may surmise, it is not a good idea to override or change this procedure until you know what you are doing.

Delving deeper into the design, the winCallbackListC::performCallback looks up the Delegate associated to the incoming control ID, and tells the delegate to execute itself.

The sample application shows how all of this works, but let me take a brief moment to pull some of the code pertaining to buttons into this write-up. Hopefully, what the programmer needs to do will become clear.

[Realizes the program doesn’t contain a button, so works furiously, fixing the issue. And, just for good measure, the button’s ‘user’ variable has been ‘examplified’.]

//First, add a button to ObjViewWin.h, and add a callback to the class.  Technically,
//the callback can be in another class, although I've forgotten the details of the
//delegate declaration.
...
class ObjViewWin : public DwlDockChild {
   private:
      ...
      dwl::grin_ptr<DwlButton> buttonC;
      ...
   public:
      ...
      void onButtonClick(DwlWinObject * button);
      ...

   //Then, in the .cpp file, initialize the button in the constructor with:
   ...
   buttonC.reset(new DwlButton(this, 13, 240, 140, 25, _T("Press Me!"),
               CreateDelegate(ObjViewWin, onButtonClick, this, this)));
   buttonC->user = wString("Here's the user text you asked for!");
   ...

//Notice the 'user' item, adopted from Christopher Diggins' work.
//Also, if the delegate points to something outside of this class, digging
//through code I see  I've used "parentC, parentC" for the last two parameters
//in many instances.  'parentC' is, as the name implies, a pointer to the parent
//object of the window creating the delegate.  In a couple instances 
//"this, gWinMain" are the last two parameters, but those are rare. They may be
//a relic of very old code, and might be replacable by "gWinMain, gWinMain", but
//I'm not going to touch working code, and those do work.


//And, finally, for the callback routine itself:
void ObjViewWin::onButtonClick(DwlWinObject * button) {

   //To show one possible use of the button's 'user' variable:
   dwl::msgBox(buttonC->user.cast<wString>().c_str());
   
   //The 'user' variable is very powerful.  Its been used like this before:
   //buttonC->user.cast<SomeClassType*>()->doSomething();
   //But it can be used for almost anything your imagination dreams up.
   //As noted in CDigginsAny.h, thanks go to the author/s.
   
   //And as an example, uncomment the following line to see that the 'user' item is
   //smart enough to not let you do something bad with the held item:
   //int i = buttonC->user.cast<int>();
   
   //To test the 'user' to see if it is safe to use, instead of throwing,
   //you can do the following.  In my rework of Diggins' programming, 'compatible' has
   //been renamed 'isA' because it is more descriptive to me.  I haven't tested what
   //happens if this is tested against something higher up in the derivation heirachy,
   //such as DwlWinObject.  I suspect only the exact same type of object as the ‘user’
   //holds will meet the ‘true’ qualification.
   if (buttonC->user.compatible<wString>()) dwl::msgBox("Compatible with 'wString'!");
   if (!buttonC->user.compatible<int>()) dwl::msgBox("Not compatible with 'int'!");
   }

I believe the previous snippets are fairly self explanatory. A button is created with an ‘any’ object called ‘user.’ The container holds a wString, which is is a wrapper around std::string or std::wstring depending upon Unicode’s definition. ‘ObjViewWin::onButtonClick’ is set to handle the button click, and displays the wString in a MessageBox, as well as performs some other coding examples. The CreateDelegate portion is the aspect diverging the most from Borland’s VCL, but all things considered, the code is still clean and easy to grasp.

If I was to redo this today I would take a serious look at replacing the entire callback mechanism with Sarah Thompson’s Signal and Slot implementation. I have never used her work, or any other signal/slot library, but sound interesting, and possibly cleaner than the end result of the previous approach.

Another option just came to my attention: using the new C++11 Lambda functions. I’m happy with the final results, though, so for now I will put that time to productive use elsewhere.

A Note on CDiggins::any

For those of you who play around with the ‘user’ portion of the example code, the following is worth being aware of.

While programming the last button I noticed Christopher Diggins updated his ‘any’ class in 2011 (I was too busy researching ancient astronomy to keep abreast of these things at the time). So I adopted MEdit to the change. It crashed.

The problem, unless I missed something esoteric, is pointers are similar to the fundamental types, such as ‘int’ and ‘float.’ In his code Diggins wrote special logic for creating an ‘any’ variable of those other objects, and when they are destroyed ‘delete’ isn’t called. For bigger types ‘delete’ is executed.

In DWinLib, items derived from DwlControl contain a public ‘any’ member named ‘user.’ This is defaulted to nothing. DwlButtons derive from DwlControl.

MEdit uses this item in several places, and changes it depending upon various conditions. All my ‘live’ instantiations hold pointers to classes, so the information can be accessed elsewhere. The ‘=’ operator sets the value, like this: someButton.user = pointerToSomeClass.

As of January 27, 2013, Diggins’ codebase results in ‘delete’ being called on the pointer when it is overwritten by another pointer. Placing a breakpoint on the line after the reassignment, I noticed the popup showed the object state had some bad 0xfeeefeee values (or whatever the default one is). I did not immediately suspect the new code, but rather that his changes, and the others I implemented myself, had somehow activated a deeper logic bug. (I’m a pessimist while troubleshooting these things.) After a lot of painful, tedious debugging I changed my mind.

To get around this the following construct works:

SomeClass * tempPointer = &something;
any tempAny(tempPointer);
someButton.user = tempAny;

The previous fix isn’t very clean, but does keep ‘delete’ from being called.

I believe that is everything worth mentioning as far as responding to user input is concerned.

Menus

Before leaving the ‘User Input’ portion of this writing, another item to point out is how menus work in DWinLib. (Having used Microsoft’s ‘ribbons,’ I do not care for them. They make things harder to find, not easier, unless you only have one ‘ribbon’ of features for your entire program.)

Basically, DwlMenu is a thin wrapper around the Windows’ menu functions. If you are interested in that awkwardness, feel free to poke inside ‘DwlMenu.cpp’ and its header.

That ‘thinness’ is one item I’m considering a rewrite on, as this aspect of Windows programming is rather ugly. An extra layer of indirection may be a good thing in this regard, although I haven’t gotten around to it at this point.

To illuminate the claim, all my DWinLib projects so far contain a unit called ‘MenuMain.' That code is responsible for manipulating the DwlMenu calls in order to build the program’s menu. Here is a copy/paste of the awkwardness in the StupidSquares example:

MenuMain::MenuMain(WinMainO * parent) :
            parentC(parent),
            mainMenuC(DwlMenu::CreateMain, DwlMenu::DontDestroy, parent),
            fileMenuC(DwlMenu::CreatePopup, DwlMenu::DontDestroy, parent),
            toolMenuC(DwlMenu::CreatePopup, DwlMenu::DontDestroy, parent),
            windowMenuC(DwlMenu::CreatePopup, DwlMenu::DontDestroy, parent),
            viewMenuC(DwlMenu::CreatePopup, DwlMenu::DontDestroy, parent),
            helpMenuC(DwlMenu::CreatePopup, DwlMenu::DontDestroy, parent),
            windowSubC(NULL),
            helpSubC(NULL),
            fileSubC(NULL),
            viewSubC(NULL) {

   //First, everything for the 'File' menu:
   fileMenuC.addItem(_T("&New\tCtrl+N"), CreateDelegate(WinMainO, newWindow, parentC,
               parentC));
   fileMenuC.addItem(_T("&Open...\tCtrl+O"), CreateDelegate(WinMainO, open, parentC,
               parentC));
   closeC = fileMenuC.addItem(_T("&Close"), CreateDelegate(WinMainO, closeWindow, parentC, 
               parentC));
   closeAllC = fileMenuC.addItem(_T("C&lose All"), CreateDelegate(WinMainO, closeAll, parentC,
               parentC));

   fileMenuC.addSeparator();

   saveC = fileMenuC.addItem(_T("&Save\tCtrl+S"), CreateDelegate(WinMainO, save, parentC, 
               parentC));
   saveAsC = fileMenuC.addItem(_T("Save &As..."), CreateDelegate(WinMainO, saveAs, 
               parentC, parentC));
               
   fileMenuC.addSeparator();
   
   aboveHistoryListC = fileMenuC.addItem(_T("E&xit"), CreateDelegate(WinMainO, exit, parentC,
               parentC));
   fileSubC = mainMenuC.addSubMenu(&fileMenuC, _T("&File"));


   //Next, the 'View' menu:
   viewMenuC.addItem(_T("Show &Toolbar"), CreateDelegate(WinMainO, showToolbar, parentC,
               parentC));
   viewMenuC.addItem(_T("Show &Object View Bar"), CreateDelegate(WinMainO, showObjView,
               parentC, parentC));
   viewSubC = mainMenuC.addSubMenu(&viewMenuC, _T("&View")); 
   
   
   //And the 'Windows' menu:
   windowSubC = mainMenuC.addSubMenu(&windowMenuC, _T("&Window"), WINDOW_MENU_POS);


   //And finally the 'Help' menu.
   helpMenuC.addItem(_T("&Help"), CreateDelegate(MenuMain, help, this, parentC));
   helpMenuC.addItem(_T("&About StupidSquares"), CreateDelegate(MenuMain, about, this, parentC));
   helpSubC = mainMenuC.addSubMenu(&helpMenuC, _T("&Help"));
   }


MenuMain::~MenuMain() {
   delete fileSubC;
   delete viewSubC;
   delete windowSubC;
   delete helpSubC;
   }


void MenuMain::refresh() {
   DrawMenuBar(gWinMain->hwnd());
   }


void MenuMain::update(Application * app) {
   updateWindowItems(app);
   refresh();
   }


void MenuMain::updateWindowItems(Application * app) {
   //Now continue normal processing
   fileMenuC.enableItem(closeAllC, app ? true : false);
   fileMenuC.enableItem(saveC, app ? true : false);
   fileMenuC.enableItem(saveAsC, app ? true : false);
   fileMenuC.enableItem(fileInfoC, app ? true : false);
   fileMenuC.enableItem(closeC, app ? true : false);
   
   }


void MenuMain::help(DwlWinObject * ) {
   gWinMain->help();
   }


void MenuMain::about(DwlWinObject * ) {
   AboutWindow * f = new AboutWindow;
   }


HMENU CopyMenu(HMENU menu, BOOL Popup = FALSE) {
   HMENU copy;
   if (!Popup) copy = CreateMenu();
   else copy = CreatePopupMenu();
   int items = GetMenuItemCount(menu);
   
   for(int i=0; i<items; i++) {
      UINT ID = GetMenuItemID(menu, i);
      TCHAR str[128]=_T("");
      GetMenuString(menu, i, str, 128, MF_BYPOSITION);
      if( ID == 0xFFFFFFFF && str[0] ) { // SubMenu
         HMENU SubMenu=CopyMenu(GetSubMenu(menu, i), TRUE);
         AppendMenu(copy, MF_POPUP|MF_STRING, (unsigned int )SubMenu, str);
         }
      else {
         AppendMenu(copy, str[0]?MF_STRING:MF_SEPARATOR, ID, str);
         }
      }
   return(copy);
   }

The most difficult-to-understand code is the constructor. My head wants to explode looking at it. Examining the function for the first time in a long time, the questions begin with, “Why must I use the ‘DwlDontDestroy’ enumeration so many places in the initializer list? And what is up with all that ‘DwlMenu::CreateMain/CreatePopup’ stuff?”

Of course, then I need to backtrack to the implementation of DwlMenu for the answers, and they aren’t very clear, even there. The problem isn’t so much with the code itself, and the fact that it may not be commented as well as it could be. Rather, the mechanisms Windows uses to create menus do not lead to very good self-documenting code!

So I must assume that when I originally wrote those routines I had a specific reason for each item. This is not an ideal state.

The programming after the initializer list doesn’t improve the situation. If the comments and white space weren’t there, how many times would you have to read it before realizing the first ten items are simply creating the ‘File’ menu and its components? In my case, more than twice.

Perhaps I shouldn’t mention the things I don’t really care for in this writing, but doing so may motivate me to improve this aspect in the future.

Docking Toolbars

The next item worthy of a brief illumination is Docking Toolbars. I’m not going to go into the mechanism behind them, as they were originally developed from an excellent C tutorial available at Catch22’s website, and my code is fairly self-explanatory. When I first wrote this page I came across Jeff Glatt’s Docking Toolbars in Plain C. Due to its wording I thought it was a straightforward adaptation of James’ work, but while editing this for a revision back on 8/1/05 I had reason to delve into it more deeply, and found that Jeff made some substantial changes to the code itself (all of them quite good), and I adopted his method of making docked windows resizable as a result.

I will mention that I vastly improved the workings of the docking toolbars, and polished them to the point where very little (to no) flicker or jumping occurs while the window is being resized (in the older Window’s themes). As far as I know, no other library has accomplished this feat.

Briefly, the secret is the handling of the WM_ERASEBKGND and WM_PAINT messages. When you create controls from scratch, return 0 from an otherwise blank WM_ERASEBKGND handler, and do all your painting double-buffered in the WM_PAINT handler. For wrapped Windows Child Controls, double-buffer your painting routine while handling the WM_ERASEBKGND message. This may sometimes require calling the default WM_PAINT handler with a memory DC for its use, to force double-buffered painting to occur.

The process of figuring out everything it took to eliminate these flicker sources was not one of the most fun experiences of my life, and made me pretty tired of looking at docking window code. That section has now seen *four* major edits, in order to overcome all of the difficulties involved.

Do not let the above scare you, though. If you decide to create a flicker-free control (not a docking toolbar, just a regular control) from scratch, it is not that painful of an event, as long as you are not wrapping a Windows control. (But if you are, the DwlControlWin class will considerably ease your task.) You can see an example of a non-WCC (non-Wrapped Child Control) in the DwlStringGrid unit in the DWinLib subdirectory.

Getting back to docking toolbars, I should mention that although I’m proud of the work I’ve done, they are another area I want to see further improvements in. Ultimately it would be nice to have something like the system used in Visual Studio Express. There, the toolbars aren’t represented by a dummy rectangle while being moved, and they snap into place intuitively. I suspect a lot of work is required to get to that state.

As I write this (in January, 2013), the ‘dummy rectangle’ is another item which needs improved. The code was originally created during the XP era, and on those systems the rectangle redrew itself and moved around the screen very quickly, and intuitively. When moving a docking toolbar in Windows 7 there is a noticeable lag. I haven’t looked into the reason for this due to lack of time.

If you play with the docking toolbar implementation on your own, the following pointer may help orient you more quickly.

During the January 2013 rewrite I discovered a bug in this portion of the code. The change from SDI to MDI had not been thoroughly tested, and I found that if a floating docker was closed by using the upper right hand ‘close’ button, the destructors weren’t called properly, and leaked memory.

Behind this oversight lies a little complexity worth being aware of: there are three ways to initiate a docking window destruction. The first is to manually call the destructor of the appropriate docker. The second is to close the docker while it is floating by using its ‘close’ button (which calls the docker’s ‘wClose’ routine). And the third is to terminate the program when the docker is visible. All of these have different control flows (the last one starting at DwlDockChild::wDestroyNonClientArea, or your overridden version if you have done so), and I overlooked this fact during the recoding.

The attached executable (and those in the other projects) have been fixed, and the dockers destroy themselves correctly. A word of warning, though. I have not tested manually ‘deleting’ the toolbar window. Instead, I recommend calling the wClose routine, which I know will initiate the appropriate chain of events:

void WinMainO::showToolbar(DwlWinObject * ) {
   if (toolbarC.get()) {
      toolbarC->wClose();
      return;
      }
   toolbarC.reset(createToolbar());
   updateUI();
   }

And with that, our docking window discussion will now close!

Dealing with wrapped child controls

Wrapped child controls (WCC - the Windows standards, such as edit boxes, combo boxes, etc.), posed their own problems, because they do not call into the DwlBaseWin::winProc procedure. This means methods are needed in order to notify your windows when interesting things happen to them.

DWinLib’s solution to this problem is subclassing the WCCs, and making the subclass call your code when events take place.

A good example of this is the handling of the edit box in StupidSquares. Let me first illustrate how a WCC is subclassed, and then I will show a few specifics of the box in question. (It is the one which says "I’m Alive!" in the above graphic. Each square in StupidSquares contains its own string, and this box reflects the string of the currently selected box, and enables editing of that string.)

The DwlControlWin unit takes care of the basic subclassing of WCCs. Not every WCC needs to be subclassed, but I believe a majority of the WCCs which haven’t yet been wrapped by DWinLib will have to be, therefore they must derive from DwlControlWin. (Scrollbars do not need this treatment, as one example, although you would need to if you wanted to change their colors.) This subclassing is handled in the following manner:

void DwlControlWin::setupWindowProc() {
   //Set the GWL_USERDATA to this instance.
   #pragma warning (push)           //Pragma is to overcome problem in PSDK regarding 64
   #pragma warning (disable:4312)   //bit LONG->LONG_PTR warning issue for 32 bit builds.
   #pragma warning (disable:4244)
   SetWindowLongPtr(hwndC, GWLP_USERDATA, (LONG_PTR)this);
   origProcC = (WNDPROC) SetWindowLongPtr(hwndC, GWL_WNDPROC, (LONG_PTR)winProc);
   #pragma warning (pop)
   }


//The above will make Windows call into the following static function for the WCC, and the
//programmer can override or set handlers for the individual Window messages. I am not
//showing the definitions for the individual callback functions. You can see that in the
//code in the zip file.

LRESULT DwlControlWin::winProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
   #pragma warning (push)           //Pragma is to overcome problem in PSDK regarding 64
   #pragma warning (disable:4312)   //bit LONG->LONG_PTR warning issue for 32 bit builds.
   DwlControlWin * win = r_cast<DwlControlWin*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
   #pragma warning (pop)

   try {
      switch (msg) {
         case WM_CHAR:
            return win->wChar(wParam, lParam);

         case WM_HELP:
            if (win->wParentC) return win->wParentC->wHelp((HELPINFO*)lParam);
            else break;

         case WM_LBUTTONDOWN:
            return win->wLButtonDown(wParam, LOWORD(lParam), HIWORD(lParam));

         case WM_KILLFOCUS:
            return win->wKillFocus((HWND)wParam);

         case WM_CONTEXTMENU:
            return win->wContextMenu((HWND)wParam, LOWORD(lParam), HIWORD(lParam));

         case WM_COMMAND:
            return win->wCommand(HIWORD(wParam), LOWORD(wParam), (HWND)lParam);

         case WM_KEYDOWN:
            return win->wKeyDown(wParam, lParam);

         //If you don't do the following two cases (WM_ERASEBKGND and WM_PAINT), a little
         //flicker may be seen in the embedded Window controls.
         case WM_ERASEBKGND: {
            HDC wdc = (HDC) wParam;
            HDC mdc = CreateCompatibleDC(wdc);   //Memory DC
            RECT r;
            GetClientRect(hwnd, &r);
            HBITMAP bmp = CreateCompatibleBitmap(wdc, r.right-r.left, r.bottom-r.top);
            HBITMAP oldBmp = (HBITMAP)SelectObject(mdc, bmp);
            CallWindowProc(win->origProcC, hwnd, WM_ERASEBKGND, (WPARAM)mdc, 0);
            CallWindowProc(win->origProcC, hwnd, WM_PAINT, (WPARAM)mdc, 0);
            BitBlt(wdc, 0, 0, r.right-r.left, r.bottom-r.top, mdc, 0, 0, SRCCOPY);
            SelectObject(mdc, oldBmp);
            DeleteObject(bmp);
            DeleteDC(mdc);
            win->justErasedC = true;
            return 0;
            }

         case WM_PAINT:
            if (win->justErasedC) {
               win->justErasedC = false;
               return 0;
               }
            break;

         case WM_WINDOWPOSCHANGED:
            return win->wPosChanged((WINDOWPOS*)lParam);
         
         case WM_SETFOCUS:
            return win->wSetFocus((HWND)wParam);
         }
      }
   catch (DwlException & e) {
      wString str = _T("Programming Error: ") + e.strC;
      str += _T("\n\nPlease report this error and the steps taken to obtain");
      str += _T("\nit to medit_support@randommonkeyworks.com");
      if (e.continuableC)
         str += _T("\nWill attempt to continue (program may become unstable)");
      else str += _T("\n\nProgram must exit.  Sorry.");
      MessageBox(NULL, str.c_str(), _T("Error"), MB_OK | MB_APPLMODAL);
      if (!e.continuableC) exit(EXIT_FAILURE);
      }
   catch (...) {
      int wish = MessageBox(NULL,
                  _T("Exception caught - Abort?\n(Program may be unstable if NO)"),
                  _T("Error"), MB_YESNO | MB_APPLMODAL);
      if (wish == IDYES) abort();
      }
   return CallWindowProc(win->origProcC, hwnd, msg, wParam, lParam);
   }

In the case of the text box in the ObjViewWin in the StupidSquares example, the text box responds to the WM_KILLFOCUS in the following manner:

LRESULT DwlTextBox::wKillFocus(HWND receiveFocusWin) {
   //As written, this is used to determine if the text has changed when the box is
   //de-activated.  If so, it will call into the 'OnChange' function that the user has
   //set.
   wString s = text();
   if (s != strC) {
      strC = s;
      SetWindowText(hwndC, &strC[0]);
      if (onChangeC) onChangeC(this, user);
      }
   if (wKillFocusProcC) return wKillFocusProcC(this, receiveFocusWin);
   else return CallWindowProc(origProcC, hwndC, WM_KILLFOCUS, (WPARAM)receiveFocusWin, 0);
   }

The static onChangeC function pointer inside the text box can be replaced by the creating class (or any other class, for that matter), and the ObjViewWin does so in the ObjViewWin’s ctor:

...
strBoxC.onChange(onTextChange);
ObjViewWin * temp(this);
strBoxC.user = temp;
...

The ObjViewWin implements onTextChange in the following manner:

void ObjViewWin::onTextChange(DwlControlWin * win, dwl::any & user) {
   ObjViewWin * view = user.cast<ObjViewWin *>(); //Just to show how, but not used
   if (view) view = NULL;   //To shut up the compiler warning about not being used.
   Application * app = gWinMain->topApp();
   if (!app) return;
   StupidSquare * square = app->lastSquare();
   if (!square) return;
   square->text(s_cast<DwlTextBox*>(win)->text());
   }

You can see more of the internals by wading through the code in the Zip file.

This does not cover all of the wrapped WCCs, such as the scrollbars (WinScrollbar class). While I recommend looking into that unit in particular, and how it interacts with the AppWindow, I am not going to go into the details here - this is already more than long enough. But I think you will like how they are wrapped, and the simplicity of the end-programmer’s task as compared to a pure-API solution. The only four scrollbar routines which needed to be programmed for StupidSquares were AppWindow::wScrollHor, AppWindow::wScrollVer, AppWindow::scrollPosChanged, and AppWindow::updateScrolls.

Closing Thoughts

The previous items are the important ones which come to mind when looking back upon DWinLib’s development. For programmers wishing to become more familiar with the inner workings of Windows, and how the programming task can be made easier, I hope this writing has been useful.

As always, Happy Coding!

History

2/13/13

Completely revamped the MDI routines to the standard Windows’ method. Improved Unicode capabilities with units such as CharArrayWrappers.cpp, so wStrings can be easily converted from/to string/wstring if they are mostly American English, and locales are not an issue. (Also see DwlAsciiAndUnicodeFileWrapper.cpp for file reading/writing capabilities.) Rewrote entire DWinLib article series to logically flow from one topic to the next.

8/1/05

A huge ‘Thank You’ goes to David Nash. He took the time to get DWinLib working in Visual Studio .NET 2003, and sent a copy back. It was a major job, and I appreciate it, as well as the great feedback he gave me, helping me further improve DWinLib and this article. Due to his efforts I was able to compile DWinLib on Dev-C++, so if you don’t have access to VS, you can still play with the code on a great free compiler.

(Having played with both Eclipse and Dev-C++, I am much more impressed by Dev-C++, as its project management seems to be a lot simpler than Eclipse’s, as well as easier to intuit. You don’t have to work in a ‘workspace’, and Eclipse’s ‘workspace’ caused me about two hours of cussing, due to either my feeble brain or the fact that Eclipse’s workspaces are as bad as I got the impression they are. Additionally, I beat my head into some other issues which are not easy-to-grasp for a beginner. After my attempt back in about 2002 I gave up.)

David also indicated he tried to get it working on VC6, but because of standards incompliance with that compiler (not due to non-standard code), he was unable to do so. I am guessing it has to do with using "for (int i=0; i<something..." everywhere, and I refuse to go back to improperly-scoped variables.

Another major change is DWinLib is now ‘Unicode’ enabled. Again, thanks goes to David for starting me down that road.

The last modification was to the callback mechanism outlined in the Callbacks section.

There are also various other minor changes made to DWinLib and this article which I won’t go into details on.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

David O'Neil
Software Developer www.randommonkeyworks.com
United States United States
I am the author of Laughing at the Devil: One Man’s Religious Discoveries. For those who are “ready to look at the world - religion, science, spirituality - differently,” LATD is the book to turn to.
 
In about 1994 I began studying and documenting the astronomy of our ancestors. A hint lead to many years of partial understanding, before a profound breakthrough occurred and some old myths finally made sense.
 
The greatest of my discoveries is the celestial observations behind the biblical tale of Samson, which was created 3,000 years ago. That find casts a profound new light on the roots of Western religion, as well as the foundation of modern science. To learn more, visit my website.
 
Trained as a mechanical engineer, I learned C++ programming on my own in order to create a MIDI program. I am delighted to say I also succeeded in that goal. Happy coding, everybody!

Comments and Discussions

 
GeneralMy vote of 5 PinmvpEspen Harlinn14-Feb-13 6:07 
GeneralRe: My vote of 5 PinmemberDavid O'Neil14-Feb-13 14:42 

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.

| Advertise | Privacy | Mobile
Web01 | 2.8.140721.1 | Last Updated 14 Feb 2013
Article Copyright 2013 by David O'Neil
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid