Motivation
If you don't like tools that automatically create loads of useless code you mostly won't understand it because the authors didn't bother to document it. If you demand the most possible control of your code, if you like to force Windows looking your way, and if you are lazy enough, this tutorial is exactly for you.
Well, some months ago these were the reasons that made me think of an ultimate solution of a simple, expandable and easy-to-use GUI system. Actually I liked the ideas behind MFC like: separate class for each window type, handlers for messages instead of huge window procedures, but I wanted them in combination with the "low-level" nature of pure Win32 API.
Description
When you look at the source code, you will notice that the basic framework consists of two classes. (Note: Both classes have more or less detailed documentation in the source files. They were extracted from a larger project, and for the purpose of this tutorial, they were stripped down to substantial elements needed to present the basic principles behind them.)
The first class is winbase
, a class that represents a generic window (like CWnd
in MFC). Its core functionality relies on the standard creation method create()
, pre and post create initialization methods and message handlers. Message handlers are implemented as message specific, one handler for one type of message, except for the generic message handler on_message()
that provides a way of extending the functionality of the system without recompiling the whole API. The generic handler has also the ability to catch unprocessed messages. All functions in this class are virtual
.
The second class is winpool
that is a placeholder for all application windows and also acts as an application manager. This class has only static methods as there is no need for multiple instances of this class per application.
Now comes 'the magic' - the common window procedure passed to CreateWindowEx()
that gets called in create()
:
LRESULT CALLBACK window_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
winbase* wb = 0;
if(msg == WM_CREATE)
{
wb = winpool::get_created_window();
wb->post_create_window(hwnd);
return wb->on_create(wp, lp);
}
wb = winpool::get_window_from_hwnd(hwnd);
switch(msg)
{
case WM_LBUTTONUP:
return wb->on_lbuttonup(wp, lp);
case WM_RBUTTONUP:
return wb->on_rbuttonup(wp, lp);
case WM_CLOSE:
return wb->on_close(wp, lp);
case WM_DESTROY:
return wb->on_destroy(wp, lp);
case WM_NCDESTROY:
return wb->on_ncdestroy(wp, lp);
default:
if(wb)
return wb->on_message(msg, wp, lp);
else
return DefWindowProc(hwnd, msg, wp, lp);
}
}
Using the system
Well, the system is created, it should do something useful. For the purpose of this tutorial we will make a simple window that will react on mouse clicks and key strokes. So we create a subclass of the generic window, called MyWindow
. This window will center itself on the screen and it will also load and display the cross-hair cursor. Then we override the handlers so that when the user left-clicks the window, a message box pops up asking whether the user likes to close the window. When the user right-clicks the window a message box pops up showing the coordinates of the click, and when the user strokes a key a message box pops up showing the ASCII value of the key being stroked. Making the application run is then a piece of cake:
int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int showcmd)
{
winpool::open_session(hinst);
winpool::register_class(hinst, "MyWindow");
MyWindow wnd;
int dims[] = {0, 0, 640, 480};
wnd.pre_create_window(hinst, "MyWindow");
wnd.create(NULL, "Tutorial 1 - Use mouse buttons"
" and keyboard keys", dims);
wnd.show_window(true);
while(winpool::dispatch_message()){ }
winpool::unregister_class(hinst, "MyWindow");
winpool::close_session();
return 1;
}
Last Words
As you can see we ended up with a running GUI system that is easy-to-use and can be further expanded. Now you should understand how the "wheels are turning" in the Win32 Pro package (you can find this package here - feel free to download it and experiment with it). See the next tutorial, where I try to explain how I implemented the support for dialogs, custom and subclassed controls.