NOTE: THIS ARTICLE IS GOING TO BE ELIMINATED WITHIN TWO WEEKS OF FEBRUARY 6, 2013 (if all goes well). PLEASE SEE DWinLib: An Overview[^] INSTEAD.

The above source Zip includes DWinLib itself, as well as the code to StupidSquares, the example flicker-free docking program, and a minimal program from which to derive programs. There are Borland Builder 4.0 project files, VS.NET 2003 project files, and Dev-C++ 4.9.9.2 project files included. (The 'include' directories path in your IDE will need to be changed to the paths you install the sources to on your own hard drive, if you rebuild them yourself.)
Last Update
Major update: 8/1/05. Now runs on VS.NET 2003, Dev-C++, and BCB 4.0.
Index
Introduction
DWinLib is pronounced 'Da Win Lib', like in a bad mafia movie. The 'D' can stand for 'Docking', or 'David's', or 'Dandy', or whatever, but I prefer just 'Da, as in 'The'. DWinLib was developed because I wanted a minimal Windows API wrapper that was understandable and easy to use by my standards. It was also developed to teach myself some of the ins and outs of the Windows API, overcome some DLL dependencies that were in my code, and make my code portable to a non-Borland compiler in the future, if I wanted to.
DWinLib originally came about because there was a bug in a program I was developing. Windows would occasionally send a message to a window (created using Borland's VCL) after the window was completely destroyed. This botched things up quite badly, as you can imagine, and I wanted to get to the point where I understood the API well enough to ensure that this would not happen to my programs in future. Learning MFC would not give me that knowledge, so I went down to the nuts and bolts and got busy.
The benefits of DWinLib that appear to me are:
- Easy-to-use controls and child windows inside the windows you create.
- DLL dependency free - only relies upon core Windows DLLs.
- Clean interface.
- Flicker-free docking framework included.
- Minimal run-time overhead.
- Use the STL wherever you want.
- Closely mimics a Windows API program, thereby making it somewhat easier to learn API programming in future.
- You can develop windows using true multiple inheritance if you so desire. You are not limited to only using aggregation in window objects to achieve the effects of multiple inheritance.
Programming is a highly subjective art, so many will disagree with what I consider to be the ninth benefit of DWinLib. That is that the core to DWinLib is clean by my standards. DWinLib does not use a million different prefixes, and it is thereby easier to understand what is happening under the hood. Starting as a neophyte, that was very important to me.
The fourth benefit deserves a little extra mention. If you resize most (possibly all) available commercial docking window applications from any edge (but most notably the top or left edges), the dockers will jump and/or flicker pretty badly. DWinLib applications can be made to be flicker free, thereby vastly improving the user 'feel' to your program in this aspect. (Try out the above 'flicker-free docking sample' program to see what I mean.) This is an added benefit to going all the way to the API, and understanding it well, rather than just using MFC.
(We will see how long it takes MS to steal this for their own purposes, now that I have made it public. If they do steal this method, do you suppose they will send me any money? I would most certainly appreciate it if they would!)
Background
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.
Each of the pages except the Win32 Generics site show you how to create a basic framework, but that is about all. You will still have to resort to the ground-level API and assign control IDs yourself if you want to handle child controls. Or you will have to develop your own solution if you want to do this in an easier manner. DWinLib has solved that problem for you, as well as a couple other problems.
The simplicity of Borland's Builder product highly influenced the development of this wrapper. I have extensive experience using BCB, as it is fondly known, and was very impressed with the ease it allowed you to do such things as changing button handlers on-the-fly. Methods were developed to mimic these features as closely as possible, using pure C++.
So with that, let us get started.
General overview of a DWinLib program
A good first step to begin understanding DWinLib is to give you a high-level view. The following hierarchy shows a generic overview to the main pieces of a DWinLib program. This design was the result of several iterations of work, as DWinLib was progressively made more intuitive and easier to use.

As you can see, it is a straightforward design. The WinApp unit simply takes care of the main window procedure for the application, as well as having a helper 'appPathC
' variable, to make it easier for you to get the program directory, if you want it.
Each Windows program must have a WinMain window, and the WinMainO unit is the heart of this window in DWinLib. All of the external GUI controls for your application should be instantiated and high-level controlled in WinMainO. By "external GUI controls" I mean those controls that are not embedded into your AppWindow itself. Examples are dockers, toolbars, menus, status bars, etc.
"High-level controlled" means that the controls are resized within WinMainO when WinMainO resizes. (Dockers are an exception to this, as they are resized by resizing the WinDockParent from within WinMainO, not the individual dockers.) The controls are also told to update themselves from within WinMainO when interesting things happen in the program. This doesn't mean that the WinMainO unit is responsible for enabling and disabling individual buttons and menu items. You can make WinMainO responsible for those actions if you wish, but that is just asking for a serious headache, as your WinMainO code will quickly become a monster. Examine how the code for StupidSquares was put together, and how the Toolbar::update
and MenuMain::update
functions interact with WinMainO for an example of what I consider to be a clean delegation of these responsibilities.
It is assumed that you will want to use docking windows inside a DWinLib program, and the WinDockParent unit takes care of the main docking window responsibilities. If you don't want docking windows, don't include any windows derived from WinDockChild
. If it bothers you that the docking window code will be bloating your executable, you can strip this unit out, and tie the WinMainO unit directly to the WinMdiWorkspace unit. That is left as an exercise for the reader.
The WinMdiWorkspace unit takes care of parenting the AppWindows, and has a couple helper functions built into it. It is a very simple window.
We now come to the heart of what happens when a user selects 'File -> New' in your program. DWinLib is designed to create a new Application object, and that Application object will contain an AppWindow inside it.
The Application unit is the core of your program, and it should not contain any user interface elements other than the link to an AppWindow. In this way your program's core logic can be kept separate from the interface logic, and the Application unit will not need to be changed if you port to another platform unless you use platform specific data types, or some other code tied to the hardware. All that will be required will be a user interface modification and you will be done.
Class hierarchy of general windows within DWinLib
The following is an outline to the class hierarchy of general window objects inside DWinLib. This diagram includes some, but not all of the Windows common controls that have been wrapped. (Some of the wrappers are very minimal at this point, and there are many common controls that haven't yet been wrapped.)

The important classes to notice are the WinBaseO
and WinControlWin
classes. DWinLib is designed so that all your normal windows will derive from WinBaseO
. The WinControlWin
is a base class designed for deriving objects that handle standard Windows controls. The WinControlWin
class considerably eases development of those wrappers.
(You will note that a couple standard Windows controls do not derive from WinControlWin
. The reasons for those decisions will not be gone into in this document, other than to state that it was the simplest method I could figure out. It also kept some unnecessary code introduced by WinControlWin out of those controls.)
Associating Windows messages to specific windows
With that out of the way, we can begin discussing how things work under the hood.
The first obstacle that must be overcome is associating Windows messages to specific windows. DWinLib's solution is slightly different than the previously mentioned wrappers.
The forwarding of Windows messages to the appropriate window class is accomplished in the WinApp::WndProc
function. This is the main callback for Windows to use for all the windows in your application. (Remember that standard Windows controls have their own window procedure, so they won't call into WinApp::WndProc
). Here is the implementation of WinApp::WndProc
, along with the definition of an important variable being used:
static WinBaseO * winBeingCreatedC;
static LRESULT CALLBACK WndProc(HWND window, unsigned int msg,
unsigned int wParam, long lParam);
void setWinBeingCreated(WinBaseO * win) { winBeingCreatedC = win; }
...
LRESULT CALLBACK WinApp::WndProc(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);
}
This is very simple and efficient.
I chose to use the static winBeingCreatedC
variable, rather than testing for WM_NCCREATE
, because it is very difficult (almost impossible, really) to determine which WinBaseO
instance to call when a WM_NCCREATE
message is received if you don't use this method.
David Nash pointed out that this could create problems with multithreaded applications, if a sub-thread instantiated its own windows. He also pointed out a solution that he used, which is to place winBeingCreatedC
into thread local storage. I do not have the multithreading experience available to know if there are other issues that are being overlooked, so I decided to leave this 'as-is' for the time being. The change could be made without affecting any programs that don't create windows in sub-threads, unless someone goes to great lengths to circumvent the 'private' aspect to winBeingCreatedC
. If you start creating such programs, you may want to consider the advise of many who went down that path before. They recommend not doing it, and making the main thread responsible for all GUI interaction. If you do modify this for your own purposes, and it tests out well, please send me a copy of the modified code.
You may wonder what is occurring to set winBeingCreatedC
. This occurs in WinBaseO
's ctor. It is implemented in the following manner:
WinBaseO::WinBaseO(WinControl * parent, int x, int y, int w, int h) :
WinControl(parent, x, y, w, h) {
gWinApp->setWinBeingCreated(this);
}
That is the complete implementation, and as you can see, it is also very simple. You don't have to test for WM_NCCREATE
, and you don't have to set up a map to your window instances and their HWND
s. That is all that it takes to associate Windows messages to the appropriate WinBaseO
class instances.
The WinBaseO winProc
As you can well imagine, the WinBaseO::winProc
is a very important function. It is the one responsible for finally handling specific Windows messages. I chose to send the messages to virtual functions from within WinBaseO::winProc
. This enabled me to at last give meaningful names to the appropriate parts of WPARAM
and LPARAM
, and to place all of those ugly casts into one block where they could be rationally dealt with. This method also enables you to have reasonable default responses to the messages, and then override those functions for more refined processing as you need it.
As part of the original design of DWinLib, the WinBaseO::winProc
was declared virtual
, so windows derived from WinBaseO
can either overwrite the winProc
procedure (and have to once again deal with all the casting and exception handling, unless other steps are taken), or use it 'as-is'. There is one example of overwriting the winProc
in the StupidSquares example: the WinMainO::winProc
, and it is only used to handle an additional message and then call back into WinBaseO::winProc
.
This virtual function method of handling the class winProc
s entirely eliminates message maps (they are so ugly, aren't they?), and enables you to respond to any message you desire inside the winProc
procedures.
If you are worried about the virtual call overhead of this design, don't. Users won't notice the extra hundred billionths of a second that it takes your program to respond to a mouse click, if it takes even that amount. The slight lag is comparable to, or better than the lag imposed by other frameworks, so why worry about it?
Here is part of the implementation to the WinBaseO::winProc
procedure. It should give you an idea as to how the rest of the case
statements look:
virtual LRESULT wMouseDown(Button button, DWORD flags, WORD x, WORD y) {
if (button == LeftButton)
return DefWindowProc(hwndC, WM_LBUTTONDOWN, wParamC, lParamC);
else if (button == RightButton)
return DefWindowProc(hwndC, WM_RBUTTONDOWN, wParamC, lParamC);
return 0; }
LRESULT WinBaseO::winProc(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) {
try {
wParamC = wParam;
lParamC = lParam;
switch (msg) {
case WM_LBUTTONDOWN:
return wMouseDown(LeftButton, wParam,
LOWORD(lParam), HIWORD(lParam));
...
case WM_RBUTTONDOWN:
return wMouseDown(RightButton, wParam,
LOWORD(lParam), HIWORD(lParam));
...
}
return DefWindowProc(window, msg, wParam, lParam);
}
catch (AppException & e) {
wString str = TEXT("Exception caught: ") + e.strC;
if (e.continuableC)
str += TEXT("\nWill attempt to continue "
"(program may become unstable)");
else str += TEXT("\nAborting");
MessageBox(NULL, str.c_str(), TEXT("Error"), MB_OK);
if (!e.continuableC) abort();
}
catch (...) {
int wish = MessageBox(NULL,
TEXT("Exception caught - Abort?\n"
"(Program may be unstable if NO)"),
TEXT("Error"), MB_YESNO);
if (wish == IDYES) abort();
}
return 0;
}
Notice that the winProc
routine is the final place where exceptions will come to rest in your program. They must be handled there (or by there), so you can return an LRESULT
to Windows, and let it know that your application is still properly alive. If you tried handling exceptions in WinApp::run
, you could not return a return code to DispatchMessage
, and your program would become flaky. You can read a little more about this issue at the Microsoft website.
Button and menu clicks
The above is a rudimentary framework for handling Windows programs. If you recreated only what I have given you so far, you would still have to assign unique IDs to child controls in your program, and you would have to deal with the standard Windows controls using only API methods. Having done that for a bit, I quickly grew tired of it, and said "There has to be a better way". The API method was a very painful experience.
Perhaps the first issue you will have to deal with if you recreated the wrapper only to this point, is how to have a menu item or a button click call into the code of an instantiated WinBaseO
derived window (such as WinMainO
). This problem caused many revisions to the class hierarchy scheme before the above hierarchy was settled upon.
I originally had a nifty callback process that called back into static functions, which you could then route back into an instantiated class. During the 8/1/05 revision process I came across Sergey Ryazanov's Impossibly Fast C++ Delegates, and after pondering them for a bit, figured out that they would at last allow users to directly call back into an instantiated class without going through static functions. Three days later, and one false start, and this change was at last working on BCB, Dev-C++, and VS.
The solution involved creating a delegate that looked like the following:
class Delegate {
private:
typedef void (*stub_type)(void* object_ptr, WinObject * arg);
stub_type stub_ptr;
void * object_ptr;
WinObject * argC;
template <class T, void (T::*TMethod)(WinObject*)>
static void method_stub(void* object_ptr, WinObject * a1) {
T* p = static_cast<T*>(object_ptr);
return (p->*TMethod)(a1);
}
public:
Delegate() : object_ptr(0), argC(0) , stub_ptr(0) { }
template <class T, void (T::*Method)(WinObject*)>
static Delegate create(T* object_ptr, WinObject * arg) {
Delegate 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 user(WinObject * p) { argC = p; }
};
This code is fairly close to Sergey's original article code stripped to its bare essentials. It is far from Sergey's complete code, though, which allows multi-signature delegates to be created without change to the codebase. BCB 4.0 is incapable of compiling Sergey's complete work. The modifications in the above from Sergey's original article code allow you to create a delegate to a function of the signature "void Function(WinObject * 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 these IDs.
When a control is created, it associates itself to a WinCallbackItem
, which 'gets' the next available control ID number from the WinCallbackList
. This number is used for the control's Windows ID. The WinCallbackItem
also handles registering and unregistering the control from the WinCallbackList
.
In addition to this, WinBaseO
windows were given the following wCommand
processor:
LRESULT WinBaseO::wCommand(WORD notifyCode, WORD ctrlId, HWND ) {
if (notifyCode == 0 || notifyCode == 1) {
gWinApp->winCallBackListC.performCallBack(ctrlId);
return 0;
}
return 0;
}
You can see that when one of your windows derived from this class is called by Windows, the winCallbackListC
is automatically called. As you can surmise, it is not a good idea to override or change this procedure until you know what you are doing.
The winCallbackListC::performCallback
looks up the Delegate
associated to that control ID, and tells that delegate to execute itself.
The sample application shows how all of this works, but I will take a brief moment to pull some of the code pertaining to buttons into this write-up for your edification. Hopefully, that will help you understand what the programmer needs to do from their perspective.
//The purpose of this first bit of code is to show that you must pass a
//Delegate into the WinToolbarButton//The programmer will only need to worry about code like this when
//developing new controls.
//In WinToolbarButton header:
class WinToolbarButton : public WinObject {
private:
WinToolbar * parentC;
WinCallbackItem * callBackC;
public:
WinToolbarButton(WinToolbar * parent, TCHAR * resName,
const Delegate & delegate,
wString toolTip = TEXT(""),
DWORD state = TBSTYLE_BUTTON);
...
};
//Here//In the Toolbar unit (derived from WinToolbar):
Toolbar::Toolbar(WinControl * parent) : WinToolbar(parent) {
imageListC.addImage(TEXT("FILE_NEW"));
...
fileNewC = new WinToolbarButton(this, TEXT("FILE_NEW"),
CreateDelegate(WinMainO, newWindow, gWinMain, this),
TEXT("New StupidWindow"));
...
}
//And here is how one of the callback functions is defined in WinMainO:
//In WinMainO header file:
public:
void newWindow(WinObject * );
//And the implementation:
void WinMainO::newWindow(WinObject * sender) {
//Just to show you how you can determine what
//type of object is calling this.
//From there, the next step would be to place
//some unique //variables, and test for that //to make decisions as to what to do in
//this function. (That is for the case in which
//you may have two or more buttons
//calling into this callback, and want to take
//specific steps based upon which button
//is calling this.)
WinToolbarButton * tbButton = d_cast<WinToolbarButton*>(sender);
WinMenuItem * item = d_cast<WinMenuItem*>(sender);
//We won
WinAppKeeper * ak = gWinMain->appKeeper();
AppWindow * w = NULL;
Application * topApp = ak->activeApp();
if (topApp) w = topApp->appWindow();
Application * app = new Application();
if (w) {
if (w->windowState() == Maximized)
app->appWindow()->wSysCommand(SC_MAXIMIZE, 0, 0);
}
gWinMain->updateUI();
}
//In the above, the
#define CreateDelegate(aa, bb, cc, dd)
Delegate::create<aa, &aa::bb>(cc, dd)
//It allows you to escape from the following ugly syntax:
Delegate::create<WinMainO, &WinMainO::newWindow>(gWinMain, parentC));
Note the points about the CreateDelegate
macro, and how to determine what type of object called into your handler at the end of the above code snippet.
I could have used boost::function
and boost::bind
to accomplish the callbacks, but BCB 4.0 refuses to compile that portion of boost's code. In addition, I believe boost::function
and boost::bind
have a higher overhead than the above method.
You will find more of the details in the zipped example code. There is a toolbar in the flicker-free docking sample, and the StupidSquares sample uses a couple other controls.
And that is that! Pretty straightforward, all things considered.
Dealing with wrapped child controls
Wrapped child controls (the Windows standard child controls, such as edit boxes, combo boxes, etc.) hereafter abbreviated WCC, posed their own problems, in the fact that they do not call into the WinBaseO::winProc
procedure. This means that methods are needed in order to notify your windows when interesting things happen to the WCCs inside them.
The solution to this problem in DWinLib is subclassing the WCCs, and making the subclass call back into your code when events occur to the WCCs.
A good example of this is the handling of the edit box in StupidSquares. Let us first show how a WCC is subclassed, and then we can show a little about the specifics of the edit box in question. (It is the box that says "I'm Alive!" in the above graphic. Each square in StupidSquares contains its own string, and this box reflects the string of the box the user clicks on, and that string can be changed through this box.)
The WinControlWin
unit takes care of the basic subclassing of WCCs. Not every WCC needs to be subclassed, but I believe that a majority of the WCCs that haven't yet been wrapped by DWinLib will need to be subclassed, therefore they will need to derive from WinControlWin
. (Scrollbars do not need to be subclassed, as one example, although you would need to if you wanted to change their colors.) This subclassing is handled in the following manner:
void WinControlWin::setupWindowProc() {
SetWindowLong(hwndC, GWL_USERDATA, (LONG)this);
origProcC = (WNDPROC) SetWindowLong(hwndC,
GWL_WNDPROC, (LONG)winProc);
}
LRESULT WinControlWin::winProc(HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam) {
try {
WinControlWin * win =
r_cast<WinControlWin*>(GetWindowLong(hwnd, GWL_USERDATA));
switch (msg) {
case WM_CHAR:
return win->wChar(s_cast<TCHAR>(wParam), lParam);
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);
case <a name="wm_erasebkgnd"></a>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->
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 WinTextBox::wKillFocus(HWND receiveFocusWin) {
wString s = text();
if (s != strC) {
strC = s;
SetWindowText(hwndC, &strC[0]);
if (onChangeC) onChangeC(this, user1C);
}
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);
strBoxC.user1(this);
...
The ObjViewWin
implements onTextChange
in the following manner:
void ObjViewWin::onTextChange(WinControlWin * win, void * user) {
ObjViewWin * view = s_cast<ObjViewWin*>(user);
if (view) view = NULL;
Application * app = gWinMain->topApp();
if (!app) return;
StupidSquare * square = app->lastSquare();
if (!square) return;
square->text(s_cast<WinTextBox*>(win)->text());
}
You can see more of the details by wading through the code in the Zip file.
This does not cover all of the WCCs that have been wrapped, such as the scrollbars (WinScrollbar
class). While I sincerely recommend that you look into that unit in particular, and how it interacts with the AppWindow unit, I am not going to go into the details here - this is already more than long enough. But I think you will be very impressed at how the scrollbars are wrapped, and the simplicity of the end-programmer's task as compared to a pure-API solution. The only four scrollbar routines that needed to be programmed for StupidSquares were AppWindow::wScrollHor
, AppWindow::wScrollVer
, AppWindow::scrollPosChanged
, and AppWindow::updateScrolls
.
Docking toolbars
I am not going to go into the mechanism behind the docking of the toolbars, as they were originally developed from an excellent C tutorial available at Catch22's website, and my code is fairly self-explanatory. When I originally wrote this page, I came across Jeff Glatt's Docking Toolbars in Plain C. Due to its wording, I originally thought it was a straightforward adaptation of James' code, but while editing this for the 8/1/05 revision, I had reason to delve into it more deeply, and found that Jeff made some substantial changes to the code itself (quite good, I might add), 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 there is no flicker or jumping occurring while the window is being resized. As far as I know, no other library has accomplished this feat.
Very briefly, the secret is in the handling of the WM_ERASEBKGND
and WM_PAINT
messages. When you are creating controls from scratch, you can return 0 during the WM_ERASEBKGND
handler, and do all your painting double-buffered in the WM_PAINT
handler. For wrapped Windows Child Controls you can simply do all of your painting double-buffered in the handling of the WM_ERASEBKGND
handler. This may sometimes require calling the default WM_PAINT
handler with a memory DC for its use, to force double-buffered painting to occur. You can see how a WCC was wrapped in the case of WinControlWin
s, in the above WinControlWin::winProc
. In that instance, I also performed another trick, and 'anchored' each WCC to a locked down HWND
, in order to eliminate one more major source of flicker.
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 itself, just a control) from scratch, it is not that painful of an event, as long as you are not wrapping a window control. (And if you are wrapping a Windows control, the WinControlWin
class will considerably ease your task.) You can see some old example code of a non-WCC control in the WinStringGrid
unit in the DWinLib subdirectory. That unit will need some modification to run in DWinLib as it now stands, but the last time it was running, it was flicker-free when it was placed on a WinBaseO
derived window. (And the WinStringGrid
code *is* rudimentary, so don't be too picky about it when you look through it.)
Unicode
DWinLib natively supports Unicode applications, although you will want to keep in mind various points about creating Unicode applications. In order to enable Unicode, open the 'ProgramHeaders.h' file in the application's subdirectory. Uncomment the '#define UNICODE
' and '#define _UNICODE
' lines. In addition, use the 'wString
' type where you would use std::string
s or std::wstring
s. This typedef
is in 'ProgramHeaders.h', and looks like the following:
#include <string>
typedef std::basic_string<TCHAR> wString;
You can see that wString
isn't a full std::string
or a full std::wstring
, but it is the simplest solution to the problem at hand. (Well, I don't think it is a full std::string
or std::wstring
, but I haven't looked at the STL source code to see what the std::wstring
brings in addition to std::basic_string<wchar_t>
.)
(It is also worth noting that the 'ProgramHeaders.h' file is designed to be the first file processed by the preprocessor. It takes care of the Windows includes for the project, as well as any other items that you may desire. Once a unit of your project is stable, #include
it in the 'ProgramHeaders.h' file, and if you have kept the precompiled headers working as in the sample programs, your build times will be considerably reduced.)
In addition to that, you must link to the 'c0w32w.lib'. Unfortunately, the MinGW compiler that Dev-C++ is based upon does not have an equivalent library that I could find, so it is not possible to build a 'true' Unicode application in that environment. All of the strings can be Unicode in Dev-C++, but you will not be able to link to wWinMain
, which a true Unicode application calls as its entry point.
Building DWinLib
If you decide to build a DWinLib project from scratch, there are two points that you should be aware of.
The first of these points is that in order to build DWinLib, you will need to add the 'DWinLib', 'DWinLib/WinControlWin', and 'utilities' subdirectories into your project include paths. In Borland and Dev-C++, you can do so from the 'Project' -> 'Options' menu item, and select the 'Directories' tab in the window that appears. (These names are approximate - Dev-C++ uses 'Project Options' where I simply use 'Options', etc.)
In Visual Studio .NET 2003, you will need to right-click on the solution name in the Solution Explorer, and select 'Properties' from the popup menu. Then under the 'C/C++' node, select 'General', and the top item will be 'Additional Include Directories'.
If you are not using one of these IDEs, you will need to figure out how to do this in your IDE yourself.
The second item to be aware of is that you will need to link to 'comclt32.lib'. That is such a common task that I won't explain how to do so here.
I will also point out in this section, that if you wish to use an accelerator table in your program, you may comment or uncomment the #define USE_WINACCELERATOR
line in the IncludeAccelerator.h file in the main program subdirectory. This will make the WinApp unit either include the code for an accelerator table or leave it out.
Conclusion
I believe the above items are all the important points worth covering in order to help you understand the major workings of DWinLib, so you can either extend DWinLib or put together a wrapper of your own, if you are so inclined. The full source is included in the Zip file, and you can tear into that code to see all of the inner workings of DWinLib.
There are also many classes that have been included in the source that you are getting for free, and that are not documented here. There is a fairly robust 'undo/redo' mechanism included, and it has been incorporated into StupidSquares, so you can see how to use it from there. In addition, you can see how to activate the various AppWindows in the WinMenu::windowCallback
function. It interacts with a WinAppKeeper
object kept in the WinMainO
object. (I had fun with that one, and made it slightly non-standard!)
There is also a small WinIniFile wrapper for using INI files, a WinScrollbar
unit that was mentioned previously and is very slick, a WinFileHistory
unit that is a rudimentary unit for keeping a previous file list on a dropdown menu, and several more wrappers for other standard Windows controls. (The WinFileHistory
unit probably needs to be updated - it has been awhile since touching that one.) The core to DWinLib makes these wrappers fairly simple and straightforward. I hope that you enjoy the fruits of these labors.
One other thing: As I was editing this write-up for the fifteenth time or so, I finally realized that I could eliminate a complicated initialization step I used to have in the codebase. The change I made entailed switching over to using static boolean variables within WinBaseO
derived window ctors to determine whether the window class has already been registered. I have included all of my DWinLib work in the Zip files, but the windows that are included in those files and that are not used in the StupidSquares or Docking Window examples are still set up to use the old method, and will need to be modified.
You can get a glimmer of what changed by examining the WinApp::init
and WinMain
functions of the old code, and comparing them to the code in the Zip file. The WinApp::init
function has been entirely eliminated, so those old window units will need to be changed appropriately if you use them. I will make it a point to update this page if I change those pages on my site. Also, if you do this as an exercise, and send a copy my way, I'll update the Zip file. I may not get to it for awhile, myself, due to a heavy workload at this time, and those updates are not priority items to me at this time.
The future of DWinLib
It is my hope that you will find DWinLib to be a useful framework, and that it eases some of your tasks. I also hope that the above exposition is useful to those of you who may not be very familiar with the guts of the Windows API, and what is necessary in order to wrap that big beast into a usable framework.
There are many things that could be done to improve DWinLib from where it is now at, but I believe the current codebase is a strong beginning.
One issue you may not notice immediately is that when you maximize an application window, to the left of the minimize button there is a small separator graphic. I could only get the minimize, restore, and maximize buttons to become right-justified properly by inserting and right-justifying this separator, and adding the buttons after the separator. It may be possible to overcome this difficulty by making a separate menu for the AppWindow, and then merging them somehow when the window becomes maximized, but I have not played with that.
Another item I already mentioned is that it may be possible to achieve true multithreaded windows by playing with WinApp::winBeingCreatedC
, but it would take someone much more familiar with the pitfalls of multithreading than I currently am, in order to test out all of the potential problems.
Another item I don't know the answer to is how DWinLib interacts with XP and its themes. (I am using 2000 for my development and applications, and don't have access to an XP box.)
In addition to these items, there are many other Windows child controls that have yet to be wrapped, but as I follow the Yagni ('Ya ain't gonna need it') approach to doing things, and I haven't yet needed them, they are still just dreams. The docking framework could also be extended to include docking frames (I believe they are called), and other goodies to make it more like current docking frameworks.
Even if you do not use DWinLib, I hope that the above gave you ideas for your own projects, and that you may find some of the tricks useful.
If you do use DWinLib, on the other hand, maybe you will grace CodeProject with an article or two on some nifty additional controls for all of us to use within DWinLib.
Happy coding,
David
P.S. - I have the framework for a JIT compiler hacked out. It is on my very back burner at this point, but if enough people show interest in it (through donations at my site, or through a sponsorship), I will take the additional six to nine months I anticipate it taking, and modify it into a minimal x386 compiler, and do another write-up of this nature for CodeProject. The completed package would show every step involved, from parsing to actually executing the machine code. (It will not include any 3rd party tools, such as Flex or YACC. Everything will be programmed in pure C++.) The completed package would allow your program to execute programs typed in by your user, and not simply run their code via a virtual machine.
Also, if you appreciate all of the work that has gone into this, I would sincerely appreciate donations to help support this effort as well. Thank you.
History
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 has given me, helping me further improve DWinLib and this article. Due to his work, I was able to get DWinLib working 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 much simpler than Eclipse's, as well as more intuitive. 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. I also beat my head into some other issues that are not intuitive to a beginner. After those two hours or so, I just gave up.)
David also indicated that he tried to get it working on VC6, but due to lack of standards compliance with that compiler (not due to non-standard code), was unable to do so. I am guessing that it has to do with using "for (int i=0; i<something...
" everywhere, and I refuse to go back to variables that are not scoped properly.
Another major change is that DWinLib is now 'Unicode' enabled. Again, thanks goes to David, for starting me down that road.
The last major change was the modification to the callback mechanism that was outlined in the Button and menu clicks section.
There are also various other minor changes made to DWinLib and this article that I won't go into details on.