Windows API Tutorials

Windows API Tutorials

The tutorials start with the most basic Windows program, the windows equivalent of "hello world!", Winnie. Then we move on to a more Generic program, which serves as a skeleton for a simple Windows application. Then we discuss various Controls. Using these controls one can build a complete dialog-based application, whose main window is a dialog. We are then ready to discuss a Generic Dialog, a framework with which one can quickly build specific dialogs.

To do some simple graphics one needs a Canvas object. You use Pens and Brushes to paint on the canvas.

More advanced topics include programming with Threads with a practical example of a Folder Watcher — a program that watches directories for changes.

Modern windows programming requires some knowledge of the Shell API, which leads us to the discussion of OLE and COM. We show how one can encapsulate OLE in C++ to create Smart OLE. OLE is also used in Automation, which lets your application talk to other applications.

Controls are nothing but specialized windows. You can easily create your own controls, as evidenced by the Splitter Bar tutorial.

For graphics programmer, we give a short introduction on using Bitmaps for animation and a more advanced tutorial on using Direct Draw.

Subscription and off-line browsing

You can subscribe to our tutorials directly, or you can view our tutorials off-line by using the subscription option in your browser. Both Microsoft Internet Explorer 4.0 and Netscape Navigator 4.0 or later have this option.

Some of you asked why we don't provide a zipped version of the tutorials. Unfortunately we don't have the resources to keep both versions in synch. Sorry!

Here is how you subscribe to tutorials in IE 4.0:

• Add current page to your Favorites and

• Choose the option to subscribe by clicking on "Yes, notify me of updates and download the page for offline viewing."

• Click on "Customize,"

• Choose "Download this page and pages linked to it."

• Choose link depth 1 to get all the windows tutorials (or 2, if you want the source code for the examples).

Updating Win API Tutorial

The tutorial is less than 0.5 MB and the book is about a megabyte, and you can do the downloading during off-peak hours. To update the pages on your computer,

• Select Manage Subscriptions from the Favorites menu.

• Select the subscription(s) you want to refresh and

• Click on the Update button.

When you are ready to browse, Select Work Off-line from your browser's File menu and go to your Favorites menu.

Subscribing to C++ In Action

Follow instructions above to subscribe to each chapter separately using depth 3. Subscribing to the top page will result in following the links to Amazon, Barnes & Noble, and Microsoft.

The Simplest Windows Program

Before you can even begin thinking about programming in windows, you have to be able to understand how this simple program works. Remember to compile this and other windows programs with the STRICT compilation flag defined!

Note: This is a Win32 program — it will run under Windows 95 and Windows NT (if they want you to program for the 16-bit platform, they should pay you twice as much!). Windows API calls are highlighted in blue and windows specific data types are shown in green. I will also usually put a double colon in front of API calls. In C++, that simply means that i'm calling a global function, in case there is some ambiguity.

Remember to compile sources as a Windows application. For instance, in Visual C++ select File.New.Projects.Win32 Application. Otherwise you'll get the error: unresolved external _main. (I provided a project file for those of you who use MS VC++ 6.0)

First, you have to define a class of windows that will be displayed by your application. In our case we will display only one window, but still, we need to give Windows some minimal information about its class. The most important part of the WinClass is the address of the callback procedure, or the window procedure. Windows is supposed to call us —it sends messages to our program by calling this procedure.

Notice the declaration of WindowProcedure. windows will call us with a handle to the window in question, the message, and two data items associated with the message, the paramters, wparam and lparam.

In WinClass we also have to specify things like the program instance handle HINSTANCE, the mouse cursor (we just load the standard arrow cursor), the brush to paint the window's background (we chose the default window color brush), and the name of our class.

Once all the fields of WNDCLASS are filled, we register the class with the Windows system.

#include <windows.h>

LRESULT CALLBACK WindowProcedure(HWND hwnd, unsigned int message, WPARAM wParam, LPARAM lParam);

class WinClass {

public:

WinClass (WNDPROC winProc, char const * className, HINSTANCE hInst);

void Register () {

::RegisterClass (&_class);

}

private:

WNDCLASS _class;

};

WinClass::WinClass(WNDPROC winProc, char const * className, HINSTANCE hInst) {

_class.style = 0;

_class.lpfnWndProc = winProc; // window procedure: mandatory

_class.cbClsExtra = 0;

_class.cbWndExtra = 0;

_class.hInstance = hInst; // owner of the class: mandatory

_class.hIcon = 0;

_class.hCursor = :: LoadCursor (0, idc_arrow); // optional

_class.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); // optional

_class.lpszMenuName = 0;

_class.lpszClassName = className;

// mandatory

}

Once the Window Class is registered, we can proceed with the creation of a window. This is done by calling the CreateWindow API. It takes a lot of arguments, the name of the window class that we have just registered, the caption that will appear in the title bar, style, position and size, and the application instance. the rest of the arguments, for the time being, will be left equal to zero.

The window will not appear on the screen until you tell Windows to show it.

class WinMaker {

public:

WinMaker (): _hwnd (0) {}

WinMaker (char const * caption, char const * className, HINSTANCE hInstance);

void Show (int cmdShow) {

::ShowWindow (_hwnd, cmdshow);

::UpdateWindow (_hwnd);

}

protected:

HWND _hwnd;

};

WinMaker::WinMaker (char const * caption, char const * className, HINSTANCE hInstance) {

_hwnd = :: CreateWindow (

className, // name of a registered window class

caption, // window caption

WS_OVERLAPPEDWINDOW, // window style

CW_USEDEFAULT, // x position

CW_USEDEFAULT, // y position

CW_USEDEFAULT, // witdh

CW_USEDEFAULT, // height

0, // handle to parent window

0, // handle to menu

hInstance, // application instance

0); // window creation data

}

A Windows program is event-driven. It means that you, as a programmer, are supposed to be on the defensive. the user will bombard windows with various input actions, and windows will bombard your program with messages corresponding to these actions. All you have to do is to respond to these messages. The picture below shows schematically how it works.

Windows gets various events from the keyboard, the mouse, the ports, etc. Each event is quickly converted into a message. Windows dispatches messages to appropriate windows. For instance, all keyboard messages go to the window that currently has the input focus (the active window). Mouse messages are dispatched according to the position of the mouse cursor. They usually go to the window that is directly under the cursor (unless some program captured the mouse).

All these messages end up in message queues. Windows keeps a message queue for every running application (actually, for every thread). It is your duty to retrieve these messages one-by-one in what is called a message loop. Your program has to call GetMessage to retrieve a message. then you call DispatchMessage to give it back to windows. couldn't windows just go ahead and dispatch all these messages itself? in principle it could, but a message loop gives your program a chance to have a peek at them and maybe perform some additional actions before dispatching them. or not…

Each message is addressed to a particular window. When you tell Windows to dispatch such a message, it will figure out the class of this window, find the associated Window Procedure, and call it. Every single message sent to our window will end up in our window procedure. It is now up to us to react to it. So, do we have to respond appropriately to every possible type of Windows message? There a hundreds of them! Fortunately, no! We only need to intercept these messages that we are interested in. Everything else we pass back to Windows for default processing using DefWindowProc.

Let's have a look at WinMain. the execution of a windows program doesn't start in main-it starts in WinMain. First, we create a winclass and register it. Then we create an actual window (of the class we've just registered) and show it. Actually, WinMain is called with the appropriate show directive — the user might want to start the application minimized or maximized. So we just follow this directive. Next we enter the message loop and keep retrieving and dispatching messages until GetMessage returns 0. at that point the message's wparam will contain the return code of the whole program.

int WINAPI WinMain (hinstance hinst, hinstance hprevinst, char * cmdParam, int cmdShow) {

char className [] = "Winnie";

WinClass winClass (WindowProcedure, className, hInst);

winClass.Register ();

WinMaker win ("Hello Windows!", className, hInst);

win.Show (cmdShow);

MSG msg;

int status;

while ((status = :: GetMessage (& msg, 0, 0, 0)) != 0) {

if (status == –1) return –1;

:: DispatchMessage (& msg);

}

return msg.wParam;

}

The GetMessage API is an interesting example of the bizarre microsoft Troolean (as opposed to traditional, Boolean) logic. GetMessage is defined to return a bool, but the documentation specifies three types of returns, non-zero, zero and –1. I am not making it up! here's an excerpt from the help file:

• If the function retrieves a message other than WM_QUIT, the return value is nonzero.

• If the function retrieves the WM_QUIT message, the return value is zero.

• If there is an error, the return value is -1.

The other part of a Windows program is the Windows Procedure. Remember, Windows will call it with all kinds of messages. All these messages can be ignored by sending them to DefWindowProc. There is only one message that we must intercept. That's the WM_DESTROY message that is sent by Windows when the user decides to close the window (by pressing the close button in the title bar). The standard response to WM_DESTROY is to post the quit message and return zero. That's all there is to it.

// Window Procedure called by Windows

LRESULT CALLBACK WindowProcedure (HWND hwnd, unsigned int message, WPARAM wParam, LPARAM lParam) {

switch (message) {

case WM_DESTROY:

:: PostQuitMessage (0);

return 0;

}

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

}

This is actually the ugliest, least object oriented part of encapsulating Windows. It's getting better from this point on. Like in this generic Windows program.

The Generic Windows Program

This program uses  the basic set of classes that encapsulate the Windows API.

• Controller — The bridge between Window Procedure and Object Oriented world.

• View — Encapsulates the output of a Windows program.

• Canvas — Encapsulated various Device Contexts and things you can do with them.

• Model — The worker and the brain of your program. Doesn't deal with Windows at all.

Note: This is a Win32 program — it will run under Windows 95 and Windows NT.

Note: _set_new_handler is Microsoft-specific. If you're using some other compiler, just remove this line of code. According to current C++ standard, operator new should throw exceptions anyway.

Note: Older compilers might have problems with templates. In such case, you may substitute the use of Win[Get/Set]Long templates with direct calls to Get/SetWindowLong. For instance, instead of calling:

Controller * pCtrl = WinGetLong (hwnd);

you can call:

Controller * pCtrl = reinterpret_cast<Controller *> (::GetWindowLong (hwnd, GWL_USERDATA));

Let's start with WinMain where we create the window class and the top window of our application. i have encapsulated these actions inside two classes: WinClass and WinMaker. WinClass can also tell us if there already are running instances of our program. When something like that happens, in our example, we will simply activate the previously running instance of the program and exit. You should do that when you want only one instance of your program running at a time.

Once the top window is successfully created, we enter the message loop. Notice that this time we are processing keyboard shortcuts by calling TranslateMessage. That's because our program has menu items that can be accessed using Alt+key combinations.

Another interesting twist in this program is that we are no longer using strings to name our resources — we use numerical ids. More than that-even when the API's call for strings, like the name of the Windows class or the caption, we store the strings in string resources and access them through ids. Your Windows development environment most likely has a resource editor that lets you create icons, menus, and string resources and assign them appropriate numerical ids. Symbolic names for these ids are stored in a header file produced by such an editor — in our case it's called resource.h.

The constant, ID_MAIN, for instance, refers to the main program's icons (large and small in the same resource), the main menu, and the string with the Windows class name. ID_CAPTION refers to the window caption string. Such organization promotes code reusability, not to mention the ease of localization.

int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrevInst, char * cmdParam, int cmdShow) {

_set_new_handler (& NewHandler);

// Using exceptions here helps debugging your program

// and protects from unexpected incidents.

try {

// Create top window class

TopWinClass topWinClass (ID_MAIN, hInst, MainWndProc);

// Is there a running instance of this program?

HWND hwndOther = topWinClass.GetRunningWindow ();

if (hwndOther != 0) {

::SetForegroundWindow (hwndOther);

if (::IsIconic (hwndOther)) ::ShowWindow (hwndOther, SW_RESTORE);

return 0;

}

topWinClass.Register ();

// Create top window

ResString caption (hInst, ID_CAPTION);

TopWinMaker topWin (topWinClass, caption);

topWin.Create ();

topWin.Show (cmdShow);

// The main message loop

MSG msg;

int status;

while ((status = ::GetMessage (&msg, 0, 0, 0)) != 0) {

if (status == –1) return –1;

::TranslateMessage (&msg);

::DispatchMessage (&msg);

}

return msg.wParam;

}

catch ( WinException e ) {

char buf [50];

wsprintf (buf, "%s, Error %d", e.GetMessage (), e.GetError ());

::MessageBox (0, buf, "Exception", MB_ICONEXCLAMATION | MB_OK);

}

catch (…) {

::MessageBox (0, "Unknown", "Exception", MB_ICONEXCLAMATION | MB_OK);

}

return 0;

}

Let's have a look at the WinClass class. It encapsulates a Windows-defined structure called WNDCLASSEX and provides reasonable defaults for all its fields. It is derived from a simple WinSimpleClass class, which you might use to encapsulate some built-in Windows classes (like buttons, list views, etc.).

I have provided examples of methods that can be used to override the defaults. For instance, SetBgSysColor changes the default background color of the user area of the window to one of the predefined system colors. The method SetResIcons loads appropriate icons from resources and attaches them to the window class. These icons will then appear in the upper left corner of the main window and on the windows' taskbar.

TopWinClass derived from WinClass makes use of this method. It also assigns the menu to the top Window class.

class WinSimpleClass {

public:

WinSimpleClass (char const * name, HINSTANCE hInst) : _name (name), _hInstance (hInst) {}

WinSimpleClass (int resId, HINSTANCE hInst); char const * GetName () const {

return _name.c_str ();

}

HINSTANCE GetInstance () const {

return _hInstance;

}

HWND GetRunningWindow ();

protected:

HINSTANCE _hInstance;

std::string _name;

};

WinSimpleClass::WinSimpleClass (int resid, hinstance hinst) : _hInstance (hInst) {

ResString resStr (hInst, resId);

_name = resStr;

}

HWND WinSimpleClass::GetRunningWindow () {

HWND hwnd = :: FindWindow (getname (), 0);

if (:: IsWindow (hwnd)) {

HWND hwndPopup = :: GetLastActivePopup (hwnd);

if (:: IsWindow (hwndpopup)) hwnd = hwndPopup;

} else hwnd = 0;

return hwnd;

}

class WinClass: public WinSimpleClass {

public:

WinClass (char const * className, HINSTANCE hInst, WNDPROC wndProc);

WinClass (int resId, HINSTANCE hInst, WNDPROC wndProc);

void SetBgSysColor (int sysColor) {

_class.hbrBackground = reinterpret_cast<HBRUSH> (sysColor + 1);

}

void SetResIcons (int resId);

void Register ();

protected:

void SetDefaults ();

WNDCLASSEX _class;

};

WinClass::WinClass (char const * className, HINSTANCE hInst, WNDPROC wndProc) : WinSimpleClass (className, hInst) {

_class.lpfnWndProc = wndProc;

SetDefaults ();

}

WinClass::WinClass (int resId, HINSTANCE hInst, WNDPROC wndProc) : WinSimpleClass (resId, hInst) {

_class.lpfnWndProc = wndProc;

SetDefaults ();

}

void WinClass::SetDefaults () {

// Provide reasonable default values

_class.cbSize = sizeof (WNDCLASSEX);

_class.style = 0;

_class.lpszClassName = GetName ();

_class.hInstance = GetInstance ();

_class.hIcon = 0;

_class.hIconSm = 0;

_class.lpszMenuName = 0;

_class.cbClsExtra = 0;

_class.cbWndExtra = 0;

_class.hbrBackground = reinterpret_cast<HBRUSH> (COLOR_WINDOW + 1);

_class.hCursor = ::LoadCursor (0, IDC_ARROW);

}

void WinClass::SetResIcons (int resid) {

_class.hIcon = reinterpret_cast<HICON> (

:: LoadImage (

_class.hInstance,

MAKEINTRESOURCE (resId),

IMAGE_ICON,

:: GetSystemMetrics (sm_cxicon),

:: GetSystemMetrics (sm_cyicon),

0));

// Small icon can be loaded from the same resource

_class.hIconSm = reinterpret_cast<HICON> (

:: LoadImage (

_class.hInstance,

MAKEINTRESOURCE (resId),

IMAGE_ICON,

:: GetSystemMetrics (sm_cxsmicon),

:: GetSystemMetrics (sm_cysmicon),

0));

}

void WinClass::Register () {

if (:: RegisterClassEx (&_class) == 0) throw WinException ("Internal error: RegisterClassEx failed.");

}

class TopWinClass: public WinClass {

public:

TopWinClass (int resId, HINSTANCE hInst, WNDPROC wndProc);

};

TopWinClass::TopWinClass (int resId, HINSTANCE hInst, WNDPROC wndProc) : WinClass (resId, hInst, wndProc) {

SetResIcons (resId);

_class.lpszMenuName = MAKEINTRESOURCE (resId);

}

Once the Windows class is registered with the system, you can create as many windows of this class as you wish. They will, of course, share the same Window procedure that was registered with the class. We'll see later how we can distinguish between various instances of the window inside the procedure.

The class WinMaker works in much the same way as WinClass. Its constructor provides sensible defaults that may be overriden by calling particular methods. Once everything is set, you call the Create method to create the window and the Show method to display it. notice that the moment you call create, your window procedure is called with the wm_create message.

The top window is created using TopWinMaker class, which provides the appropriate style and caption.

class WinMaker {

public:

WinMaker (WinClass & winClass);

operator HWND () {

return _hwnd;

}

void AddCaption (char const * caption) {

_windowName = caption;

}

void AddSysMenu () {

_style |= WS_SYSMENU;

}

void AddVScrollBar () {

_style |= WS_VSCROLL;

}

void AddHScrollBar () {

_style |= WS_HSCROLL;

}

void Create ();

void Show (int nCmdShow = SW_SHOWNORMAL);

protected:

WinClass & _class;

HWND _hwnd;

DWORD _exStyle; // extended window style

char const * _windowName; // pointer to window name

DWORD _style; // window style

int _x; // horizontal position of window

int _y; // vertical position of window

int _width; // window width

int _height; // window height

HWND _hWndParent; // handle to parent or owner window

HMENU _hMenu; // handle to menu, or child-window ID

void * _data; // pointer to window-creation data

};

WinMaker::WinMaker (WinClass & winClass) :

_hwnd (0), _class (winClass), _exStyle (0), // extended window style

_windowName (0), // pointer to window name

_style (WS_OVERLAPPED), // window style

_x (CW_USEDEFAULT), // horizontal position of window

_y (0), // vertical position of window

_width (CW_USEDEFAULT), // window width

_height (0), // window height

_hWndParent (0), // handle to parent or owner window

_hMenu (0), // handle to menu, or child-window identifier

_data (0) // pointer to window-creation data { }

void WinMaker::Create () {

_hwnd = :: CreateWindowEx (

_exStyle,

_class.GetName (),

_windowName,

_style,

_x,

_y,

_width,

_height,

_hWndParent,

_hMenu,

_class.GetInstance (),

_data);

if (_hwnd == 0) throw WinException ("Internal error: Window Creation Failed.");

}

void WinMaker::Show (int ncmdshow) {

::ShowWindow (_hwnd, nCmdShow);

::UpdateWindow (_hwnd);

}

// Makes top overlapped window with caption

TopWinMaker::TopWinMaker ((WinClass & winClass, char const * caption)  : WinMaker (winClass) {

_style = WS_OVERLAPPEDWINDOW | WS_VISIBLE;

_windowName = caption;

}

Before we go any further, here are some light-weight classes of general interest. WinException is something that we want to throw any time a Windows API fails. It takes care of retrieving the windows error code. (By the way, there is an easy way to convert the error code to a string using FormatMessage API.)

The class ResString is a simple encapsulation of a string stored in the string resources of your application.

// The exception class: stores the message and the error code

class WinException {

public:

WinException (char* msg) : _err (::GetLastError()), _msg(msg) {}

DWORD GetError() const {

return _err;

}

char const * GetMessage () const {

return _msg;

}

private:

DWORD _err;

char * _msg;

};

// The out-of-memory handler: throws exception

int NewHandler (size_t size) {

throw WinException ( "Out of memory" );

return 0;

}

class ResString {

enum { MAX_RESSTRING = 255 };

public:

ResString (HINSTANCE hInst, int resId);

operator char const * () { return _buf; }

private:

char _buf [MAX_RESSTRING + 1];

};

ResString::ResString (hinstance hinst, int resid) {

if (!:: LoadString (hinst, resid, _buf, max_resstring + 1)) throw WinException ("Load String failed");

}

The Controller is the nervous system of a particular window instance. It is created with that window, stored with it and finally destroyed with it. You can put any state information pertaining to a particular window instance in its controller. In general, the controller has a view that deals with painting on the window's surface and it has access to the model, which is the brain of your application (this is called the MVC, or Model-View-Controller pattern invented by Smalltalk programmers).

If, as it often happens, your application has only one top-level window, you can directly embed the model in its controller. That simplifies the management of resources but at the cost of tight coupling of the controller with the model. In large projects one should avoid such couplings-the use of a (smart) pointer to the model inside the controller is then preferred.

Most controller methods require a handle to the window on which behalf they are operating. This handle is passed with every Windows message, but it is simpler to store it once inside the controller object and use it whenever necessary. Remember-there is a one-to-one correspondence between window instances (and therefore window handles) and controller objects.

class Controller {

public:

Controller(HWND hwnd, CREATESTRUCT * pCreate);

~Controller ();

void Size (int x, int y);

void Paint ();

void Command (int cmd);

private:

HWND _hwnd;

Model _model;

View _view;

};

The Window Procedure is the main switchboard of a windows application. You don't call it from your program — Windows calls it! Every time something interesting happens, Windows sends your program a message. This message is passed to your Window procedure. You may deal with it, or you may pass it on to the default window procedure.

Window procedure is called with a handle to a window to which a given message is directed. This handle uniquely identifies an internal Windows' data structure that corresponds to a given window instance. It so happens that we can access this data structure and use it to store some instance-specific data. Here's the type safe way of accessing this structure. By the way, the GWL_USERDATA member of this structure is guaranteed to be present in all windows, including message boxes, dialog boxes and even buttons.

template <class T>

inline T WinGetLong (hwnd hwnd, int which = gwl_userdata) {

return reinterpret_cast<T> (::GetWindowLong (hwnd, which));

}

template <class T>

inline void WinSetLong (hwnd hwnd, t value, int which = gwl_userdata) {

:: SetWindowLong (hwnd, which, reinterpret_cast<long> (value));

}

Every time Windows calls our window procedure, we want to first retrieve its controller object. Remember, there may be several windows sharing the same window procedure and we want to have a separate controller for each window. How do we know which controller to use when we are called? We find out by looking at the window handle. In this handle we store the pointer to this particular window's controller using the Win[Set/Get]Long trick.

The Window procedure is first called with the WM_CREATE message. At that time we create the controller object and initialize it with the window handle and a special data structure called CREATESTRUCT that is passed to us by Windows. once we have the controller, we store the pointer to it in the corresponding internal Windows' data structure under the label of the current hwnd. next time the window procedure is called, with a message other than WM_CREATE, we simply retrieve the pointer to our controller using the hwnd.

The rest is easy. The Window procedure interprets message parameters and calls the appropriate methods of the controller.

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {

Controller * pCtrl = WinGetLong<Controller *> (hwnd);

switch (message) {

case WM_CREATE:

// Have to catch exception in case new throws!

try {

pCtrl = new Controller (hwnd, reinterpret_cast<CREATESTRUCT *> (lParam));

WinSetLong<Controller *> (hwnd, pCtrl);

}

catch (WinException e) {

:: MessageBox (hwnd, e.GetMessage(), "Initialization",   MB_ICONEXCLAMATION | MB_OK);

return –1;

}

catch (…) {

:: MessageBox (hwnd, "Unknown Error", "Initialization", MB_ICONEXCLAMATION | MB_OK);

return –1;

}

return 0;

case WM_SIZE:

pCtrl->Size (LOWORD(lParam), HIWORD(lParam));

return 0;

case WM_PAINT:

pCtrl->Paint ();

return 0;

case WM_COMMAND:

pCtrl->Command (LOWORD (wParam));

return 0;

case WM_DESTROY:

WinSetLong<Controller *> (hwnd, 0);

delete pCtrl;

return 0;

}

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

}

Here are examples of simple implementations of a few controller methods. The constructor has to remember the handle to the window for later use, the destructor has to post the quit message, The Size method passes its argument to View, etc. we'll talk about painting the window a little later. for now, notice that the controller prepares the paint canvas for the View to work with.

Controller::Controller (HWND hwnd, CREATESTRUCT * pCreate) :_hwnd (hwnd), _model ("Generic") { }

Controller::~Controller () {

:: PostQuitMessage(0);

}

void Controller::Size (int cx, int cy) {

_view.SetSize (cx, cy);

}

void Controller::Paint () {

// prepare the canvas and let View do the rest

PaintCanvas canvas (_hwnd);

_view.Paint (canvas, _model);

// Notice: The destructor of PaintCanvas called automatically!

}

When the user selects one of the menu items, Window procedure is called with the message WM_COMMAND. The appropriate controller method dispatches the command based on the command id. When you create the menu using your resource editor, you pick these command ids for each menu item. They are stored in the appropriate header file (resource.h in our case) that has to be included in the controller's source file.

Our menu contains only three items with the ids IDM_EXIT, IDM_HELP, and IDM_ABOUT. the dialog box that is displayed in response to IDM_ABOUT is also created using the resource editor and given the id IDD_ABOUT. Its dialog procedure is AboutDlgProc.

Finally, in order to display a dialog box we need the handle to the application instance. The standard way to retrieve it is to access the internal Windows data structure using the appropriate hwnd.

// Menu command processing

void Controller::Command (int cmd) {

switch (cmd) {

case IDM_EXIT:

:: SendMessage (_hwnd, WM_CLOSE, 0, 0L);

break;

case IDM_HELP:

:: MessageBox (_hwnd, "Go figure!", "Generic", MB_ICONINFORMATION | MB_OK);

break;

case IDM_ABOUT:

{

// Instance handle is available through HWND

HINSTANCE hInst = WinGetLong<HINSTANCE> (_hwnd, GWL_HINSTANCE);

:: DialogBox (hInst, MAKEINTRESOURCE (IDD_ABOUT), _hwnd, AboutDlgProc);

}

break;

}

}

The view object usually stores the dimensions of the client area. They are updated whenever the controller processes the WM_SIZE message. The first WM_SIZE message is sent during window creation and before WM_PAINT, so we can safely assume that when Paint is called, the dimensions of the client area are known.

Graphical output to the window is done by calling appropriate methods of the Canvas object. In our case, we print text obtained from the model and draw a vertical line ten pixels from the left edge of the client area.

class View {

public:

void SetSize (int cxNew, int cyNew) {

_cx = cxNew;

_cy = cyNew;

}

void Paint (Canvas & canvas, Model & model);

protected:

int _cx;

int _cy;

};

void View::Paint (Canvas & canvas, Model & model) {

canvas.Text (12, 1, model.GetText(), model.GetLen());

canvas.Line (10, 0, 10, _cy);

}

The canvas object encapsulates what is called a Device Context in Windows parlance. Our Canvas is very simple, it only knows how to print text and draw lines, but your Canvas can have many more methods that do creative things. We'll talk about Canvas more in one of the next tutorials.

class Canvas {

public:

operator HDC () { return _hdc; }

void Line ( int x1, int y1, int x2, int y2 ) {

:: MoveToEx (_hdc, x1, y1, 0);

:: LineTo (_hdc, x2, y2);

}

void Text (int x, int y, char const * buf, int cBuf) {

:: TextOut ( _hdc, x, y, buf, cBuf );

}

void Char (int x, int y, char c) {

:: TextOut (_hdc, x, y, & c, 1);

}

protected:

// Protected constructor: You can't construct

// a Canvas object, but you may be able

// to construct objects derived from it.

Canvas (HDC hdc): _hdc (hdc) {}

HDC _hdc;

};

The canvas that you create in response to WM_PAINT message are of special kind. They obtain the device context by calling BeginPaint and release it by calling EndPaint. paintstruct contains additional information about which parts of the user area should be painted, etc. For now we ignore such details, but if you're serious about performance, you should learn more about it.

// Concrete example of canvas.

// Create this object after WM_PAINT message

class PaintCanvas: public Canvas {

public:

// Constructor obtains the DC

PaintCanvas (HWND hwnd) : Canvas (:: BeginPaint (hwnd, & _paint)), _hwnd (hwnd) {}

// Destructor releases the DC

~PaintCanvas () {

:: EndPaint(_hwnd, & _paint);

}

protected:

PAINTSTRUCT _paint;

HWND _hwnd;

};

What would Windows programming be without controls?

Windows Controls

Controls can be added to the main window or to any dialog box in your program. Controls are best picked and positioned using a graphical resource editor. Such an editor will also let you pick names or symbolic id's for your controls. You will then use these id's to identify the controls in your program.

Most controls can be encapsulated in objects that are either embedded in the appropriate Controller (you can have a separate Controller objects for every dialog box in your program) or, for static controls, in the View.

Controller objects are created in response to WM_CREATE or, for dialog boxes, WM_INITDIALOG messages. Constructors of controls embedded in these Controllers are executed at that time.

The base class for most controls is SimpleControl. It obtains and stores the window handle of the particular control. To obtain this handle, you need the parent window handle and the control's id.

class SimpleControl {

public:

SimpleControl (HWND hwndParent, int id) : _hWnd (GetDlgItem (hwndparent, id)) {}

void SetFocus () {

::SetFocus (_hwnd);

}

HWND Hwnd () const { return _hWnd; }

protected:

HWND _hWnd;

};

Here's an example of an edit control

class Edit: public SimpleControl {

public:

Edit (HWND hwndParent, int id) : SimpleControl (hwndParent, id) {}

void SetString (char* buf) {

SendMessage (Hwnd (), WM_SETTEXT, 0, (LPARAM) buf);

}

// code is the HIWORD (wParam)

static BOOL IsChanged (int code) {

return code == EN_CHANGE;

}

int GetLen () {

return SendMessage (Hwnd (), WM_GETTEXTLENGTH, 0, 0);

}

void GetString (char* buf, int len) {

SendMessage (Hwnd (), WM_GETTEXT, (WPARAM) len, (LPARAM) buf);

}

void Select () {

SendMessage (Hwnd (), EM_SETSEL, 0, –1);

}

};

This is how the edit control may be used:

class Controller {

public:

Controller(HWND hwnd);

…

private:

Edit _edit;

char _string [maxLen];

};

Controller::Controller (hwnd hwnd) : _edit (hwnd, IDC_EDIT) {

_edit.SetFocus ();

…

}

void Controller::Command (HWND hwnd, WPARAM wParam, LPARAM lParam) {

switch (LOWORD(wParam)) {

case IDC_EDIT:

if (_edit.IsChanged(HIWORD (wParam))) {

_edit.GetString (_string, maxLen);

}

break;

…

}

}

But, of course, the most likely place to use controls is in a Dialog Box.

Program with a Dialog Box as the Main Window

The main window of a program doesn't have to be a resizable general purpose window. many small applications work better in a dialog box format. The obvious advantage of such an approach is that you can use a resource editor to arrange all your controls on the surface of the box. This is in fact how the UI of the Frequency Analyzer was implemented. Since this is a useful technique, i will describe it in some detail.

First of all, we have to design the dialog box using the resource editor. We assign identifiers to all the controls and to the dialog itself. Here, the dialog resource has the identifier DLG_MAIN. In the WinMain procedure we don't have to register any window class, because Windows has a pre-defined class for dialog boxes. Instead of creating a window, we call CreateDialog, passing it a pointer to our own dialog procedure (explained later).

The message loop is non-standard in that it calls IsDialogMessage for every message. This API not only checks whether a given message is directed at the dialog box, but more importantly, it dispatches it to the dialog procedure. If the message was not for the dialog, we do the standard translation and dispatching.

For convenience, we keep the value of HINSTANCE in a global variable. This is actually a precursor of a more general object — the Application. Here, however, we decided it was too trivial to deserve a class of its own.

HINSTANCE TheInstance = 0;

int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrevInst, char * cmdParam, int cmdShow) {

TheInstance = hInst;

_set_new_handler (& NewHandler);

HWND hDialog = 0;

hDialog = CreateDialog (hinst, MAKEINTRESOURCE (dlg_main), 0, DialogProc);

if (!hDialog) {

char buf [100];

wsprintf (buf, "Error x%x", GetLastError ());

MessageBox (0, buf, "CreateDialog", MB_ICONEXCLAMATION | MB_OK);

return 1;

}

MSG msg;

int status;

while ((status = GetMessage (& msg, 0, 0, 0)) != 0) {

if (status == –1) return –1;

if (! IsDialogMessage (hDialog, & msg)) {

TranslateMessage ( & msg );

DispatchMessage ( & msg );

}

}

return msg.wParam;

}

The dialog procedure is just like Windows procedure, except that it returns TRUE when it processes a message and FALSE when it doesn't. There is no need to call the default procedure, because Windows does it for us whenever the dialog procedure returns FALSE (makes you think, "Why wasn't the same design used in the window procedure…?"). The first message the dialog gets is WM_INITDIALOG and the last one is WM_CLOSE. During the processing of these messages we create and destroy the Controller. Other than that, the dialog expects messages from its controls-these are passed as WM_COMMAND. One control that requires special processing is a (horizontal) scrollbar — it sends the WM_HSCROLL message. There are scrollbar controls in the Frequency Analyzer and that's how they are dealt with.

BOOL CALLBACK DialogProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {

static Controller* control = 0;

switch (message) {

case WM_INITDIALOG:

try {

control = new Controller (hwnd);

}

catch (WinException e) {

MessageBox (0, e.GetMessage (), "Exception", MB_ICONEXCLAMATION | MB_OK);

PostQuitMessage(1);

}

catch (…) {

MessageBox (0, "Unknown", "Exception", MB_ICONEXCLAMATION | MB_OK);

PostQuitMessage(2);

}

return TRUE;

case WM_COMMAND:

control->Command(hwnd, LOWORD (wParam), HIWORD (wParam));

return TRUE;

case WM_HSCROLL:

control->Scroll (hwnd, LOWORD (wParam), HIWORD (wParam));

return 0;

case WM_DESTROY:

PostQuitMessage(0);

return TRUE;

case WM_CLOSE:

delete control;

DestroyWindow (hwnd);

return TRUE;

}

return FALSE;

}

Let's have a look at the Controller. Notice how every control on the face of the dialog box has a corresponding (embedded) control object inside the Controller. There are edits, combos, radio buttons and scrollbars. There is a special metafile control that draws the frequency scale and two view objects corresponding to two static panes upon which we draw the graphs. Finally we have the Painter object that is responsible for the asynchronous repainting of the two panes.

class Controller {

public:

Controller(HWND hwnd);

~Controller ();

void Command (HWND hwnd, int id, int code);

void Scroll (HWND hwnd, int cmd, int pos);

void Paint (HWND hwnd);

void ReInit (HWND hwnd);

void Stop (HWND hwnd);

private:

void InitScrollPositions ();

void PaintScale ();

BOOL _isStopped;

int _bitsPerSample;

int _samplesPerSecond;

int _fftPoints;

int _samplesPerBuf;

EditReadOnly _edit;

Combo _comboFreq;

Combo _comboPoints;

RadioButton _radio8;

RadioButton _radio16;

ScrollBarMap _scroll;

StaticEnhMetafileControl _scaleFreq;

ViewWave _viewWave;

ViewFreq _viewFreq;

Painter _display;

};

The constructor of the Controller takes care of the initialization of all the controls by passing them the handle to the dialog box window and the appropriate identifiers. As a matter of cosmetics, we attach our own icon to the dialog-otherwise the system would use the standard Windows icon.

Controller::Controller (HWND hwnd) :_isStopped (TRUE), _bitsPerSample (16), _samplesPerSecond (SAMPLES_SEC), _fftPoints (FFT_POINTS * 4), _samplesPerBuf (FFT_POINTS * 2), _radio8 (hwnd, IDC_8_BITS), _radio16 (hwnd, IDC_16_BITS), _scroll (hwnd, IDC_SCROLLBAR), _edit (hwnd, IDC_EDIT), _comboFreq (hwnd, IDC_SAMPLING), _comboPoints (hwnd, IDC_POINTS), _viewWave (hwnd, IDS_WAVE_PANE, FFT_POINTS * 8), _viewFreq (hwnd, IDS_FREQ_PANE), _scaleFreq (hwnd, IDC_FREQ_SCALE), _display (hwnd, _viewWave, _viewFreq, _samplesPerBuf, _samplesPerSecond, _fftPoints) {

// Attach icon to main dialog

HICON hIcon = LoadIcon (TheInstance, MAKEINTRESOURCE (ICO_FFT));

SendMessage (hwnd, WM_SETICON, WPARAM (ICON_SMALL), LPARAM (hIcon));

// Other initializations…

}

Using a dialog box as the main window is a very handy and simple technique, especially for applications that display a panel-like interface. By the way, the Battleship app (see the home page) uses the same trick. You may now continue to the next tutorial on the use of dialog boxes in Windows applications.

Dialog Box

Dialog box is for a Windows program what a function call is for a C program. First, a Windows programs passes some data to the dialog box in order to initialize it. Next, the dialog box solicits information from the user. When the user decides that the program's curiosity has been satisfied, he or she clicks the OK button. The new data is then returned back to the program.

A dialog box is usually spiked with all kinds of controls (widgets, gadgets, gizmos, whatever you call them). Dialog box code interacts with these gizmos by sending and receiving messages in its Dialog Box Procedure. There is a lot of common code in the implementation of various dialog boxes. We would like to write and test this code only once and then keep reusing it.

What varies from dialog to dialog is:

• The kind of data that is passed to and retrieved from the dialog box

• The kind of gizmos within the dialog box and

• The interactions between them.

This variability can be encapsulated in two custom client classes: the argument list class and the dialog controller class.

The argument list class encapsulates the in and out arguments and provides access for retrieving and changing them. The definition and the implementation of this class is under complete control of the client (the programmer who reuses our code).

The dialog controller class contains control objects for all the gizmos in the dialog and implements interactions between them. The client is supposed to derive this class from the abstract DlgController class and implement all its pure virtual methods.

The rest is just glue. We need to instantiate a template CtrlFactory class that is used by our generic implementation of the ModalDialog class. The controller factory creates the appropriate client-defined controller and returns a polymorphic pointer to it. Fig 1. shows the relationships between various classes involved in the implementation of a dialog box.

Fig 1. Classes involved in the dialog box design pattern.

Let's start with the client code. In our generic program we need a dialog box that lets the user edit a string. Using a resource editor, we create a dialog template that contains an edit control and two buttons, OK and CANCEL. The resource id of this dialog is IDD_EDITDIALOG. Next we define our own argument list class called EditorData and a special controller class called EditorCtrl. When the user selects the Edit menu item, the following code is executed:

void Controller::Edit (HWND hwnd) {

EditorData data (_model.GetText ());

ControllerFactory <EditorCtrl, EditorData> factory (& data);

ModalDialog dialog (_hInst, hwnd, IDD_EDITDIALOG, & factory);

if (dialog.IsOk ()) {

_model.SetText (data.GetName ());

// Force repaint

InvalidateRect (hwnd, 0, TRUE);

}

}

First, the EditorData object is created and initialized with a string. Next, the ControllerFactory template is instantiated. We parametrize it with the two client classes, EditorCtrl and EditorData. The factory object is initialized with a pointer to our data. Next, the ModalDialog object is created. it takes the pointer to our factory as an argument. it uses it to create the controller object and to retrieve data from the argument list. After the interaction with the user is done, we check whether the user blessed the results by clicking the ok button, and if so, we retrieve the result from the editor data object and incorporate it in our program. This is pretty much the standard way to set up a dialog box.

The EditorData class in our example is pretty simple.

class EditorData {

public:

enum { maxLen = 128 };

EditorData (char const * name) {

SetName (name);

}

BOOL IsNameOK () { return (_name[0] != '\0'); }

void SetName (char const *name) {

strcpy (_name, name);

}

char const *GetName () { return _name; }

private:

char _name [maxLen];

};

The controller class, EditorCtrl, is where all the action is. First of all, it has the Edit control embedded in it. This object is responsible for the interaction with the edit gizmo embedded in the dialog box. The gizmo has the id IDC_NAME_EDIT that we gave it in the resource editor. Second of all, the controller stores a pointer to EditorData. This pointer is retrieved from the base class DlgController. Three virtual methods of DlgController have to be overridden in our EditorControl. These are OnInitDialog, which is called immediately after the dialog has been initialized, OnCommand, which is called whenever any dialog box gizmo sends us a command and, finally, OnNotify, which is used by the new Windows95 controls.

class EditorCtrl : public DlgController {

public:

EditorCtrl (HWND hwndDlg, EditorData *argList) : DlgController (argList), _nameEdit (hwndDlg, IDC_NAME_EDIT) {

_dlgData = argList;

}

void OnInitDialog (HWND hwnd);

bool OnCommand (HWND hwnd, int ctrlID, int notifyCode);

bool OnNotify (HWND hwnd, int idCtrl, NMHDR *hdr);

private:

Edit  _nameEdit;

EditorData *_dlgData;

};

In the OnInitDialog method we retrieve the string that was passed in EditorData and use it to initialize the edit control.

void EditorCtrl::OnInitDialog (HWND hwnd) {

char const * name = _dlgData->GetName ();

_nameEdit.SetString (name);

}

OnCommand receives commands from various gizmos. The gizmos are identified by their control id. For instance, if the control id is IDC_NAME_EDIT, it means that something happend to the edit control. In our implementation we are a little overzelous and we react to every change by copying the whole string into EditorData object. There are cases though, when you need to keep validating the string or displaying it in some other control and then you really have to react to every change message.

When the user clicks the OK button, we get a command with the IDOK control id. We verify the string and, if it's okay, we end the dialog passing TRUE as the return code. When the control id is IDCANCEL (from the Cancel button) we end the dialog with the FALSE return code.

The OnNotify method does nothing in the case of pre-Windows95 controls, such as the edit control and the buttons.

bool EditorCtrl::OnCommand (HWND hwnd, int ctrlID, int notifyCode) {

switch (ctrlID) {

case IDC_NAME_EDIT:

if (_nameEdit.IsChanged (notifyCode)) {

char nameBuf [EditorData::maxLen];

int len = _nameEdit.GetLen();

if (len < EditorData::maxLen) {

_nameEdit.GetString (nameBuf, sizeof (nameBuf));

_dlgData->SetName (nameBuf);

}

return true;

}

break;

case IDOK:

if (_dlgData->IsNameOK ()) {

EndDialog(hwnd, TRUE);

} else {

MessageBox (hwnd, "Please, enter valid name", "Name Editor", MB_ICONINFORMATION | MB_OK);

}

return true;

case IDCANCEL:

EndDialog(hwnd, FALSE);

return true;

}

return false;

}

bool EditorCtrl::OnNotify (HWND hwnd, int idCtrl, NMHDR *hdr) {

return false;

}

Now that you know how to use it, let's have a look at the implementation of the whole dialog box pattern. The controller factory is a very simple template class. All it does it to accept the generic argument list and store it as a void pointer. Only the client-defined controller knows what the actual class of the argument list is and it will perform a cast (see the constructor of EditorCtrl).

The code common to all controller factories is isolated in the class CtrlFactory from which the actual template is derived. The template overrides the MakeController method to create a new controller of the client-defined class ActualCtrl. Notice however that the method returns ActualCtrl as a pointer to its base class DlgController and that's what the rest of the implementation sees.

class CtrlFactory{

public:

CtrlFactory (void *argList) : _argList (argList) {}

virtual DlgController * MakeController (HWND hwndDlg) = 0;

protected:

void *_argList;

};

template <class ActualCtrl, class ActualArgList>

class ControllerFactory : public CtrlFactory {

public:

ControllerFactory (void *argList) : CtrlFactory (argList) {}

DlgController * MakeController (HWND hwndDlg) {

return new ActualCtrl (hwndDlg, (ActualArgList *) GetArgList ());

}

};

Here is the definition of the abstract class DlgController that is used as a base for all client-defined controller classes. We've already seen how this derivation works in the case of the client class EditorCtrl.

class DlgController{

public:

virtual ~DlgController () {}

// In case derived class overrides

virtual void OnInitDialog (HWND hwnd) = 0;

virtual bool OnCommand (HWND hwnd, int ctrlID, int notifyCode) = 0;

virtual bool OnNotify (HWND hwnd, int idCtrl, NMHDR *hdr) = 0;

};

The central reusable piece of software is the ModalDialog class. It does all the work in its constructor by calling the DialogBoxParam Windows API. The parameter that we are passing to the dialog box (actually, to its dialog procedure) is the pointer to the controller factory. The dialog procedure is defined as a static method (no this pointer — the dialog procedure is callable from Windows, so it has no access to the this pointer).

class ModalDialog{

public:

ModalDialog (HINSTANCE hInst, HWND hwnd, int dlgResource, CtrlFactory *ctrlFactory)     {

_result = DialogBoxParam (hInst, MAKEINTRESOURCE (dlgResource), hwnd, (DLGPROC) ModalDialogProc, (LPARAM) ctrlFactory);

}

bool IsOk () const { return (_result == -1)? false: _result != 0; }

static BOOL CALLBACK ModalDialogProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

private:

int   _result;

};

Finally, the dialog procedure, common to all types of dialogs, is implemented to respond to three kinds of messages: WM_INITDIALOG, WM_COMMAND and WM_NOTIFY. Actually, all it does is to direct these messages to the controller object. It obtains the pointer to the polymorphic controller object for the first time by calling the MakeController method of the factory.

Notice that, from that point on, we let Windows keep track of the pointer to the controller. We store it as GWL_USERDATA — a special long that is associated with every window, in particular with our dialog, and accessible through its window handle.

template <class T>

inline T GetWinLong (HWND hwnd, int which = GWL_USERDATA) {

return reinterpret_cast<T> (::GetWindowLong (hwnd, which));

}

template <class T>

inline void SetWinLong (HWND hwnd, T value, int which = GWL_USERDATA) {

::SetWindowLong (hwnd, which, reinterpret_cast<long> (value));

}

We have to be careful, though. First of all, we need to deallocate the controller after we are done. We do it when processing WM_DESTROY.

Second of all, Windows has this unfortunate habit of sending WM_COMMAND and WM_NOTIFY messages before WM_INITDIALOG and after WM_DESTROY. What can I say — if I were the manager of the poor schmuck who's responsible for this "feature" things would have been different. As it is, we have to protect ourselves by testing if ctrl is non-zero before calling OnCommand and OnNotify.

BOOL CALLBACK ModalDialog::ModalDialogProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {

DlgController * ctrl = GetWinLong<DlgController *> (hwnd);

switch (message) {

case WM_INITDIALOG:

{

CtrlFactory *ctrlFactory = reinterpret_cast<CtrlFactory *> (lParam);

ctrl = ctrlFactory->MakeController (hwnd);

SetWinLong<DlgController *> (hwnd, ctrl);

ctrl->OnInitDialog (hwnd);

}

return TRUE;

case WM_COMMAND:

if (ctrl && ctrl->OnCommand (hwnd, LOWORD(wParam), HIWORD (wParam))) {

return TRUE;

}

break;

case WM_NOTIFY:

if (ctrl && ctrl->OnNotify (hwnd, wParam, (NMHDR *)lParam))  return TRUE;

break;

case WM_DESTROY:

delete ctrl;

SetWinLong<DlgController *> (hwnd, 0);

break;

}

return FALSE;

}

Here's the beauty of polymorphism in action. The factory object is created by the client using a template class. This object is passed to the constructor of ModalDialog. ModalDialog passes it to the dialog procedure as a void pointer (that's because it has to go through Windows). Dialog procedure gets it inside the WM_INITDIALOG message as LPARAM. After going through the digestive tract of Windows it has to be restored to its original shape by casting it back to the pointer to CtrlFactory — the base class of all controller factories.

When we call its virtual method MakeController, we are calling the method overridden in the class template ControllerFactory. It creates a new object of the client-defined class ActualCtrl. But again, it returns this object to us disguised as a generic pointer to DlgController. So whenever we call any of the virtual methods of ctrl, we are executing client overrides defined in the class ActualCtrl. That's your polymorphism at its best: you write code using generic pointers, but when the code is executed, it is called with very specific pointers. When you call methods through these pointers, you are executing specific methods provided by the client of your code.

This is what happens to the object factory whose actual class is ControllerFactory<EditorCtrl, EditorData>

Passed to ModalDialog constructor as

void *

Passed by Windows to ModalDialogProcedure as

LPARAM

Cast in ModalDialogProcedure to

CtrlFactory *

And this is what happens to the object data whose actual class is EditorData.

Passed to factory constructor as

void *

Cast in the MakeController method of ControllerFactory<EditorCtrl, EditorData> to

EditorData *

Passed to the constructor of EditCtrl as

EditorData *

The object of class EditCtrl created in the MakeController method of ControllerFactory<EditorCtrl, EditorData> is returned from it as DlgController * and stored in this form as a static data member of ModalDialog.

If you have problems following my explanation, don't despair. The object oriented techniques I just described are difficult, but essential. They are called design patterns. I highly recommend reading the book by Gamma, Helm, Johnson and Vlissides called Design Patterns, Elements of Reusable Object-Oriented Software or look at the Patterns Home Page. it describes a lot of creative ways of using polymorphism, inheritance and templates to make software more reusable.

Now it's time to talk some more about the Canvas.

Canvas, or Windows Device Context

To paint, draw or print in a window you need a device context, DC for short. A DC is a resource that you borrow from Windows and you're supposed to return it immediately after you're done. That's where the Canvas object comes. The Canvas' constructor obtains the DC and the Canvas' destructor releases it. It is important that you create Canvas objects as automatic (stack) variables. That will guarantee that their destructor is always called when the program exits the local scope where they are defined (this class is an example of a more general Resource Management methodology). The typical use of the Canvas object is as follows (as you've already seen in the Generic program):

void Controller::Paint () {

// prepare the canvas and let View do the rest

PaintCanvas canvas (_hwnd);

_view.Paint (canvas, _model);

// Notice: The destructor of PaintCanvas called automatically!

}

All the various types of Canvas share the common ancestor — the Canvas class. Notice that Canvas' constructor is protected. You can't actually instantiate an object of this class. However, the derived classes are free to provide their own constructors. By the way, you can keep adding new methods to Canvas as the need arises.

class Canvas {

public:

// operator cast to HDC

// (used when passing Canvas to Windows API)

operator HDC () { return _hdc; }

void Point (int x, int y, COLORREF color) {

::SetPixel (_hdc, x, y, color);

}

void MoveTo (int x, int y) {

::MoveToEx (_hdc, x, y, 0);

}

void Line ( int x1, int y1, int x2, int y2 ) {

MoveToEx (_hdc, x1, y1, 0);

LineTo (_hdc, x2, y2);

}

void Rectangle (int left, int top, int right, int bottom) {

// draw rectangle using current pen

// and fill it using current brush

::Rectangle (_hdc, left, top, right, bottom);

}

void GetTextSize (int & cxChar, int & cyChar) {

TEXTMETRIC tm;

GetTextMetrics (_hdc, & tm);

cxChar = tm.tmAveCharWidth;

cyChar = tm.tmHeight + tm.tmExternalLeading;

}

void Text (int x, int y, char const * buf, int cBuf) {

::TextOut ( _hdc, x, y, buf, cBuf );

}

void Char (int x, int y, char c) {

TextOut (_hdc, x, y, & c, 1);

}

void SelectObject (void* pObj) {

::SelectObject (_hdc, pObj);

}

protected:

Canvas (HDC hdc): _hdc (hdc) {}

HDC  _hdc;

};

In response to the WM_PAINT message one should construct the PaintCanvas object. Notice the way PaintCanvas obtains and releases the DC.

class PaintCanvas: public Canvas {

public:

// Constructor obtains the DC

PaintCanvas (HWND hwnd) : Canvas (BeginPaint (hwnd, & _paint)), _hwnd (hwnd) {}

// Destructor releases the DC

~PaintCanvas () {

EndPaint (_hwnd, & _paint);

}

protected:

PAINTSTRUCT _paint;

HWND _hwnd;

};

Another important example is the UpdateCanvas which is used for graphical operations outside of the context of WM_PAINT processing. Of course, your program may always force the repainting by calling InvalidateRect, but in many cases it would be an overkill. If your program keeps painting new things as it executes or in response to user actions, you can update the window using UpdateCanvas.

class UpdateCanvas: public Canvas {

public:

UpdateCanvas (HWND hwnd) : Canvas (GetDC (hwnd)), _hwnd(hwnd) {}

~UpdateCanvas () {

ReleaseDC (_hwnd, _hdc);

}

protected:

HWND _hwnd;

};

There are other types of Canvas: DrawItemCanvas used for owner-draw controls, MemCanvas for drawing on a piece of memory, etc.

Use pens and brushes to draw and paint on your canvas.

Drawing with Pens and Painting with Brushes

Like a painter, you will need pens and brushes to create artwork on your canvas. When you call Canvas::Line or Canvas::Rectangle, Windows uses the currently attached pen to draw the lines and the currently attached brush to fill the insides of the shapes.

When you attach an object to the Canvas, you have to remember to detach it after you're done. Unless… you let C++ remember about that. Just use a local object whose constructor attaches, and destructor (called automatically when exiting the scope) detaches the object (see the Resource Management page for more details on this methodology). Notice that the following objects take HDC (handle to Device Context) as an argument in their constructors. However, you should simply pass them a Canvas object instead. Remember that Canvas can be automatically cast to HDC.

class StockObject {

public:

StockObject (HDC hdc, int type) : _hdc(hdc) {

_hObjOld = SelectObject (_hdc, GetStockObject (type));

}

~StockObject () {

SelectObject (_hdc, _hObjOld);

}

private:

HGDIOBJ _hObjOld;

HDC _hdc;

};

Windows comes with a set of pre-defined pens and brushes. It is enough to attach them to your canvas for the time you want to use them.

class WhitePen : public StockObject{

public:

WhitePen (HDC hdc): StockObject (hdc, WHITE_PEN) {}

};

// example

void Controller::Paint (HWND hwnd) {

PaintCanvas canvas (hwnd);

WhitePen pen (canvas);

canvas.Line (0, 10, 100, 10);

// destructor of WhitePen

// destructor of PaintCanvas

}

If your program keeps using a small set of non-stock pens, you might want to create them up-front (e.g. by embedding them in the View object) and use a PenHolder object to temporarily attach them to your Canvas.

class Pen {

public:

Pen (COLORREF color) {

_hPen = CreatePen (PS_SOLID, 0, color);

}

~Pen () {

DeleteObject (_hPen);

}

operator HPEN () { return _hPen; }

private:

HPEN    _hPen;

};

class PenHolder {

public:

PenHolder (HDC hdc, HPEN hPen) : _hdc (hdc) {

_hPenOld = (HPEN)SelectObject (_hdc, hPen);

}

~PenHolder () {

SelectObject (_hdc, _hPenOld);

}

private:

HDC _hdc;

HPEN _hPenOld;

};

class View {

public:

View () : _penGreen (RGB (0, 255, 128)) {}

void Paint (Canvas & canvas) {

PenHolder holder (canvas, _penGreen);

canvas.Line (0, 10, 100, 10);

// destructor of PenHolder

}

private:

Pen _penGreen;

};

Finally, if your program needs colored pens on-demand, i.e., you can't possibly preallocate them for all the colors you'll need, you should use colored pens. When you define an automatic ColorPen object, its constructor both creates and attaches the pen. When the destructor is called, at the end of the scope, it detaches the pen and deletes it.

class ColorPen {

public:

ColorPen (HDC hdc, COLORREF color) : _hdc (hdc) {

_hPen = CreatePen (PS_SOLID, 0, color);

_hPenOld = (HPEN)SelectObject (_hdc, _hPen);

}

~ColorPen () {

SelectObject (_hdc, _hPenOld);

DeleteObject (_hPen);

}

private:

HDC _hdc;

HPEN _hPen;

HPEN _hPenOld;

};

You deal with Brushes in exactly the same way (there are, however, more types of brushes supported by Windows). For instance, here's a definition of a ColorBrush.

class ColorBrush {

public:

ColorBrush (HDC hdc, COLORREF color) : _hdc (hdc) {

_hBrush = CreateSolidBrush (color);

_hBrushOld = (HBRUSH)SelectObject (_hdc, _hBrush);

}

~ColorBrush () {

SelectObject (_hdc, _hBrushOld);

DeleteObject (_hBrush);

}

private:

HDC _hdc;

HBRUSH _hBrush;

HBRUSH _hBrushOld;

};

As always, you are encouraged to experiment on your own.

And now for something completely different: Threads

Using Threads

Multitasking is one of the most difficult aspects of programming. It makes it even more important to provide a simple set of abstractions and to encapsulate it in a nice object-oriented shell. In the OO world, the natural counterpart of the thread (which is a strictly procedural abstraction) is the Active Object. An active object owns a captive thread that performs certain tasks asynchronously. This thread has access to all the internal (private) data and methods of the object. The public interface of the Active Object is available to external agents (e.g., to the main thread, or to the Windows thread carrying messages) so that they, too, can manipulate the state of the object and that of the captive thread, albeit in a very controlled and restricted manner.

An Active Object is built upon a framework called ActiveObject. The implementor of the derived class is supposed to provide the implementation for the pure virtual methods InitThread, Run and Flush (as well as write the destructor).

class ActiveObject {

public:

ActiveObject ();

virtual ~ActiveObject () {}

void Kill ();

protected:

virtual void InitThread () = 0;

virtual void Run () = 0;

virtual void FlushThread () = 0;

static DWORD WINAPI ThreadEntry (void *pArg);

int _isDying;

Thread _thread;

};

The constructor of the ActiveObject initializes the captive thread by passing it a pointer to a function to be executed and a pointer to "this." We have to disable the warning about using "this" before it is fully constructed. We know that it won't be used too early, because the thread is created in the inactive state. The constructor of the derived class is supposed to call _thread.Resume() in order to activate it.

// The constructor of the derived class

// should call

//  _thread.Resume ();

// at the end of construction

ActiveObject::ActiveObject () : _isDying (0),

#pragma warning(disable: 4355) // 'this' used before initialized

_thread (ThreadEntry, this)

#pragma warning(default: 4355)

{

}

The method Kill calls the virtual method FlushThread — it is supposed to release the thread from any wait state and let it run ahead to check the _isDying flag.

void ActiveObject::Kill () {

_isDying++;

FlushThread ();

// Let's make sure it's gone

_thread.WaitForDeath ();

}

We also have a framework for the ThreadEntry function (it's a static method of ActiveObject, so we can specify the calling convention required by the API). This function is executed by the captive thread. The argument it gets from the system is the one we passed to the constructor of the thread object — the "this" pointer of the Active Object. The API expects a void pointer, so we have to do an explicit cast to the ActiveObject pointer. Once we get hold of the Active Object, we call its pure virtual method InitThread, to make all the implementation specific preparations, and then call the main worker method, Run. The implementation of Run is left to the client of the framework.

DWORD WINAPI ActiveObject::ThreadEntry (void* pArg){

ActiveObject * pActive = (ActiveObject *) pArg;

pActive->InitThread ();

pActive->Run ();

return 0;

}

The Thread object is a thin encapsulation of the API. Notice the flag CREATE_SUSPENDED which assures that the thread doesn't start executing before we are done with the construction of the ActiveObject.

class Thread{

public:

Thread ( DWORD (WINAPI * pFun) (void* arg), void* pArg) {

_handle = CreateThread (

0, // Security attributes

0, // Stack size

pFun, pArg, CREATE_SUSPENDED, &_tid);

}

~Thread () { CloseHandle (_handle); }

void Resume () { ResumeThread (_handle); }

void WaitForDeath () {

WaitForSingleObject (_handle, 2000);

}

private:

HANDLE _handle;

DWORD  _tid;     // thread id

};

Synchronization is what really makes multitasking so hard. Let's start with mutual exclusion. Class Mutex is a thin encapsulation of the API. You embed Mutexes in your Active Object and then use them through Locks. A Lock is a clever object that you construct on the stack and for the duration of its lifetime your object is protected from any other threads. Class Lock is one of the applications of the Resource Management methodology. You have to put Locks inside all the methods of your Active Object that access data shared with the captive thread.

class Mutex {

friend class Lock;

public:

Mutex () { InitializeCriticalSection (& _critSection); }

~Mutex () { DeleteCriticalSection (& _critSection); }

private:

void Acquire () {

EnterCriticalSection (& _critSection);

}

void Release () {

LeaveCriticalSection (& _critSection);

}

CRITICAL_SECTION _critSection;

};

class Lock {

public:

// Acquire the state of the semaphore

Lock ( Mutex & mutex ) : _mutex(mutex) {

_mutex.Acquire();

}

// Release the state of the semaphore

~Lock () {

_mutex.Release();

}

private:

Mutex & _mutex;

};

An Event is a signalling device that threads use to communicate with each other. You embed an Event in your active object. Then you make the captive thread wait on it until some other thread releases it. Remember however that if your captive thread waits on a event it can't be terminated. That's why you should call Release from the Flush method.

class Event {

public:

Event () {

// start in non-signaled state (red light)

// auto reset after every Wait

_handle = CreateEvent (0, FALSE, FALSE, 0);

}

~Event () {

CloseHandle (_handle);

}

// put into signaled state

void Release () { SetEvent (_handle); }

void Wait () {

// Wait until event is in signaled (green) state

WaitForSingleObject (_handle, INFINITE);

}

operator HANDLE () { return _handle; }

private:

HANDLE _handle;

};

To see how these classes can be put to use, I suggest a little side trip. You can jump to the page that explains how the Frequency Analyzer uses the activeobject class to update the display asynchronously. Or, you can study a somehow simpler example of a Folder Watcher that waits quietly watching a folder and wakes up only when a change happens.

I wish I could say programming with threads was simple. It is, however, simpler if you use the right kind of primitives. The primitives I advertised here are ActiveObject, Thread, Mutex, Lock and Event. Some of them are actually available in MFC. For instance, they have a Lock object (or is it CLock?) whose destructor releases the critical section (theirs is slightly less convenient because of the "two-step" construction — you have to create it and then take it in two separate steps — as if you'd want to create it and then not take it). Other than that, MFC only offers some thin veneer over the API, nothing exciting.

You might also recognize some of the mechanisms I presented here in the Java programming language. Of course, when you have the liberty of designing multitasking into the language, you can afford to be elegant. Their version of ActiveObject is called Runnable and it has the run method. Every Java object potentially has a built-in mutex and all you have to do to take a lock is to declare a method (or a scope) synchronized. Similarly, the events are implemented with the wait and notify calls inside any synchronized method. So, if you know how to program in Java, you know how to program in C++ (as if there were people fluent in Java, but ignorant of C++).

When Folders Change

Have you ever wondered how the Explorer knows that it should update its display because a file has been added or removed from a folder by some external application? Wonder no more because, with the use of our Active Object, we can do the same and more. There are a few simple API calls that you can make to ask the file system to selectively notify you about changes to files and folders. Once you set up such a watch, your thread can go to sleep waiting on an event. The file system will set the event as soon as it detects the kind of change that you're watching for.

Without further ado, let's derive a FolderWatcher from ActiveObject. We'll give it a notification source — an event, and a notification sink — a handle to a window that will respond to the notification. The source event is set up in the constructor of FolderWatcher. It is also important to start the captive thread at the end of the constructor.

class FolderWatcher : public ActiveObject {

public:

FolderWatcher (char const * folder, HWND hwnd) : _notifySource (folder), _hwndNotifySink (hwnd) {

strcpy (_folder, folder);

_thread.Resume ();

}

~FolderWatcher () {

Kill ();

}

private:

void InitThread () {}

void Loop ();

void FlushThread () {}

FolderChangeEvent _notifySource;

HWND _hwndNotifySink;

char _folder [MAX_PATH];

};

The action in the ActiveObject happens inside the Loop method. Here we set up an "infinite" loop in which we let the thread wait for the event. When the event happens, we check for the _isDying flag (as usual) and post a special message WM_FOLDER_CHANGE to the window that deals with notifications. This is not one of the predefined Windows messages, it's a message defined by us for the special purpose of passing folder notification information from one thread to another.

Two thing happen now: the captive thread makes another API call to let the file system know that it wants more notifications. It then goes back to watchful sleep. Concurrently, Windows retrieves our WM_FOLDER_CHANGE message from the message queue and sends it to the Windows Procedure of the sink window. More about that in a moment.

UINT const WM_FOLDER_CHANGE = WM_USER;

void FolderWatcher::Loop () {

for (;;) {

// Wait for change notification

DWORD waitStatus = WaitForSingleObject (_notifySource, INFINITE);

if (WAIT_OBJECT_0 == waitStatus) {

// If folder changed

if (_isDying) return;

PostMessage (_hwndNotifySink, WM_FOLDER_CHANGE, 0, (LPARAM) _folder);

// Continue change notification

if (!_notifySource.ContinueNotification ()) {

// Error: Continuation failed

return;

}

} else {

// Error: Wait failed

return;

}

}

}

So here what happens in the Windows Procedure in response to our special message: we call the Controller's method OnFolderChange. This method can do anything we want. In the Explorer it refreshes the display of the folder we are watching. In our example it just pops up a simple message box. Notice how we are passing the name of the changed folder as LPARAM. It is totally up to us to define what goes into WPARAM and LPARAM of a user-defined message.

By the way, the Folder Watcher is just a part of the Controller.

case WM_FOLDER_CHANGE:

pCtrl->OnFolderChange (hwnd, (char const *) lParam);

return 0;

void Controller::OnFolderChange (HWND hwnd, char const * folder) {

MessageBox (hwnd, "Change Detected", "Folder Watcher", MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_OK);

}

class Controller {

public:

Controller(HWND hwnd, CREATESTRUCT * pCreate);

~Controller ();

void OnFolderChange (HWND hwnd, char const *folder);

private:

FolderWatcher _folderWatcher;

};

Now that we know how to deal with notification, let's have a look at their sources, the File Change Events. An event object is created by the file system in response to FindFirstChangeNotification. A handle to that event is returned from the call. We remember this handle and use it later to either renew or abandon our interest in further notifications. Notice that we can set the watch to be recursive, i.e., watching a given folder and all its subfolders and sub-subfolders. We can also express interest in particular types of changes by passing a bitwise OR of any combination of the following flags:

• FILE_NOTIFY_CHANGE_FILE_NAME (renaming, creating, or deleting a file)

• FILE_NOTIFY_CHANGE_DIR_NAME (creating or deleting a directory)

• FILE_NOTIFY_CHANGE_ATTRIBUTES

• FILE_NOTIFY_CHANGE_SIZE

• FILE_NOTIFY_CHANGE_LAST_WRITE (saving a file)

• FILE_NOTIFY_CHANGE_SECURITY

For convenience we have defined a few subclasses of FileChangeEvent that correspond to some useful combinations of these flags. One of them is the FolderChangeEvent that we used in our FolderWatcher.

class FileChangeEvent {

public:

FileChangeEvent (char const * folder, BOOL recursive, DWORD notifyFlags) {

_handle = FindFirstChangeNotification (folder, recursive, notifyFlags);

if (INVALID_HANDLE_VALUE == _handle) throw WinException ("Cannot create change notification handle");

}

~FileChangeEvent () {

if (INVALID_HANDLE_VALUE != _handle) FindCloseChangeNotification (_handle);

}

operator HANDLE () const { return _handle; }

BOOL ContinueNotification () {

return FindNextChangeNotification (_handle);

}

private:

HANDLE _handle;

};

class FolderChangeEvent : public FileChangeEvent {

public:

FolderChangeEvent (char const * folder) : FileChangeEvent (folder, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME) {}

};

class TreeChangeEvent : public FileChangeEvent {

public:

TreeChangeEvent (char const * root) : FileChangeEvent (root, TRUE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME) {}

};

It should be easy now to generalize this example to do some really useful work in your programs. Remember to look up the API's that we use in these tutorials in the online help that comes with your compiler.

How about some OLE or COM programming without MFC? Go to the next page.

Using Windows95 Shell and COM — A. K. A. OLE

COM programming is so difficult that you shouldn't even try it without MFC. Right or wrong? Absolutely wrong!. Granted, OLE and its successor COM have the elegance of a figure-skating hippopotamus. But putting MFC on top of COM is like dressing the hippo in an oversized clown suit.

So what is a programmer to do when faced with the necessity to use some of the Windows 95 (and Windows NT 4.0) shell features that are only accessible through COM interfaces? Read on…

To begin with, whenever you are planning to use COM, you have to tell the system to initialize the COM subsystem. Similarly, whenever you're done using COM, you should tell the system to uninitialize it. The simplest thing to do, is to define an object whose constructor initializes COM and whose destructor deinitializes it. The best place to place it is to embed it in the Controller object (see the Generic Windows program), like this.

class Controller {

public:

Controller (HWND hwnd, CREATESTRUCT * pCreate);

~Controller ();

// ...

private:

UseCom    _comUser; // I'm a COM user

Model       _model;

View        _view;

HINSTANCE   _hInst;

};

This way we are guaranteed that the COM subsystem will be initialized before any calls are made to it and that it will be deinitialized after the program has done its tear-down (i.e., after the View and the Model have been destroyed).

The UseCom class is very simple.

class UseCom {

public:

UseCom () {

HRESULT err = CoInitialize (0);

if (err != S_OK) throw "Couldn't initialize COM";

}

~UseCom () {

CoUninitialize ();

}

};

So far it hasn't been too difficult, has it? That's because we haven't touched the bane of COM programming — reference counting. You see, every time you obtain an interface, its reference count is incremented, every time you're done with it, you are supposed to explicitly decrement it. Oh, and it gets even weirder when you start querying, cloning, passing interfaces around, etc. But wait a moment, we know how to manage such problems! It's called Resource Management. We should never touch COM interfaces without encapsulating them in smart interface pointers. Here's how it works.

template <class T>class SIfacePtr {

public:

~SIfacePtr () {

Free ();

}

T * operator->() { return _p; }

T const * operator->() const { return _p; }

operator T const * () const { return _p; }

T const & GetAccess () const { return *_p; }

protected:

SIfacePtr () : _p (0) {}

void Free () {

if (_p != 0) _p->Release ();

_p = 0;

}

T * _p;

private:

SIfacePtr (SIfacePtr const & p) {}

void operator = (SIfacePtr const & p) {}

};

Don't worry that this class looks unusable (because it has a protected constructor). We'll never use it directly — we'll inherit from it. By the way, this is a great trick: create a class with a protected do-nothing constructor and keep deriving from it. All the derived classes will have to provide their own specialized public constructors. As you might have guessed, various classes derived from SIfacePtr will differ in the way they obtain, in their constructors, the interface in question.

Private dummy copy constructor and operator= are not strictly necessary, but they will save you hours of debugging if, by mistake, you'll pass a smart interface by value rather than by reference. That's a big no-no. You'll end up releasing the same interface twice and that'll screw the COM's reference counting. Believe me, I've been there. As it is, the compiler will refuse to pass by value an object that has a private copy constructor (dummy or not). It will also issue an error when you try to assign an object that has a private assignment operator.

To wrap this short introduction up, let me present one more variation on the theme of smart pointers. Shell API's often allocate memory using their own special allocator. That wouldn't be so bad, if it weren't for the fact that they expect you to release this memory using the same allocator. So, whenever the shell hands us such an embarassing package, we'll wrap it up in a special smart pointer.

template <class T>

class SShellPtr {

public:

~SShellPtr () {

Free ();

_malloc->Release ();

}

T * weak operator->() { return _p; }

T const * operator->() const { return _p; }

operator T const * () const { return _p; }

T const & GetAccess () const { return *_p; }

protected:

SShellPtr () : _p (0) {

// Obtain malloc here, rather than

// in the destructor.

// Destructor must be fail-proof.

// Revisit: Would static IMalloc * _shellMalloc work?

if (SHGetMalloc (& _malloc) == E_FAIL) throw Exception "Couldn't obtain Shell Malloc";

}

void Free () {

if (_p != 0) _malloc->Free (_p);

_p = 0;

}

T * _p;

IMalloc *  _malloc;

private:

SShellPtr (SShellPtr const & p) {}

void operator = (SShellPtr const & p) {}

};

Notice the same trick as before: the class SShellPtr is not directly usable. You have to subclass it and provide the appropriate constructor.

Notice also that I wasn't sure if _shellMalloc couldn't be made a static member of SShellPtr. The problem is that static members are initialized before WinMain. At that time, the whole COM system might be unstable. On the other hand, the documentation says that you can safely call another API, CoGetMalloc, before the call to CoInitialize. It doesn't say whether SHGetMalloc, which does almost the same thing, can also be called anytime in your program. Like in many other cases with badly designed/documented systems, only experiment can answer such questions. Feedback will be very welcome.

By the way, if you need a smart pointer that uses COM-specific malloc, the one obtained by calling CoGetMalloc, you can safely make its _malloc a static member and initialize it once in your program (here, SComMalloc::GetMalloc is static, too):

IMalloc * SComMalloc::_malloc = SComMalloc::GetMalloc ();

IMalloc * SComMalloc::GetMalloc () {

IMalloc * malloc = 0;

if (CoGetMalloc (1, & malloc) == S_OK) return malloc;

else return 0;

}

That's all there is to know in order to start using Windows95 shell and its COM interfaces. Here's an example. Windows95 shell has this notion of a Desktop being the root of the "file" system. Did you notice how Windows95 applications let the user browse the file system starting at the desktop? This way you can, for instance, create files directly on your desktop, move between drives, browse a network drive, etc. It is, in effect, a poor man's Distributed File System (PMDFS). How can your application get access to PMDFS? Easy. For instance, let's write some code that will let the user pick a folder by browsing PMDFS. All we need to do is to get hold of the desktop, position ourselves with respect to it, start the built-in browser and retrieve the path that the user has chosen.

char path [MAX_PATH];

path [0] = '\0';

Desktop desktop;

ShPath browseRoot (desktop, unicodePath);

if (browseRoot.IsOK ()) {

FolderBrowser browser (hwnd, browseRoot, BIF_RETURNONLYFSDIRS, "Select folder of your choice");

if (browser.IsOK ()) {

strcpy (path, browser.GetPath ());

}

}

Let's start with the desktop object. It envelops the interface called IShellFolder. Notice how we conform to the First Rule of Acquisition — we allocate resources in the constructor by calling the API SHGetDesktopFolder. The smart interface pointer will take care of resource management (reference counting).

class Desktop: public SIfacePtr<IShellFolder> {

public:

Desktop () {

if (SHGetDesktopFolder (& _p) != NOERROR) throw "HGetDesktopFolder failed";

}

};

Once we have the desktop, we have to create a special kind of path that is used by PMDFS. The class ShPath encapsulates this "path." It is constructed from a regular Unicode path (use mbstowcs to convert ASCII path to Unicode: int mbstowcs(wchar_t *wchar, const char *mbchar, size_t count)). The result of the conversion is a generalized path relative to the desktop. Notice that memory for the new path is allocated by the shell--we encapsulate it in SShellPtr to make sure it is correctly deallocated.

class ShPath: public SShellPtr<ITEMIDLIST> {

public:

ShPath (SIfacePtr<IShellFolder> & folder, wchar_t * path) {

ULONG lenParsed = 0;

_hresult = folder->ParseDisplayName (0, 0, path, & lenParsed, & _p, 0);

}

bool IsOK () const { return SUCCEEDED (_hresult); }

private:

HRESULT _hresult;

};

This shell path will become the root from which the browser will start its interaction with the user.

From the client code's point of view, the browser is the path chosen by the user. That's why it inherits from SShellPtr<ITEMIDLIST>.

By the way, ITEMIDLIST is the official name for this generalized path. It is a list of, and I'm not making this up, SHITEMID's. I shall refrain from making any further comments or jokes about shell naming conventions.

class FolderBrowser: public SShellPtr<ITEMIDLIST> {

public:

FolderBrowser(HWND hwndOwner, SShellPtr<ITEMIDLIST> & root, UINT browseForWhat, char const *title);

char const * GetDisplayName () { return _displayName; }

char const * GetPath ()        { return _fullPath; }

bool IsOK() const              { return _p != 0; };

private:

char _displayName [MAX_PATH];

char _fullPath [MAX_PATH];

BROWSEINFO _browseInfo;

};

FolderBrowser::FolderBrowser (HWND hwndOwner, SShellPtr<ITEMIDLIST> & root, UINT browseForWhat, char const *title) {

_displayName [0] = '\0';

_fullPath [0] = '\0';

_browseInfo.hwndOwner = hwndOwner;

_browseInfo.pidlRoot = root;

_browseInfo.pszDisplayName = _displayName;

_browseInfo.lpszTitle = title;

_browseInfo.ulFlags = browseForWhat;

_browseInfo.lpfn = 0;

_browseInfo.lParam = 0;

_browseInfo.iImage = 0;

// Let the user do the browsing

_p = SHBrowseForFolder (& _browseInfo);

if (_p != 0) SHGetPathFromIDList (_p, _fullPath);

}

That's it! Isn't it simple?

Now, would you like to hear what I think about OLE?

What's Wrong with OLE?

The Insider's story

You might have heard or read critical opinions about OLE. Programmers mostly complain about the complex system of reference counting and the lack of support for inheritance. Microsoft evangelists counter this by saying that there is no other way, and that it's for your own good[1]. Interfaces, it is said, have to be refcounted, and there is a clever hack called aggregation (fondly called aggravation by OLE programmers) that provides the same functionality as inheritance. Maybe they are right, maybe the problem of interacting with run-time-loadable objects is so complex that there simply isn't any better way? On the other hand, maybe OLE has a fatal flaw that just keeps popping up all over the place.

The fatal design flaw of OLE is the requirement that one should be able to get from any interface to any other interface.

Technically this interface jumping it is done by having every interface inherit from the mother of all interfaces, IUnknown. IUnknown has the fatal method QueryInterface that is supposed to return any interface supported by the current object. This single assumption precludes any possibility of having simple implementation of inheritance. Let me explain why.

Suppose that you have an object FooObj with an interface IFoo. This situation is easily modeled in C++ by having an abstract class (all methods pure virtual) IFoo and a concrete class FooObj that inherits from IFoo and implements all its methods.

Now you would like to extend this object by adding support for another interface IBar. In C++ it's trivial, you just define a class FooBarObj that inherits from FooObj and IBar. This new class supports the IFoo interface together with its implementation through inheritance from FooObj. It also supports the interface IBar and provides the implementation of IBar methods.

Anybody who knows C++ can do it with their eyes closed. So why can't you do the same in OLE? Here comes the Flaw. You have to be able to obtain the IBar interface from the IFoo interface using its QueryInterface. But, wait a minute, the object FooObj that provides the implementation of all methods of IFoo, including QueryInterface, had no clue about IBar! It could have been created long before anyone even thought about the possibility of IBar. So how can it provide access to IBar?

Good question. I'm not going to go into the gory details of the aggregation hack that is supposed to solve this problem. Given the constraints of the flawed initial design, it is a truly ingenious hack. So is there a better design? Read on…

Have you ever noticed how one is forced to distinguish between the object that implements interfaces and the interfaces themselves? These are two completely different notions. You can't explain anything in OLE without talking about objects, sometimes called components. Interfaces are important, but objects are even more important. When can you get one interface from another? When they share the same underlying object. You can change the state of an object using one interface and then examine this state through another interface. It's obviously the same object! In my example I described two interfaces, IFoo and IBar, and two objects (or classes of objects), FooObject and FooBarObject.

In fact, anybody who's the implementor of interfaces (whether in C++, C, or Basic) has to deal with objects. Nevertheless, this very important abstraction is completely absent from the client's view of OLE. All that the client sees are interfaces. The underlying object is like a ghost.

But it's not a ghost, it is physically present in the address space of your program, either directly, or as a forwarding stub. So why hide it? Indeed, wouldn't OLE be simpler with the explicit notion of an object? Let's see how it would work.

The client of this "smart OLE" would call CoCreateInstance or ClassFactory::CreateInstance to obtain a pointer to an object (not an interface!). Using this pointer, the client would call QueryInterface to obtain an interface. If the client wanted to obtain another interface, he or she would make another QueryInterface call through the object, not through the interface. You could not obtain an interface from another interface. Only the object would have the ability to dispense interfaces. Bye, bye IUnknown!

Let me show you some hypothetical code in this new "smart OLE."

CoObject * obj CoCreateInstance (CLSID_FooBarObject);

IFoo * foo = obj->QueryInterface (IID_FOO);

foo->FooMethod ();

IBar * bar = obj->QueryInterface (IID_BAR);

bar->BarMethod ();

delete obj;

I purposely omitted all the error checking and reference counting. In fact, I wouldn't write code like this in a serious application, I'd use smart pointers and exceptions. But notice one thing, in "smart OLE" inheritance is as simple as in C++. Since there is no way to jump from interface to interface and there is no IUnknown; extending FooObject by adding IBar requires no more work than having FooBarObject inherit from FooObject and IBar, implementing IBar methods and overriding the QueryInterface method of CoObject. I assume that all "smart OLE" objects inherit from the abstract class CoObject and override its QueryInterface method (it's very much different from having every interface inherit from IUnknown!).

What about reference counting? The truth is, there is very little need for refcounting as long as you agree not to destroy the object while you are using its interfaces. That's not such a big deal — we do it all the time when we are using methods in C++. We don't think it's an especially harsh requirement, not to destroy the object while we are using its methods. If we were to follow OLE's current model to its full extent, we should require the client to get a refcount of any method he or she is planning to use, and then release it after the call? It would be absurd, wouldn't it?

So why does OLE so meticulously count references? Simple — it's because it is hiding the object from the client. The OLE object is created implicitly when you get its first interface, and destroyed implicitly when you release its last interface. You see, OLE is doing you a big favor by hiding this bookkeeping from you. Or is it? Funny you'd ask.

Long, long time ago, when computer languages were still in their infancy, the wizards of C were trying to implement a stack. They made the discovery that all the client needed in order to operate a stack were two functions, push and pop. They also realized that they would have to allocate some memory to hold stack data and, since they were neat programmers, they would have to release it when the client was done. But how would they know when the client was done? Well, obviously, the client was done when he or she didn't need to call push or pop any more. Once the wizards of C realized that, the rest was simple. The stack was created and memory allocated when the client requested the first pointer to push. He could then call push with a special argument to obtain the corresponding pop. In fact, using the same scheme he could create as many pushes and pops as he wished. Then, when he was done with a given push or pop, he'd simply release it. Once all the pushes and pops were released, the stack would be freed. This ingeniuos scheme simplified programming tremendously, because the clients didn't have to deal with the stack itself. The system took care of all the bookkeeping. Programmers were ecstatic and they gave all their money to the wizards of C. And, by the way, the new functions were called i_push and i_pop.

Here's the best part of the story. You might think, "Oh, right, big deal! It's easy to come up with these ideas now, after OLE has been on the market for almost a decade." What if I told you that yours truly, who worked for Microsoft back then, soon after OLE 1.0 was released, had these ideas written down and sent to the responsible people. To make the long story short, the ideas were accepted as valid, but rejected on the premise that there already had been too much code written to the OLE specification (mostly at Microsoft). No manager was willing to take the risk of redesigning OLE.

So here we are now, reference counting, aggregating and all. The moral of the story is,

There is nothing sacred about OLE. It can be done better!

But can we have the cake and eat it too? In other words, is it possible to build "smart OLE" on top of "the other OLE"? You bet! Go straight to the next tutorial.

Rationalizing OLE

Building smart OLE on top of, you know, the other ole.

First of all, you have to tell the world that you're going to use OLE. Here's a little class that will do it for you. Just embed an object of this class in some high-level object that's constructed before you do anything with OLE and destroyed after you're done with OLE. In a Windows program, the Controller is a perfect host for UseOle.

class UseOle {

public:

UseOle () { OleInitialize (0); }

~UseOle () { OleUninitialize (); }

};

class Controller {

public:

Controller(HWND hwnd, CREATESTRUCT * pCreate);

~Controller() { PostQuitMessage(0); }

void Paint (HWND hwnd);

private:

UseOle useOle;

};

Next, you need to create an OLE object that is the provider of interfaces. The abstract base class CoObject declares the method AcquireInterface. The actual class SObject (smart object) provides one particular implementation of CoObject that uses, internally, OLE's infamous IUnknown.

class CoObject {

public:

virtual void * AcquireInterface (IID const & iid) = 0;

};

class SObject: public CoObject {

public:

SObject (CLSID const & classId, bool running = false);

~SObject () {

if (_iUnk) _iUnk->Release ();

}

void * AcquireInterface (IID const & iid);

private:

IUnknown * _iUnk;

};

Let's have a look at the implementation of the constructor and the AcquireInterface method.

The constructor gets hold of the object by calling the API GetActiveObject and/or CoCreateInstance. The difference between the two is that GetActiveObject will try to get hold of an already running object, whereas CoCreateInstance will try the same, but if it fails, it'll start whatever exe is necessary for this object to run. Some objects actually specify a preference: they want a new server to be started every time CoCreateInstance is called. GetActiveObject lets you bypass that.

Notice that this is just one example how you get hold of an OLE object. You may want to play with some of the parameters — for instance, I am passing CLSCTX_SERVER as an argument to CoCreateInstance. That will make sure that the object will live in a separate process from the client. In many cases you'd rather have the object as an inproc server — a DLL that is loaded into the client's address space. For more details, look up CoCreateInstance in your friendly help.

SObject::SObject (CLSID const & classId, bool running) :_iUnk (0) {

HRESULT hr = S_OK;

if (running) {

::GetActiveObject (classId, 0, & _iUnk);

}

if (_iUnk == 0) {

hr = ::CoCreateInstance (classId, 0, CLSCTX_SERVER, IID_IUnknown, (void**)& _iUnk);

}

if (FAILED (hr)) throw HEx (hr, "Couldn't create instance");

}

I will explain the strange exception type, HEx, in a moment.

Here's our implementation of AcquireInterface that simply calls the QueryInterface method of IUnknown (or, should I say, the unfortunate QueryInterface of the unfortunate IUnknown).

void * SObject::AcquireInterface (IID const & iid) {

void * p = 0;

HRESULT hr = _iUnk->QueryInterface (iid, & p);

if (FAILED (hr)) {

if (hr == E_NOINTERFACE) throw "No such interface";

else throw HEx (hr, "Couldn't acquire interface");

}

return p;

}

The method AcquireInterface is one of these exceptional Acquire methods of Resource Management that release raw resources. We wan't call it other than inside of the constructor of a smart interface pointer. (By the way, the template parameter is an IID pointer because the compiler won't accept references as template arguments. I'm not sure why.)

So here it is, the template for a smart interface pointer.

template<class I>

class SFace {

public:

~SFace () {

if (_i) _i->Release ();

}

I * operator-> () { return _i; }

protected:

SFace () : _i (0) {}

SFace (void * i) {

_i = static_cast<I*> (i);

}

protected:

I * _i;

};

As you can see, this particular template can't be instantiated. That's because all its constructors are protected. But don't worry, we'll create other classes that will provide their own specialized constructors.

Here's one that uses our CoObject (or any other object derived from it) as an interface source.

template<class I, IID const * iid>class SObjFace: public SFace<I> {

public:

SObjFace (CoObject & obj) : SFace<I> (obj.AcquireInterface (*iid)) {}

};

Finally, let me introduce the HEx class (HRESULT Exception). It is an exception class that is capable of displaying meaningful error messages. For my limited purposes, I chose to simply display the messages directly on the canvas of the main screen. Feel free to implement your own Display method to pop up a message box or something.

class HEx {

public:

HEx (HRESULT hr, char const * str = 0) : _hr (hr), _str (str) {}

void Display (int x, int y, Canvas & canvas) {

if (_str != 0) {

canvas.Text (x, y, _str);

y += 20;

}

if (FAILED (_hr)) {

char * msg;

::FormatMessage (

FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,

0, _hr,

MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),

reinterpret_cast<char *> (& msg),

0, 0);

canvas.Text (x, y, msg);

::LocalFree (msg);

}

}

private:

HRESULT _hr;

char const * _str;

};

There will be sample code for downloading. I just need to explain what Automation is.

OLE Automation

I just wanted to be able to syphoon information from a running copy of Microsoft Developers Studio. It should have been a simple task--DevStudio, like many other MS apps, exposes its interfaces through OLE Automation. Not so simple! You see, Microsoft assumed that clients of VC++ automation interfaces will either be using Visual Basic, or DevStudio's own clever wizards. I, on the other hand, like programming in C++. (Don't you think that Microsoft's Visual C++ should be renamed Microsoft Visual MFC wizard? It relegated C++ to the role of a script language for MFC.)

Anyway, once I've found out how things should be done, it turned out not to be too difficult. You just have to figure out where the heck all the information is kept in the registry. In particular, the IIDs of all the interfaces. Tip: Use OLE-COM Object Viewer that comes with VC++ to look at type libraries. It would be common courtesy if Microsoft provided a source file or an obj file with the definitions of interface ids. As it is, I had to copy and paste them from the Object Viewer. Here's an example.

static const IID IID_IApplication = { 0xEC1D73A1, 0x8CC4, 0x11CF, { 0x9B, 0xE9, 0x00, 0xA0, 0xC9, 0x0A, 0x63, 0x2C } };

So how does one get hold of a running copy of DevStudio? First, you have to create an OLE object. For that, you'll need a class id of this object. You can get the class id from the system (it's stored in the registry) if you know the program id. Program id is supposed to be a human readable name. Of course, every human being knows that the Developer Studio goes by the name "MSDEV.APPLICATION". So that's easy.

With the class id in hand we can create our SObject. We're passing true as the value of the parameter running, because we would like to connect to the running copy of MSDEV.APPLICATION, if possible. Getting an interface from SObject is as simple as instantiating the SObjFace template with the appropriate arguments. That will be our starting point, the interface to the application.

CLSID idMsDev;

HRESULT hr = ::CLSIDFromProgID (L"MSDEV.APPLICATION", &idMsDev);

if (FAILED (hr)) throw HEx (hr, "Couldn't convert prog id to class id");

SObject obj (idMsDev, true);

SObjFace<IApplication, &IID_IApplication> app (obj);

Notice that the string you pass to CLSIDFromProgID must be Unicode (wide character). Putting L in front of the string literal takes care of that.

I hope you can appreciate the simplicity of this code. It's almost as simple as its VB equivalent.

Dim app as Application

Set app = GetObject (, "MSDEV.APPLICATION")

if (app = NULL) Set app = CreateObject ("MSDEV.APPLICATION")

Now let's do something with this interface. It so happens that IApplication has a member Visible that you can put or get. When you set Visible to true, the application window becomes visible. Here's the syntax for "putting" a member. Notice that in OLE you have to use things called VARIANT_BOOL and VARIANT_TRUE instead of bool and true. That's because of compatibility with Basic (makes Bill happy).

VARIANT_BOOL b = VARIANT_TRUE;

app->put_Visible (b);

How did I know that IApplication has a member Visible? Good question! There is a subdirectory objmodel in VC++ include directory where you can find such files as Appauto.h that contain lines like the ones below. You can sort of learn to interpret these files. Their crypticity is caused by the (silly!) requirement that they be includable in both C and C++ code. Microsoft didn't want to maintain two sets of header files, so here you go.

STDMETHOD(get_Visible)(THIS_ VARIANT_BOOL FAR* Visible) PURE;

STDMETHOD(put_Visible)(THIS_ VARIANT_BOOL Visible) PURE;

So what do we do, now that we have the application in our hands? How about finding out what document is currently active. Using the IApplication interface as our source, we can create another OLE object that will represent the active document. This particular OLE object, SActiveDocument, can be used as a source of a few interfaces, one of them being IGenericDocument. We grab this interface in a standard way  — by creating an object from the SObjFace template. SActiveDocument, like all our OLE/COM objects, inherits from CoObject, the source of interfaces.

IGenericDocument has a member FullName that can be gotten by calling the method get_FullName. Unfortunately, Basic compatibility strikes again — the result is passed in the form of a BSTR, or Basic string. I have created two helper classes BString and CString to take care of this weirdness. In particular BString makes sure that the string is deallocated using the API SysFreeString.

SActiveDocument docObj (app);

if (docObj) {

SObjFace<IGenericDocument, &IID_IGenericDocument> doc (docObj);

BString bPath;

doc->get_FullName (bPath.GetPointer ());

CString path (bPath);

canvas.Text (20, y, "Active Document:");

canvas.Text (200, y, path);

}

This a typical situation in OLE Automation. Using a method of one interface, app, in this case, you get hold of another object, docObj, with its own interfaces. For each such object-returning method, we'll construct a new smart pointer class, whose only distinguishing feature is its constructor. For instance, here's the class SActiveDocument.

class SActiveDocument: public DispObject {

public:

SActiveDocument (SObjFace<IApplication, &IID_IApplication> & app) {

HRESULT hr = app->get_ActiveDocument (&_iDisp);

if (FAILED (hr)) throw HEx (hr, "get_ActiveDocument failed");

}

};

Notice that the base class of SActiveDocument is not SObject — it's a new class DispObject. It is almost exactly like SObject with one distinction — instead of internally using a pointer to IUnknown it uses a pointer to IDispatch. Does it matter? Not really, I could have used SObject and everything would've work the same. Except that IDispatch can be used for something more than just querying other interfaces. It can be used for dynamic dispatching of calls. Since our program is written in C++ and it knows all the interfaces up front, we don't really need to use dynamic dispatching. But there are situations in which you need to let the user decide what object to load and which method to call, real time. Script interpreters have to be able to do it. In particular, Visual Basic scripting tools require such functionality.

Below is a skeleton implementation of a DispObject that demonstrates this point. It also explains why we were talking about "members" like Visible or FullName when discussing interfaces. In VB they actually appear as data members, or properties, of objects. Here, I have implemented a dispatch method GetProperty that can be used to load the value of any property based on its DISPID. And you can get a DISPID of any property or method as long as you know its name. The method GetDispId will do that for you. In a similar way, you should be able to implement PutProperty and, if you need it, Invoke, that can be used to invoke any member function by its DISPID. I leave this as an exercise for the reader.

class DispObject: public CoObject {

public:

DispObject (CLSID const & classId) :_iDisp (0) {

HRESULT hr = ::CoCreateInstance ( classId, 0, CLSCTX_ALL, IID_IDispatch, (void**)&_iDisp);

if (FAILED (hr)) {

if (hr == E_NOINTERFACE) throw "No IDispatch interface";

else throw HEx (hr, "Couldn't create DispObject");

}

}

~DispObject () {

if (_iDisp) _iDisp->Release ();

}

operator bool () const {

return _iDisp != 0;

}

bool operator ! () const {

return _iDisp == 0;

}

DISPID GetDispId (WCHAR * funName) {

DISPID dispid;

HRESULT hr = _iDisp->GetIDsOfNames ( IID_NULL, &funName, 1, GetUserDefaultLCID (), &dispid);

return dispid;

}

void GetProperty (DISPID propId, VARIANT & result) {

// In parameters DISPPARAMS args = { 0, 0, 0, 0 };

EXCEPINFO except;

UINT argErr;

HRESULT hr = _iDisp->Invoke (propId, IID_NULL, GetUserDefaultLCID (), DISPATCH_PROPERTYGET, &args, &result, &except, &argErr);

if (FAILED (hr)) throw HEx (hr, "Couldn't get property");

}

void * AcquireInterface (IID const & iid) {

void * p = 0;

HRESULT hr = _iDisp->QueryInterface (iid, &p);

if (FAILED (hr)) {

if (hr == E_NOINTERFACE) throw "No such interface";

else throw HEx (hr, "Couldn't query interface");

}

return p;

}

protected:

DispObject (IDispatch * iDisp) : _iDisp (iDisp) {}

DispObject () : _iDisp (0) {}

protected:

IDispatch * _iDisp;

};

Below is a small illustration of dynamic dispatching. Of course, the same result could have been obtained directly by calling the method get_Name of the IGenericDocument interface. We'll use this direct (vtable) method in a moment to obtain the full path of the document.

// Use docObj as a dispatch interface

DISPID pid = docObj.GetDispId (L"Name");

VARIANT varResult ;

::VariantInit(& varResult) ;

docObj.GetProperty (pid, varResult);

BString bName (varResult);

CString cName (bName);

canvas.Text (20, y, "Name:");

canvas.Text (200, y, cName);

And this is how you obtain the path, the vtable way.

SObjFace<IGenericDocument, &IID_IGenericDocument> doc (docObj);

BString bPath;

doc->get_FullName (bPath.GetPointer ());

Now you shouldn't have any problems understanding the code that retrieves the line number of the line where the user positioned the cursor.

BString bType;

doc->get_Type (bType.GetPointer ());

if (type.IsEqual ("Text")) {

SObjFace<ITextDocument, &IID_ITextDocument> text (docObj);

SSelection selObj (text);

SObjFace<ITextSelection, &IID_ITextSelection> sel (selObj);

long line;

sel->get_CurrentLine (&line);

canvas.Text (20, y, "CurrentLine:");

char buf [10];

wsprintf (buf, "%ld", line);

canvas.Text (200, y, buf);

}

SSelection is a DispObject that can be obtained by calling a method get_Selection of the text document interface.

class SSelection: public DispObject {

public:

SSelection (SObjFace<ITextDocument, &IID_ITextDocument> & doc) {

HRESULT hr = doc->get_Selection (& _iDisp);

if (FAILED (hr)) throw HEx (hr, "get_Selection failed");

}

};

You might be a little confused if this is your first contact with OLE (an understatement!). So here is a summary of what the various actions are that have to be done in order to accomplish a simple Automation task. Notice, this is the client side of the equation. If you want your application to be an Automation server, things are a bit more complicated. Fortunately, there is more literature about it.

So here's what you have to do.

The research. Search your registry (using RegEdt32 or OLE/COM object viewer) to find the ProgID of the application you want to get hold of. HKEY_CLASSES_ROOT is the starting point. You'll see there such keys as Word.Application, Excel.Application and many others. Find the Type Libraries using OLE/COM object viewer. They will have the class ids and interface ids that you have to copy and paste into your code. Find the header files for these interfaces. In your program: Convert ProgID to class id. To connect to a running app or to activate a new one, create SObject using the ClassID. Obtain the IApplication interface from the object (use the IObjFace template). Use this interface to get hold of other objects internal to the application. For each such object: Declare a class derived from DispObject In its constructor use the appropriate get_* method to get access to the internal object. Create an object of this class, passing it the interface of the parent object. Obtain the appropriate interface from this object by instantiating the template IObjFace. Call appropriate methods of this interface.

So what was this big deal with reference counting in OLE? Beats me! It must have disappeared, once we started using the correct encapsulation. Below is a diagram of class dependencies. OLE objects on the left, OLE interfaces on the right.

At runtime, you start with the SObject that represents the program you're connecting to and then keep going from object to interface and from interface to DispObject. You use objects as sources of interfaces and interfaces for calling specific methods and obtaining other objects.

Splitter Bar

A splitter bar is a useful control that is not part of the Windows' common bag of controls. How difficult is it to implement it? Not so difficult, as it turns out, once you know the basics of Windows API. The description that follows might seem complicated at first, but you'll be learning several very important techniques that can be used over and over in a variety of situations. Working with child windows, mouse capture, drawing using xor mode, just to mention a few.

A splitter bar is a window. More precisely, it's a child window. It is positioned between two other child windows — we'll call these left and right panes, respectively (or top and bottom, for a horizontal splitter). There must also be a main window that fathers the three children.

Without further ado, here's the code in WinMain that sets up the stage.

// Create top window class

TopWinClassMaker topWinClass (WndProcMain, ID_MAIN, hInst, ID_MAIN);

topWinClass.Register ();

// Create child pane classes

WinClassMaker paneClass (WndProcPane, IDC_PANE , hInst);

paneClass.SetSysCursor (IDC_IBEAM);

paneClass.SetDblClicks ();

paneClass.Register ();

Splitter::RegisterClass (hInst);

// Create top window

ResString caption (hInst, ID_CAPTION);

TopWinMaker topWin (caption, ID_MAIN, hInst);

topWin.Create ();

topWin.Show (cmdShow);

First, we register classes. The top window class is associated with its window procedure WndProcMain, which we'll examine in a moment. The two child panes share the same window class associated with WndProcPane. Next, our own splitter class is registered (we'll see the code soon). Finally, the top window is created and displayed. Child windows are created dynamically during the initialization of the parent window.

Here's the window procedure of the top window.

LRESULT CALLBACK WndProcMain (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {

Controller * pCtrl = GetWinLong<Controller *> (hwnd);

switch (message) {

case WM_CREATE:

try {

pCtrl = new Controller (hwnd, reinterpret_cast<CREATESTRUCT *>(lParam));

SetWinLong<Controller *> (hwnd, pCtrl);

}

catch (char const * msg) {

MessageBox (hwnd, msg, "Initialization", MB_ICONEXCLAMATION | MB_OK);

return -1;

}

catch (...) {

MessageBox (hwnd, "Unknown Error", "Initialization", MB_ICONEXCLAMATION | MB_OK);

return -1;

}

return 0;

case WM_SIZE:

pCtrl->Size (LOWORD(lParam), HIWORD(lParam));

return 0;

case MSG_MOVESPLITTER:

pCtrl->MoveSplitter (wParam);

return 0;

case WM_DESTROY:

SetWinLong<Controller *> (hwnd, 0);

delete pCtrl;

return 0;

}

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

}

It's a pretty standard window procedure, except for one message, MSG_MOVESPLITTER. This is our own, user-defined message that is sent by the splitter control to the parent window. But first, let's have a look at the main window controller.

class Controller {

public:

Controller (HWND hwnd, CREATESTRUCT * pCreat);

~Controller ();

void Size (int cx, int cy);

void MoveSplitter (int x);

private:

enum { splitWidth = 8 }; // width of splitter

// User Interface

HWnd _hwnd; //Main controller window HWnd

_leftWin;

HWnd _rightWin;

HWnd _splitter;

int _splitRatio; // in per cent

int _cx;

int _cy;

};

It has a handle to itself, the two child panes, and the splitter window. It also stores the current split ratio, in percent.

The controller's constructor is responsible for the creation of child windows.

Controller::Controller (HWND hwnd, CREATESTRUCT * pCreat) : _hwnd (hwnd), _leftWin (0), _rightWin (0), _splitter (0), _splitRatio (50) {

// Create child windows

{

ChildWinMaker leftWinMaker (IDC_PANE, _hwnd, ID_LEFT_WINDOW);

leftWinMaker.Create ();

_leftWin.Init (leftWinMaker);

leftWinMaker.Show ();

}

{

ChildWinMaker rightWinMaker (IDC_PANE, _hwnd, ID_RIGHT_WINDOW);

rightWinMaker.Create ();

_rightWin.Init (rightWinMaker);

rightWinMaker.Show ();

}

Splitter::MakeWindow (_splitter, _hwnd, ID_SPLITTER);

}

When the user drags the splitter bar, the parent receives the MSG_MOVESPLITTER messages. The parameter wParam contains the new distance of the splitter bar from the left edge of the parent window. In response to such a message, the parent has to resize both child panes and move the splitter. It does it by calling the Size method.

void Controller::MoveSplitter (int x) {

_splitRatio = x * 100 / _cx;

if (_splitRatio < 0) _splitRatio = 0;

else if (_splitRatio > 100) _splitRatio = 100;

Size (_cx, _cy);

}

In general, Size is called whenever the user resizes the main window, but as you've just seen, we also call it when the splitter is moved.

void Controller::Size (int cx, int cy) {

_cx = cx;

_cy = cy;

int xSplit = (_cx * _splitRatio) / 100;

if (xSplit < 0) xSplit = 0;

if (xSplit + splitWidth >= _cx) xSplit = _cx - splitWidth;

_splitter.MoveDelayPaint (xSplit, 0, splitWidth, cy);

_leftWin.Move (0, 0, xSplit, cy);

_rightWin.Move (xSplit + splitWidth, 0, cx - xSplit - splitWidth, cy);

_splitter.ForceRepaint ();

}

Notice an important trick here. We move the splitter, but delay its repainting until both panes, to its left and to its right, are resized. This technique eliminates some nasty smudging.

That's it as far as client code goes. Now you might be interested to see the implementation of the splitter.

First of all, we like to combine related functions into namespaces. You've seen the calls to Splitter::RegisterClass and Splitter::MakeWindow. The Splitter part of these names is the namespace.

namespace Splitter {

void RegisterClass (HINSTANCE hInst);

void MakeWindow (HWnd & hwndSplitter /* out */, HWnd hwndParent, int childId);

};

Here's how these functions are implemented

LRESULT CALLBACK WndProcSplitter (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

void Splitter::RegisterClass (HINSTANCE hInst) {

WinClassMaker splitterClass (WndProcSplitter, "RsSplitterClass", hInst);

splitterClass.SetSysCursor (IDC_SIZEWE);

splitterClass.SetBgSysColor (COLOR_3DFACE);

splitterClass.Register ();

}

void Splitter::MakeWindow (HWnd & hwndSplitter, HWnd hwndParent, int childId) {

ChildWinMaker splitterMaker ("RsSplitterClass", hwndParent, childId);

splitterMaker.Create ();

hwndSplitter.Init (splitterMaker);

splitterMaker.Show ();

}

The mouse cursor, IDC_SIZEWE, that we associate with the splitter class is the standard Size-West-East double arrow. We also set the background brush to COLOR_3DFACE.

The splitter's window procedure deals with splitter's creation/destruction, painting and mouse dragging.

LRESULT CALLBACK WndProcSplitter (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {

SplitController * pCtrl = GetWinLong<SplitController *> (hwnd);

switch (message) {

case WM_CREATE:

try {

pCtrl = new SplitController (hwnd, reinterpret_cast<CREATESTRUCT *>(lParam));

SetWinLong<SplitController *> (hwnd, pCtrl);

}

catch (char const * msg) {

MessageBox (hwnd, msg, "Initialization", MB_ICONEXCLAMATION | MB_OK);

return -1;

}

catch (...) {

MessageBox (hwnd, "Unknown Error", "Initialization", MB_ICONEXCLAMATION | MB_OK);

return -1;

}

return 0;

case WM_SIZE:

pCtrl->Size (LOWORD(lParam), HIWORD(lParam));

return 0;

case WM_PAINT:

pCtrl->Paint ();

return 0;

case WM_LBUTTONDOWN:

pCtrl->LButtonDown (MAKEPOINTS (lParam));

return 0;

case WM_LBUTTONUP:

pCtrl->LButtonUp (MAKEPOINTS (lParam));

return 0;

case WM_MOUSEMOVE:

if (wParam & MK_LBUTTON) pCtrl->LButtonDrag (MAKEPOINTS (lParam));

return 0;

case WM_CAPTURECHANGED:

pCtrl->CaptureChanged ();

return 0;

case WM_DESTROY:

SetWinLong<SplitController *> (hwnd, 0);

delete pCtrl;

return 0;

}

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

}

This is all pretty much standard code. The details, as usual, are in the controller's methods. The constructor is very simple.

SplitController::SplitController (HWND hwnd, CREATESTRUCT * pCreat) : _hwnd (hwnd), _hwndParent (pCreat->hwndParent) {}

Painting is more interesting. We have to imitate Windows 2.5-dimensional effects. We do it with a carfuly chosen selection of pens.

class Pens3d {

public:

Pens3d ();

Pen & Hilight () {

return _penHilight;

}

Pen & Light () {

return _penLight;

}

Pen & Shadow () {

return _penShadow;

}

Pen & DkShadow () {

return _penDkShadow;

}

private:

Pen _penHilight;

Pen _penLight;

Pen _penShadow;

Pen _penDkShadow;

};

Pens3d::Pens3d () : _penLight (GetSysColor (COLOR_3DLIGHT)), _penHilight (GetSysColor (COLOR_3DHILIGHT)), _penShadow (GetSysColor (COLOR_3DSHADOW)), _penDkShadow (GetSysColor (COLOR_3DDKSHADOW)) {}

void SplitController::Paint () {

PaintCanvas canvas (_hwnd);

{

PenHolder pen (canvas, _pens.Light ());

canvas.Line (0, 0, 0, _cy - 1);

}

{

PenHolder pen (canvas, _pens.Hilight ());

canvas.Line (1, 0, 1, _cy - 1);

}

{

PenHolder pen (canvas, _pens.Shadow ());

canvas.Line (_cx - 2, 0, _cx - 2, _cy - 1);

}

{

PenHolder pen (canvas, _pens.DkShadow ());

canvas.Line (_cx - 1, 0, _cx - 1, _cy - 1);

}

}

The tricky part is the processing of mouse messages, although most of this code is pretty standard. We have to be able to deal with button-down, mouse drag and button-up.

void SplitController::LButtonDown (POINTS pt) {

_hwnd.CaptureMouse ();

// Find x offset of splitter

// with respect to parent client area

POINT ptOrg = {0, 0};

_hwndParent.ClientToScreen (ptOrg);

int xParent = ptOrg.x;

ptOrg.x = 0;

_hwnd.ClientToScreen (ptOrg);

int xChild = ptOrg.x;

_dragStart = xChild - xParent + _cx / 2 - pt.x;

_dragX = _dragStart + pt.x;

// Draw a divider using XOR mode

UpdateCanvas canvas (_hwndParent);

ModeSetter mode (canvas, R2_NOTXORPEN);

canvas.Line (_dragX, 0, _dragX, _cy - 1);

}

When the left mouse button is clicked over the client area of the splitter, we perform the following tasks. First, we capture the mouse. The user might, and probably will, drag the mouse cursor outside of the splitter bar. Capturing the mouse will ensure that all mouse messages will now be directed to us, even though the mouse cursor may wander all over the screen.

Next, we convert the local splitter-bar coordinates to the parent window coordinates. We do it by converting the parent's origin to screen coordinates and converting our (splitter's) origin to screen coordinates. The difference gives us our origin with respect to parent's client area. We do some more arithmetics in order to find the x coordinate of the center of the splitter, because that's what we'll be dragging.

To give the user dragging feedback, we draw a single vertical line that will be dragged across the client area of the parent window. Notice two important details. The canvas that we are creating are associated with the parent — we are using the parent's window handle. The drawing mode is logical xor (exclusive or). That means that the pixels we are drawing are xor'd with the original pixels. Logical xor has this useful property that if you apply it twice, you restore the original. It's the oldest trick in computer graphics — the poor man's animation technique. You draw something using the xor mode and erase it simply by drawing it again in the xor mode. Just look at the dragging code below.

void SplitController::LButtonDrag (POINTS pt) {

if (_hwnd.HasCapture ()) {

// Erase previous divider and draw new one

UpdateCanvas canvas (_hwndParent);

ModeSetter mode (canvas, R2_NOTXORPEN);

canvas.Line (_dragX, 0, _dragX, _cy - 1);

_dragX = _dragStart + pt.x;

canvas.Line (_dragX, 0, _dragX, _cy - 1);

}

}

We draw the vertical line in the xor mode using the previous remembered position. Since this is the second time we draw this line in the same place, the net result will be the restoration of the original pixels from under the line. Then we draw the new line in the new position, still in the xor mode. We remember this position, so that next time LButtonDrag is called we'll erase it too. And so on, until finally the user releases the mouse button.

void SplitController::LButtonUp (POINTS pt) {

// Calling ReleaseCapture will send us the WM_CAPTURECHANGED

_hwnd.ReleaseMouse ();

_hwndParent.SendMessage (MSG_MOVESPLITTER, _dragStart + pt.x);

}

At this point we should erase the vertical line for the last time. However, we don't do it directly--we use a little trick here. We release the mouse capture. As a side effect, Windows sends us the WM_CAPTURECHANGED message. It's during the processing of that message that we actually erase the vertical line.

void SplitController::CaptureChanged () {

// We are losing capture

// End drag selection — for whatever reason

// Erase previous divider

UpdateCanvas canvas (_hwndParent);

ModeSetter mode (canvas, R2_NOTXORPEN);

canvas.Line (_dragX, 0, _dragX, _cy - 1);

}

Why do we do that? It's because Windows can force us to release the mouse capture before the user releases the mouse button. It might happen, for instance, when another application suddenly decides to pop up its window while the user is in the middle of dragging. In such a case our window would never get the button-up message and, if we weren't clever, wouldn't be able to cleanly finish the drag. Fortunately, before taking away the mouse capture, Windows manages to send us the WM_CAPTURECHANGED message, and we take it as a clue to do our cleanup.

Going back to LButtonUp — once the user finishes dragging, we send the parent our special message MSG_MOVESPLITTER, passing it the new position of the center of the splitter bar measured in parent's client area coordinates. You've already seen the client code response to this message.

This is how one might define a client message.

// Reserved by Reliable Software Library

const UINT MSG_RS_LIBRARY = WM_USER + 0x4000;

// wParam = new position wrt parent's left edge

const UINT MSG_MOVESPLITTER = MSG_RS_LIBRARY + 1;

Finally, here's an excerpt from a very useful class HWnd that encapsulates a lot of basic Windows APIs that deal with windows. In particular, look at the methods MoveDelayPaint and ForceRepaint that we used in repainting the splitter bar.

class HWnd {

public:

void Update () {

::UpdateWindow (_hwnd);

}

// Moving

void Move (int x, int y, int width, int height) {

::MoveWindow (_hwnd, x, y, width, height, TRUE);

}

void MoveDelayPaint (int x, int y, int width, int height) {

::MoveWindow (_hwnd, x, y, width, height, FALSE);

}

// Repainting

void Invalidate () {

::InvalidateRect (_hwnd, 0, TRUE);

}

void ForceRepaint () {

Invalidate ();

Update ();

}

private:

HWND _hwnd;

};

Next tutorial talks about bitmaps.

Bitmaps

In this tutorial we'll learn how to load bitmaps from resources and from files, how to pass them around and blit them to the screen. We'll also see how to create and use an empty bitmap as a canvas, draw a picture on it and then blit it to the screen. Finally, we'll combine these techniques to write a simple program that uses double-buffering and timer messages to show a simple animation involving sprites.

First of all, in most cases Windows provides storage for bitmaps and takes care of the formatting of bits. The programmer gets access to the bitmap through a handle, whose type is HBITMAP. (Remember to set the STRICT flag when compiling windows programs, to make sure hbitmap is a distinct type, rather than just a pointer to void.)

Since a bitmap is a resource (in the Resource Management sense), the first step is to encapsulate it in a "strong pointer" type of interface. notice the transfer semantics of the constructor and the overloaded assignment operator, characteristic of a resource that can have only one owner at a time.

We instruct Windows to release the resources allocated to the bitmap by calling DeleteObject.

class Bitmap {

public:

Bitmap () : _hBitmap (0) {}

// Transfer semantics

Bitmap (Bitmap & bmp) : _hBitmap (bmp.Release ()) {}

void operator = (Bitmap & bmp) {

if (bmp._hBitmap != _hBitmap) {

Free ();

_hBitmap = bmp.Release ();

}

}

HBITMAP Release () {

HBITMAP h = _hBitmap;

_hBitmap = 0;

return h;

}

~Bitmap () {

Free ();

}

// implicit conversion for use with Windows API

operator HBITMAP () {

return _hBitmap;

}

protected:

Bitmap (HBITMAP hBitmap) : _hBitmap (hBitmap) {}

void Free () {

if (_hBitmap) ::DeleteObject (_hbitmap);

}

HBITMAP    _hBitmap;

};

Now that the management issues are out of the way, we can concentrate on loading bitmaps. The simplest way to include a bitmap in your program is to add it to the resource file. In the resource editor of your development environment you can create new bitmaps or import them from external files. You can either give them names (strings) or numerical ids. When you want to access such a bitmap in your program you have to load it from the resources. Here are two methods that do just that. You have to give them a handle to the program instance.

void Bitmap::Load (HINSTANCE hInst, char const * resName) {

Free ();

_hBitmap = (HBITMAP) ::LoadImage (hInst, resName, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);

if (_hBitmap == 0) throw WinException ("Cannot load bitmap from resources", resName);

}

void Bitmap::Load (HINSTANCE hInst, int id) {

Free ();

_hBitmap = (HBITMAP) ::LoadImage (hInst, MAKEINTRESOURCE (id), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);

if (_hBitmap == 0) throw WinException ("Cannot load bitmap from resources");

}

Loading a bitmap directly from a file is also very simple and can be done using the same API, LoadImage. Remember, it will only work if the file is a Windows (or OS/2) bitmap — such files usually have the extension .bmp. There is no simple way of loading other types of graphics files, .gif, .jpg, .png, etc. You have to know their binary layout and decode them explicitly (there are other web sites that have this information).

void Bitmap::Load (char * path) {

Free ();

_hBitmap = (HBITMAP) ::LoadImage (0, path, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);

if (_hBitmap == 0) throw WinException ("Cannot load bitmap from file", path);

}

Once you got hold of a bitmap, you may want to enquire about its dimensions. Here's how you do it.

void Bitmap::GetSize (int & width, int & height) {

BITMAP bm;

::GetObject (_hBitmap, sizeof (bm), & bm);

width = bm.bmWidth;

height = bm.bmHeight;

}

Finally, you might want to create an empty bitmap and fill it with your own drawings programmatically. You have to specify the dimensions of the bitmap and you have to provide a device context (Canvas) for which the bitmap is targeted. Windows will create a different type of bitmap when your target is a monochrome monitor or printer, and different when it's a graphics card set to True Color. Windows will create a bitmap that is compatible with the target device.

Bitmap::Bitmap (Canvas & canvas, int dx, int dy) : _hBitmap (0) {

CreateCompatible (canvas, dx, dy);

}

void Bitmap::CreateCompatible (Canvas & canvas, int width, int height) {

Free ();

hBitmap = ::CreateCompatibleBitmap (canvas, width, height);

}

How do you display the bitmap on screen? You have to blit it. Blit stands for "block bit transfer" or something like that. When you blit a bitmap, you have to specify a lot of parameters, so we'll just encapsulate the blitting request in a separate object, the blitter. This is a very handy object that sets the obvious defaults for blitting, but at the same time lets you override each and any of them.

A blitter transfers a rectangular area of the bitmap into a rectangular area of the screen. The meaning of various parameters is the following:

Source position: pixel coordinates of the upper left corner of the bitmap area, to be transferred. The default is the upper left corner of the bitmap. Destination position: pixel coordinates within the target window of the upper left corner of the transferred area. The default is upper left corner of the window. Area dimensions: the dimensions of the rectangular area to be transferred. The default is the dimensions of the bitmap. Transfer mode. The way bitmap pixels are combined with existing window pixels. The default, SRCCOPY, copies the pixels over existing pixels. You may also specify more involved logical operations, like SRCAND (Boolean AND), SRCPAINT (Boolean OR), etc. (see your compiler's help on BitBlt).

class Blitter {

public:

Blitter (Bitmap & bmp) : _bmp (bmp), _mode (SRCCOPY), _xDst (0), _yDst (0), _xSrc (0), _ySrc (0) {

bmp.GetSize (_width, _height);

}

void SetMode (DWORD mode) {

_mode = mode;

}

void SetDest (int x, int y) {

_xDst = x;

_yDst = y;

}

void SetSrc (int x, int y) {

_xSrc = x;

_ySrc = y;

}

void SetArea (int width, int height) {

_width = width;

_height = height;

}

// transfer bitmap to canvas

void BlitTo (Canvas & canvas);

private:

Bitmap & _bmp;

int     _xDst, _yDst;

int     _xSrc, _ySrc;

int     _width, _height;

DWORD   _mode;

};

The BlitTo method performs the transfer from the bitmap to the window (or printer) as described by its Canvas.

void Blitter::BlitTo (Canvas & canvas) {

// Create canvas in memory using target canvas as template

MemCanvas memCanvas (canvas);

// Convert bitmap to the format appropriate for this canvas

memCanvas.SelectObject (_bmp);

// Transfer bitmap from memory canvas to target canvas

::BitBlt (canvas, _xDst, _yDst, _width, _height, memCanvas, _xSrc, _ySrc, _mode);

}

The API, BitBlt, transfers bits from one device to another. That's why we have to set up a fake source device. This "memory canvas" is based on the actual canvas--in this case we use target canvas as a template. So, for instance, if the target canvas describes a True Color device, our MemCanvas will also behave like a True Color device. In particular, when our bitmap is selected into it, it will be converted to True Color, even if initially it was a monochrome or a 256-color bitmap.

The simplest program that loads and displays a bitmap might look something like this: There is a View object that contains a bitmap (I assume that the file "picture.bmp" is located in the current directory). It blits it to screen in the Paint method.

class View {

public:

View () {

_background.Load ("picture.bmp");

}

void Paint (Canvas & canvas) {

Blitter blitter (_background);

blitter.BlitTo (canvas);

}

private:

Bitmap  _background;

};

A sprite is an animated bitmap that moves over some background. We already know how to display bitmaps — we could blit the background first and then blit the sprite bitmap over it. This will work as long as the sprite is rectangular. If you want to be more sophisticated and use a non-rectangular sprite, you need a mask.

The two pictures below are that of a sprite (my personal pug, Fanny) and its mask. The mask is a monochrome bitmap that has black areas where we want the sprite to be transparent. The sprite, on the other hand, must be white in these areas. What we want is to be able to see the background through these areas.

The trick is to first blit the background, then blit the mask using logical OR, and then blit the sprite over it using logical AND.

ORing a black mask pixel (all zero bits) with a background pixel will give back the background pixel. ORing a white mask pixel (all one bits) with a background will give a white pixel. So after blitting the mask, we'll have a white ghost in the shape of our sprite floating over the background.

ANDing a white sprite pixel (all ones) with a background pixel will give back the background pixel. ANDing a non-white sprite pixel with the white (all ones) background (the ghost from previous step) will give the sprite pixel. We'll end up with the sprite superimposed on the background.

What remains is to implement the animation. The naive implementation would be to keep re-drawing the image: background, mask and sprite, changing the position of the mask and the sprite in each frame. The problem with this approach is that it results in unacceptable flutter. The trick with good animation is to prepare the whole picture off-line, as a single bitmap, and then blit it to screen in one quick step. This technique is called double buffering-the first buffer being the screen buffer, the second one-our bitmap.

We'll also use Windows timer to time the display of frames.

class WinTimer {

public:

WinTimer (HWND hwnd = 0, int id = -1) : _hwnd (hwnd), _id (id) {}

void Create (HWND hwnd, int id) {

_hwnd = hwnd;

_id = id;

}

void Set (int milliSec) {

::SetTimer (_hwnd, _id, milliSec, 0);

}

void Kill () {

::KillTimer (_hwnd, _id);

}

int  GetId () const { return _id; }

private:

HWND    _hwnd;

int     _id;

};

We'll put the timer in our Controller object and initialize it there.

class Controller {

public:

Controller(HWND hwnd, CREATESTRUCT * pCreate);

~Controller ();

void    Timer (int id);

void    Size (int x, int y);

void    Paint ();

void    Command (int cmd);

private:

HWND        _hwnd;

WinTimer    _timer;

View        _view;

};

Controller::Controller (HWND hwnd, CREATESTRUCT * pCreate) :_hwnd (hwnd), _timer (hwnd, 1), _view (pCreate->hInstance) {

_timer.Set (100);

}

Once set, the timer sends our program timer messages and we have to process them.

LRESULT CALLBACK MainWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {

Controller * pCtrl = WinGetLong<Controller *> (hwnd);

switch (message) {

…

case WM_TIMER:

pCtrl->Timer (wParam);

return 0;

…

}

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

}

void Controller::Timer (int id) {

_timer.Kill ();

_view.Step ();

UpdateCanvas canvas (_hwnd);

_view.Update (canvas);

::InvalidateRect (_hwnd, 0, FALSE);

_timer.Set (50);

}

void Controller::Paint () {

PaintCanvas canvas (_hwnd);

_view.Paint (canvas);

}

The Update method of View is the workhorse of our program. It creates the image in the buffer. We then call InvalidateRectangle to force the repaint of our window (the last parameter, FALSE, tells Windows not to clear the previous image — we don't want it to flash white before every frame).

Here's the class View, with the three bitmaps.

class View {

public:

View (HINSTANCE hInst);

void SetSize (int cxNew, int cyNew) {

_cx = cxNew;

_cy = cyNew;

}

void Step () { ++_tick; }

void Update (Canvas & canvas);

void Paint (Canvas & canvas);

private:

int     _cx, _cy;

int     _tick;

Bitmap  _bitmapBuf; // for double buffering

Bitmap  _background;

int     _widthBkg, _heightBkg;

Bitmap  _sprite;

Bitmap  _mask;

int     _widthSprite, _heightSprite;

};

View::View (HINSTANCE hInst) : _tick (0) {

// Load bitmap from file

_background.Load ("picture.bmp");

// Load bitmap from resource

_background.GetSize (_widthBkg, _heightBkg);

// Load bitmaps from resources

_sprite.Load (hInst, IDB_FANNY);

_mask.Load (hInst, IDB_MASK);

_sprite.GetSize (_widthSprite, _heightSprite);

DesktopCanvas canvas;

_bitmapBuf.CreateCompatible (canvas, 1, 1);

_cx = 1;

_cy = 1;

}

And here's the implementation of Update. We create a bitmap canvas in memory, making it compatible with the current display canvas. We blit the background image into it, then blit the mask and the sprite (notice the change of position for each frame). Finally, we transfer the complete bitmap into our buffer (overloaded assignment operator at work!).

void View::Update (Canvas & canvas) {

const double speed = 0.01;

Bitmap bmp (canvas, _cx, _cy);

BitmapCanvas bmpCanvas (canvas, bmp);

RECT rect = {0, 0, _cx, _cy};

bmpCanvas.WhiteWash (rect);

// Do the off-line drawing

Blitter bltBkg (_background);

bltBkg.BlitTo (bmpCanvas);

int xRange = (_widthBkg - _widthSprite) / 2;

int yRange = (_heightBkg - _heightSprite) / 2;

int x = xRange + static_cast<int> (xRange * sin (speed * _tick));

int y = yRange + static_cast<int> (yRange * cos (4 * speed * _tick));

Blitter bltMask (_mask);

bltMask.SetMode (SRCPAINT);

bltMask.SetDest (x, y);

bltMask.BlitTo (bmpCanvas);

Blitter bltSprite (_sprite);

bltSprite.SetMode (SRCAND);

bltSprite.SetDest (x, y);

bltSprite.BlitTo (bmpCanvas);

// update the buffer

_bitmapBuf = bmp;

}

For completeness, here's the definition of bitmap canvas. You draw directly on this canvas using standard canvas methods, like Line, Text, SetPixel, etc… Here we only blit bitmaps into it.

class BitmapCanvas: public MemCanvas {

public:

BitmapCanvas (HDC hdc, HBITMAP hBitmap) : MemCanvas (hdc) {

// convert bitmap to format compatible with canvas

_hOldBitmap = reinterpret_cast (::SelectObject (_hdc, hBitmap));

}

~BitmapCanvas () {

::SelectObject (_hdc, _hOldBitmap);

}

private:

HBITMAP _hOldBitmap;

};

class MemCanvas: public Canvas {

public:

MemCanvas (HDC hdc) : Canvas (::CreateCompatibleDC (hdc)) {}

~MemCanvas () {

::DeleteDC(_hdc);

}

};

Now, if you want more speed, read about DirectDraw.

Direct Draw

Direct Draw App

If you want fast animation (games!), you have to use DirectX. Try filling the screen by drawing each pixel separately using GDI. Compare it with DirectX, which gives you practically direct access to the screen buffer. There's no competition!

The bad news is that DirectX involves dealing with COM interfaces. The good news is that you can encapsulate them nicely inside C++ classes. Let me show you first how to use these classes to write a simple application that displays an animated, rotating, bitmap. We'll use Windows timer to drive the animation. We'll have to:

• Initialize the program, including the DirectX subsystem

• Respond to timer messages

• Respond to window resizing (we'll use the window mode, rather than full screen)

• Respond to paint messages

Let's start with the WM_PAINT message. This message is sent to us when Windows needs to repaint our window. We have to stash somewhere a complete animation frame, so that we can quickly blit it to screen in response to WM_PAINT. The storage for this frame will be called the back surface.

Upon receiving the timer message, we have to paint a new frame on the back surface. Once we're done, we'll invalidate our window, so that WM_PAINT message will be sent to us.

Upon resizing the window, we have to create a new back surface, corresponding to the new window size. Then we'll draw on it and invalidate the window.

The initialization involves creating the timer, creating the World (that's where we keep our bitmap and drive the animation), creating the View, and setting the first timer countdown.

void Controller::Create (CREATESTRUCT * create) {

_timer.Create (_h, 1);

_world = new World (create->hInstance, IDB_RS);

_view.Create (_h, _world);

_timer.Set (10);

}

Here's our response to WM_SIZE. We let the View know about the new size (it will create a new back surface), we call it's Update method which draws the animation frame, and we invalidate the window.

bool Controller::Size (int width, int height) {

_view.Size (_h, width, height);

_view.Update ();

// force repaint

_h.Invalidate (false);

return true;

}

In response to WM_PAINT, we let the View do the painting (blit the back surface to the screen). We also have to tell Windows that the painting is done-validate the uncovered area. This is done simply by constructing the PaintCanvas object.

bool Controller::Paint (HDC hdc) {

_view.Paint (_h);

// validate painted area

PaintCanvas canvas (_h);

return true;

}

In response to WM_TIMER, we kill the timer, tell the World to move one frame ahead, let the View repaint the frame on the back surface, invalidate the window (so that Paint is called) and set the timeout again.

bool Controller::Timer (int id, TIMERPROC * proc) {

_timer.Kill ();

_world->Step ();

_view.Update ();

// force repaint

_h.Invalidate (false);

_timer.Set (10);

return true;

}

There is one more thing. DirectX doesn't like when a screen saver kicks in, so we have to preempt it.

bool Controller::SysCommand (int cmd) {

// disable screen savers

if (cmd == SC_SCREENSAVE || cmd == SC_MONITORPOWER) return true;

return false;

}

By the way, this is how SysCommand is called from the window procedure:

case WM_SYSCOMMAND:

if (pCtrl->SysCommand (wParam & 0xfff0)) return 0;

break;

Let's now have a closer look at View, which is the class dealing with output to screen. Notice two important data members, the Direct::Draw object and the Direct::Surface object.

class View {

public: View ();

void Create (HWND h, World * world);

void Size (HWND h, int width, int height);

void Update ();

void Paint (HWND h);

private:

Direct::Draw _draw;

Direct::Surface _backSurface;

World * _world;

int _dx;

int _dy;

};

I have encapsulated all DirectX classes inside the namespace Direct. this trick enforces a very nice naming convention, namely, you have to prefix all directx classes with the prefix Direct::.

The Direct::Draw object's duty is to initialize the DirectX subsystem. It does it in it's constructor, so any time View is constructed (once per program instantiation), DirectX is ready to use. It's destructor, by the way, frees the resources used by DirectX.

Direct::Surface _backSurface will be used by View to draw the animation upon it.

The initialization of DirectX also involves setting up the collaboration level with Windows. Here, we are telling it to work nicely with other windows, rather then taking over in the full-screen mode.

void View::Create (HWND h, World * world) {

_world = world;

// Set coopration with Windows

_draw.SetCoopNormal (h);

}

Back surface is created (and re-created) in response to WM_SIZE message. Surfaces are implemented in such a way that regular assignment operation does the right thing. It deallocates the previous surface and initializes a new one. Notice that the constructor of an OffScreenSurface takes the Direct::Draw object and the dimensions of the surface in pixels.

void View::Size (HWND h, int width, int height) {

_dx = width;

_dy = height;

if (_dx == 0 || _dy == 0) return;

_backSurface = Direct::OffScreenSurface (_draw, _dx, _dy);

}

Painting is slightly tricky. We have to get access to the primary drawing surface, which is the whole screen buffer. But since we are not using the full-screen mode, we want to draw only inside the rectangular area of our window. That's why we create a Direct::Clipper, to clip anything that would fall outside of that area. The clipper figures out the correct area using the handle to the window that we are passing to it.

Next, we create the primary surface and pass it the clipper. Now we can blit the image directly from our back surface to the screen. Again, since the primary surface represents the whole screen, we have to offset our blit by specifying the target rectangle. The auxillary class Direct::WinRect retrieves the coordinates of that rectangle.

void View::Paint (HWND h) {

if (_dx == 0 || _dy == 0) return;

// Clip to window

Direct::Clipper clipper (_draw);

clipper.SetHWnd (h);

// Screen surface

Direct::PrimarySurface surf (_draw);

surf.SetClipper (clipper);

Direct::WinRect rect (h);

// Blit from back surface to screen

surf.BltFrom (_backSurface, &rect);

}

Finally, the actual drawing is done using direct access to the back surface buffer. But first, we flood the back surface with white background. Then we construct the Direct::SurfaceBuf. This is the object that let's us set pixels directly into the buffer's memory (depending on implementation, it might be your video card memory or a chunk of your system memory). The details of our drawing algorithm are inessential here. Suffice it to say that the work of setting the pixel is done in the SetPixel method.

void View::Update () {

if (_dx == 0 || _dy == 0) return;

try {

// white background

_backSurface.Fill (RGB (255, 255, 255));

{

// Get direct access to back surface

Direct::SurfaceBuf buf (_backSurface);

// draw bitmap within a centered square

int side = 100;

if (_dx < side) side = _dx;

if (_dy < side) side = _dy;

int xOff = (_dx - side) / 2;

int yOff = (_dy - side) / 2;

assert (xOff >= 0);

assert (yOff >= 0);

double dxInv = 1.0 / side;

double dyInv = 1.0 / side;

for (int i = 0; i < side; ++i) {

for (int j = 0; j < side; ++j) {

double u = dxInv * i;

double v = dyInv * j;

COLORREF color = _world->GetTexel (u, v);

// draw pixel directly

buf.SetPixel (xOff + i, yOff + j, color);

}

}

}

// Paint will blit the image to screen

}

catch (char const * msg) {

::MessageBox (0, msg, "Viewer error", MB_OK | MB_ICONERROR);

throw;

}

catch (...) {

::MessageBox (0, "Unknown error", "Viewer error", MB_OK | MB_ICONERROR);

throw;

}

}

Besides being able to set individual pixels quickly, DirectX also provides ways to efficiently blit bitmaps or even use GDI functions to write to a surface.

Implementation

As mentioned before, all DirectDraw objects are enclosed in the Direct namespace. The fundamental object, Direct::Draw, is responsible for the initialization and the release of the DirectDraw subsystem. It can also be used to set up the cooperation level with Windows. It's easy to add more methods and options to this class, as the need arises.

namespace Direct { // Direct Draw object

class Draw {

public:

Draw ();

~Draw () {

_pDraw->Release ();

}

void SetCoopNormal (HWND h) {

HRESULT res = _pDraw->SetCooperativeLevel(h, DDSCL_NORMAL);

if (res != DD_OK) throw "Cannot set normal cooperative level";

}

IDirectDraw * operator-> () { return _pDraw; }

private:

IDirectDraw * _pDraw;

};

}

// implementation file

using namespace Direct;

Draw::Draw () {

HRESULT res = ::DirectDrawCreate (0, &_pDraw, 0);

if (res != DD_OK) throw "Cannot create Direct Draw object";

}

DirectDraw is based on COM interfaces, so it makes sense to encapsulate the "interface magic" in a handy template. The Direct::IFace class takes care of reference counting, pointer dereferencing and assignment (see the assignment of surfaces above).

template<class I>

class IFace {

public:

IFace (IFace<I> & i) {

i->AddRef ();

_i = i._i;

}

~IFace () {

if (_i) _i->Release ();

}

void operator = (IFace<I> & i) {

if (i._i) i._i->AddRef ();

if (_i) _i->Release ();

_i = i._i;

}

I * operator-> () { return _i; }

operator I * () { return _i; }

protected:

IFace () : _i (0) {}

protected:

I * _i;

};

A drawing surface is an example of an interface. First we define a generic surface, then we'll specialize it to primary and off-screen surfaces.

class Surface: public IFace<IDirectDrawSurface> {

friend class SurfaceBuf;

protected:

// Locking and unlocking the whole surface

void Lock (SurfaceDesc & desc) {

assert (_i != 0);

HRESULT res;

do res = _i->Lock (0, &desc, 0, 0); while (res == DDERR_WASSTILLDRAWING);

if (res != DD_OK) throw "Cannot lock surface";

}

void Unlock () {

assert (_i != 0);

_i->Unlock (0);

}

public:

Surface () {}

void GetDescription (SurfaceDesc & desc) {

HRESULT res = _i->GetSurfaceDesc (&desc);

if (res != DD_OK) throw "Cannot get surface description";

}

void SetClipper (Clipper & clipper) {

assert (_i != 0);

HRESULT res = _i->SetClipper (clipper);

if (res != DD_OK) throw "Cannot set clipper";

}

void BltFrom (Surface & src, RECT * dstRect = 0, RECT * srcRect = 0) {

assert (_i != 0);

HRESULT res = _i->Blt (dstRect, src._i, srcRect, 0, 0);

if (res != DD_OK) throw "Cannot perform a blt";

}

void Fill (COLORREF color);

};

When you create a surface, it has to be one of the derived ones. The primary surface lets you draw directly on your screen, the off-screen surface lets you prepare a picture off-screen in a format that can be very quickly transferred to the screen.

class PrimarySurface: public Surface {

public:

PrimarySurface () {}

PrimarySurface (Draw & draw) {

Init (draw);

}

void Init (Draw & draw) {

SurfaceDesc desc;

desc.SetCapabilities (DDSCAPS_PRIMARYSURFACE);

HRESULT res = draw->CreateSurface (&desc, &_i, 0);

if (res != DD_OK) throw "Cannot create primary surface";

}

};

class OffScreenSurface: public Surface {

public:

OffScreenSurface () {}

OffScreenSurface (Draw & draw, int width, int height) {

Init (draw, width, height);

}

void Init (Draw & draw, int width, int height) {

SurfaceDesc desc;

desc.SetCapabilities (DDSCAPS_OFFSCREENPLAIN);

desc.SetDimensions (width, height);

HRESULT res = draw->CreateSurface (&desc, &_i, 0);

if (res != DD_OK) throw "Cannot create off-screen surface";

}

};

In order to directly access pixels in a surface, you have to lock it. The class, SurfaceBuf, encapsulates locking and unlocking in its constructor and destructor, so that you don't have to worry about it. It also provides direct access to the buffer through its SetPixel method. Notice the low-level address calculations and color formatting.

class SurfaceBuf {

public:

SurfaceBuf (Surface & surface) : _surface (surface) {

SurfaceDesc desc;

surface.Lock (desc);

_pitch = desc.Pitch ();

_buf = static_cast<unsigned char *> (desc.Buffer ());

_format.Init (desc);

int bpp = _format.BitsPp ();

if (bpp != 16 && bpp != 24 && bpp != 32) {

surface.Unlock ();

throw "Only high color and true color supported";

}

}

~SurfaceBuf () {

_surface.Unlock ();

}

void SetPixel (int x, int y, COLORREF color) {

switch (_format.BitsPp ()) {

case 16:

{

int offset = y * _pitch + x * 2;

unsigned short * p = reinterpret_cast<unsigned short *> ( _buf + offset);

*p &= _format.Mask ();

*p |= static_cast<unsigned short> ( _format.ColorValue16 (color));

}

break;

case 24:

{

int offset = y * _pitch + x * 3;

unsigned long * p = reinterpret_cast<unsigned long *> ( _buf + offset);

*p &= _format.Mask ();

*p |= _format.ColorValue24 (color);

}

break;

case 32:

{

int offset = y * _pitch + x * 4;

unsigned long * p = reinterpret_cast<unsigned long *> ( _buf + offset);

*p &= _format.Mask ();

*p |= _format.ColorValue32 (color);

}

break;

}

}

private:

Surface & _surface;

unsigned char * _buf;

int _pitch;

PixelFormat _format;

};

There is a separate class to deal with the formatting of color values. As you can see, depending on the bits-per-pixel setting of the video card, different color encodings are used. The surface descriptor contains bit masks that are used in this encoding. These masks vary not only between bpp settings, but also between video cards. The most complex is the encoding of the color for the 6-bit setting. In 32-bit mode the card can actually support more colors that can be packed into the standard Windows COLORREF. Here, we're not making use of it, but it would be an interesting area to experiment.

class PixelFormat {

public:

PixelFormat () {}

PixelFormat (SurfaceDesc & desc) {

Init (desc);

}

void Init (SurfaceDesc & desc);

int BitsPp () const { return _bpp; }

unsigned long Mask () const { return _mask; }

unsigned long ColorValue16 (COLORREF color) {

return ( (GetRValue (color) << _redShift) & (_redMask << 16) | (GetGValue (color) << _greenShift) & (_greenMask << 16) | (GetBValue (color) << _blueShift) & (_blueMask << 16)) >> 16;

}

unsigned long ColorValue24 (COLORREF color) {

return (GetRValue (color) << 16) & _redMask | (GetGValue (color) << 8) & _greenMask | GetBValue (color) & _blueMask;

}

unsigned long ColorValue32 (COLORREF color) {

return (GetRValue (color) << 16) & _redMask | (GetGValue (color) << 8) & _greenMask | GetBValue (color) & _blueMask;

}

unsigned long ColorValue (COLORREF color) {

switch (_bpp) {

case 16:

return ColorValue16 (color);

case 24:

return ColorValue24 (color);

case 32:

return ColorValue32 (color);

default:

throw "PixelFormat: only 16, 24 and 32 bits supported";

}

}

private:

int _bpp; // bits per pixel 4, 8, 16, 24, or 32

unsigned long _redMask;

unsigned long _greenMask;

unsigned long _blueMask;

unsigned _redShift;

unsigned _greenShift;

unsigned _blueShift;

unsigned long _mask;

};

unsigned HiBitShift (unsigned val) {

unsigned i = 0;

while (val != 0) {

val >>= 1;

++i;

}

return i;

}

void PixelFormat::Init (SurfaceDesc & desc) {

DDPIXELFORMAT & format = desc.PixelFormat ();

if (format.dwFlags != DDPF_RGB) throw "Direct Draw: Non-RGB formats not supported";

_bpp = format.dwRGBBitCount;

_redMask = format.dwRBitMask;

_greenMask = format.dwGBitMask;

_blueMask = format.dwBBitMask;

_mask = ~(_redMask | _greenMask | _blueMask);

switch (_bpp) {

case 16:

_redShift = HiBitShift (_redMask) + 8;

_greenShift = HiBitShift (_greenMask) + 8;

_blueShift = HiBitShift (_blueMask) + 8;

break;

case 24:

break;

case 32:

break;

default:

throw "Only 16, 24 and 32 bit graphics supported";

}

}

Notice that this tutorial only scratches the surface of DirectX. There are several versions of DirectDraw. There is Direct3D for three-dimensional graphics (although it seems like Open GL might be a better platform). Sound can be dealt with using DirectSound and input devices have their DirectInput. All these subsystems can be encapsulated using similar techniques.

Joining the Tutorial Project

This is an exprerimental subscription service — a new method of distributing and updating sources. It uses our version control system, Code Co-op, to create a project on your machine and enlist you as an anonymous project member. You will be able to download the sources of all the tutorials in the form of Code Co-op synchronization scripts.

When we modify the sources, you'll be able to obtain modification scripts that will automatically update your copy of the project. These scripts will only contain changes , so the updates will be much smaller than the original sources.

Download and install Code Co-op (less than 1mb).

During installation, configure your machine as standalone. Notice: the whole procedure is free. Normally, you have to buy a license to use Code Co-op. However, you don't need a license to be an "observer" in a project. Additionally, for the first 31 days after installation, you'll have access to the full functionality of the program. You're welcome to experiment with it.

Notice also that the installation of Code Co-op (unlike many other programs) does not replace any dll's in your system directory. So, after you've defected from all projects and ran uninstall from the Control Panel, there will be no trace of our program on your computer!

Join the Tutorial project.

Start Code Co-op. Select Join from the Project menu. Fill out the following dialog:

Make sure the items circled in red are filled in as in the picture. Type in a path where you want the sources on your machine (the database path will default to its subdirectory, Co-op). Provide your name and e-mail address (we'll notify you about updates, but no spam!).

The dispatcher will try to email a join request to tutorial@relisoft.com. If your e-mail program is not Simple MAPI compatible, it will fail. Don't worry: delete the join script from your local outbox (in the example above it would be at New">c:\your path\tutorial\Co-op\Outbox") and send us a note (may be empty) to tutorial@relisoft.com, so that we can notify you about updates.

Download initisal scripts.

This is a zipped file. Unzip its contents into the local inbox of your project (it's the Inbox subdirectory of your project's database). In the example above, the path would be c:\your path\tutorial\Co-op\Inbox. (Do not put it in the Global Inbox!)

Synchronize the Project.

Start Code Co-op, (visit the Tutorial project, if necessary).

Click on the Mailbox tab if necessary,

Click on the Synchronize button.

If there are more scripts in the mailbox, repeat the following steps.

In the Mailbox area, click on the Synchronize button. You'll be transferred to the Synch Area,

Click the Accept Synch button.

Update the Project Occasionally

When you get a notification from us, download the appropriate script(s), copy them into your local inbox and perform the synchronization. If you're not using Code Co-op for anything else, you may remove the Dispatcher from your Startup group.

If you purchase a license, or already have one…

Code Co-op will ask you if you want to change your status in the Tutorial project to that of a "voting member." The answer is no. Code Co-op will then send a license update script to us, but we'll disregard it.

Defecting from the Tutorial Project

If you're no longer interested in updates to our tutorials, select Defect from the Project menu. A defect script will be sent to us. If you don't have automatic emailing, just delete the script and send a note to tutorial@relisoft.com with Defect in the subject line. We'll remove you from our subscription list.

Примечания

1

It really cracked me up when I read the introduction to the chapter on Aggregation in the otherwise fine (although somehow dorky) book "Inside COM" by Dale Rogerson. If he knew the real story, he wouldn't be so adamant in his defense of aggregation.

Content

Windows API Tutorials

Windows API Tutorials

The Simplest Windows Program

The Generic Windows Program

Windows Controls

Program with a Dialog Box as the Main Window

Dialog Box

Canvas, or Windows Device Context

Drawing with Pens and Painting with Brushes

Using Threads

When Folders Change

Using Windows95 Shell and COM — A. K. A. OLE

What's Wrong with OLE? The Insider's story

Rationalizing OLE Building smart OLE on top of, you know, the other ole.

OLE Automation

Splitter Bar

Bitmaps

Direct Draw

Joining the Tutorial Project

Примечания