README.TXT

This is the stuff I want you to read first, before proceeding on or posting messages to this article's discussion board.

This series of articles originally covered WTL 7.0 and written for VC 6 users. Now that VC 8 is out, I felt it was about time to update the articles to cover VC 7.1. ;) (Also, the automatic 6-to-7 conversion done by VC 7.1 doesn't always go smoothly so VC 7.1 users could get stuck when trying to use the demo source code.) So as I go through and update this series, the articles will be updated to reflect new WTL 7.1 features, and I'll have VC 7.1 projects in the source downloads.

Important note for VC 2005 users: The Express edition of VC 2005 does not come with ATL (or MFC for that matter) so you can't build ATL or WTL projects with the Express version.

If you are using VC 6, then you need the Platform SDK. You can't use WTL without it. You can use the web install version, or download the CAB files or an ISO image and run the setup locally. Be sure you use the utility to add the SDK include and lib directories to the VC search path. You can find this in the Visual Studio Registration folder in the Platform SDK program group. It's a good idea to get the latest Platform SDK even if you're using VC 7 so you have the latest headers and libs.

You need WTL. Download version 7.1 from Microsoft. See the articles "Introduction to WTL - Part 1" and "Easy installation of WTL" for some tips on installing the files. Those articles are rather out-of-date now, but still contain some good info. The WTL distribution also has a readme file with installation instructions. One thing which I don't think is mentioned in those articles is how to add the WTL files to the VC include path. In VC 6, click Tools|Options and go to the Directories tab. In the Show directories for combo box, select Include files. Then add a new entry that points to the directory where you put the WTL header files. In VC 7, click Tools|Options, click Projects then VC++ Directories. In the Show directories for combo box, select Include files. Then add a new entry that points to the directory where you put the WTL header files.

Important: While we're on the subject of the VC 7 include path, you must make a change to the default directory list if you haven't updated your Platform SDK. Make sure that $(VCInstallDir)PlatformSDK\include is first in the list, above ($VCInstallDir)include, as shown here:

You need to know MFC, and know it well enough that you understand what's behind the message map macros, and can edit the code marked "DO NOT EDIT" with no problems.

You need to know Win32 API programming, and know it well. If you learned Windows programming by going straight into MFC, without learning how messages work at the API level, you are unfortunately going to have trouble in WTL. If you don't know what a message's WPARAM and LPARAM mean, you should read other articles on API-level programming (there are lots of them here at CodeProject) so you understand.

You need to know C++ template syntax. See the VC Forum FAQ for links to C++ FAQs and template FAQs.

Since I haven't used VC 8 yet, I don't know if the sample code will compile on 8. Hopefully the 7-to-8 upgrade process will work better than the 6-to-7 process did. Please post on this article's forum if you have any trouble with VC 8.

Introduction to the Series

WTL rocks. It does. It has a lot of the power of MFC's GUI classes, yet produces substantially smaller executables. If you're like me, and learned GUI programming with MFC, you've become quite comfortable with all the control wrappers MFC provides, as well as with the flexible message handling built into MFC. If you're also like me and don't relish the idea of several hundred K of MFC framework getting added to your programs, WTL is for you. However, there are a few barriers we have to overcome:

No documentation in MSDN, you need to find it somewhere else, or even look at the WTL source.

No reference books you can buy and keep on your shelf.

It has that "not officially supported by Microsoft" stigma

ATL/WTL windowing is sufficiently different from MFC windowing that not all your knowledge transfers over.

On the other hand, the benefits of WTL are:

No complex doc/view framework to learn or work around.

Has some essential UI features from MFC, like DDX/DDV and "update command UI" functionality.

Actually improves on some MFC features (e.g., more flexible splitter windows)

Much smaller executables compared to a statically-linked MFC app.

You can apply bug fixes to WTL yourself and not affect existing apps (compare this with replacing the MFC/CRT DLLs to fix a bug in one app, and having other apps break)

If you still need MFC, MFC and ATL/WTL windows can co-exist peacefully (for a prototype at work, I ended up creating an MFC CFrameWnd that contained a WTL CSplitterWindow, which contained MFC CDialogs -- that wasn't me showing off, it was modifying existing MFC code but using the nicer WTL splitter).

In this series, I will first introduce the ATL windowing classes. WTL is, after all, a set of add-on classes to ATL, so a good understanding of ATL windowing is essential. Once I've covered that, I'll introduce WTL features and show how they make GUI programming a lot easier.

Introduction to Part I

WTL rocks. But before we get to why, we need to cover ATL first. WTL is a set of add-on classes to ATL, and if you've been strictly an MFC programmer in the past, you may never have encountered the ATL GUI classes. So please bear with me if I don't get to WTL right off the bat; the detour into ATL is necessary.

In this first part, I will give a little background on ATL, cover some essential points you need to know before writing ATL code, quickly explain those funky ATL templates, and cover the basic ATL windowing classes.

ATL Background

ATL and WTL history

The Active Template Library... kind of an odd name, isn't it? Old-timers might remember that it was originally the ActiveX Template Library, which is a more accurate name, since ATL's goal is to make writing COM objects and ActiveX controls easier. (ATL was also developed during the time that Microsoft was naming new products ActiveX-something, just as new products nowadays are called something.NET.) Since ATL is really about writing COM objects, it only has the most basic of GUI classes, the equivalent of MFC's CWnd and CDialog. Fortunately, the GUI classes are flexible enough to allow for something like WTL to be built on top of them.

WTL had two major revisions as a Microsoft-owned project, numbered 3 and 7. (The version numbers were chosen to match the ATL version numbers, that's why they're not 1 and 2.) Version 3.1 is obsolete now so it will not be covered in this series. Version 7.0 was a major update to version 3, and version 7.1 added some bug fixes and minor features.

Following version 7.1, Microsoft made WTL an open-source project, hosted at Sourceforge. The latest release from that site is version 7.5. I have not looked at 7.5 yet, so this series will not cover 7.5 at this time. (I'm already behind by two versions! I'll catch up eventually.)

ATL-style templates

Even if you can read C++ templates without getting a headache, there are two things ATL does that might trip you up at first. Take this class for example:

class CMyWnd : public CWindowImpl<CMyWnd>
{
...
};

That actually is legal, because the C++ spec says that immediately after the class CMyWnd part, the name CMyWnd is defined and can be used in the inheritance list. The reason for having the class name as a template parameter is so ATL can do the second tricky thing, compile-time virtual function calls.

The static_cast<T*>(this) is the trick here. It casts this, which is of type B1*, to either D1* or D2* depending on which specialization is being invoked. Because template code is generated at compile-time, this cast is guaranteed to be safe, as long as the inheritance list is written correctly. (If you wrote class D3 : public B1<D2> you'd be in trouble.) It's safe because the this object can only be of type D1* or D2* (as appropriate), and nothing else. Notice that this is almost exactly like normal C++ polymorphism, except that the SayHi() method isn't virtual.

To explain how this works, let's look at each call to SayHi(). In the first call, the specialization B1<D1> is being used, so the SayHi() code expands to:

This time, D2does contain a PrintClassName() method, so that is the one that gets called.

The benefits of this technique are:

It doesn't require using pointers to objects.

It saves memory because there is no need for vtbls.

It's impossible to call a virtual function through a null pointer at runtime because of an uninitialized vtbl.

All function calls are resolved at compile time, so they can be optimized.

While the saving of a vtbl doesn't seem significant in this example (it would only be 4 bytes), think of the case where there are 15 base classes, some of those containing 20 methods, and the savings adds up.

ATL Windowing Classes

OK, enough background! Time to dive into ATL. ATL is designed with a strict interface/implementation division, and that's evident in the windowing classes. This is similar to COM, where interface definitions are completely separate from an implementation (or possibly several implementations).

ATL has one class that defines the "interface" for a window, that is, what can be done with a window. This class is called CWindow. It is nothing more than a wrapper around an HWND, and it provides wrappers for almost all of the User32 APIs that take an HWND as the first parameter, such as SetWindowText() and DestroyWindow(). CWindow has a public member m_hWnd that you can access if you need the raw HWND. CWindow also has a operator HWND method, so you can pass a CWindow object to a function that takes an HWND. There is no equivalent to CWnd::GetSafeHwnd().

CWindow is very different from MFC's CWnd. CWindow objects are inexpensive to create, since there is only one data member, and there is no equivalent to the object maps that MFC keeps internally to map HWNDs to CWnd objects. Also unlike CWnd, when a CWindow object goes out of scope, the associated window is not destroyed. This means you don't have to remember to detach any temp CWindow objects you might create.

The ATL class that has the implementation of a window is CWindowImpl. CWindowImpl contains the code for such things as window class registration, window subclassing, message maps, and a basic WindowProc(). This is unlike MFC where everything is in one class, CWnd.

There are also two separate classes that contain the implementation of a dialog box, CDialogImpl and CAxDialogImpl. CDialogImpl is used for plain dialogs, while CAxDialogImpl is used for dialogs that host ActiveX controls.

Defining a Window Implementation

Any non-dialog window you create will derive from CWindowImpl. Your new class needs to contain three things:

A window class definition

A message map

The default styles to use for the window, called the window traits

The window class definition is done using the DECLARE_WND_CLASS or DECLARE_WND_CLASS_EX macro. Both of these define an ATL struct CWndClassInfo that wraps the WNDCLASSEX struct. DECLARE_WND_CLASS lets you specify the new window class name and uses default values for the other members, while DECLARE_WND_CLASS_EX lets you also specify a class style and window background color. You can also use NULL for the class name, and ATL will generate a name for you.

Let's start out a new class definition, and I'll keep adding to it as we go through this section.

Next comes the message map. ATL message maps are much simpler than MFC maps. An ATL map expands into a big switch statement; the switch looks for the right handler and calls the corresponding function. The macros for the message map are BEGIN_MSG_MAP and END_MSG_MAP. Let's add an empty map to our window.

I'll cover how to add handlers to the map in the next section. Finally, we need to define the window traits for our class. Window traits are a combination of window styles and extended window styles that are used when creating the window. The styles are specified as template parameters so the caller doesn't have to be bothered with getting the styles right when it creates our window. Here's a sample traits definition using the ATL class CWinTraits:

The caller can override the styles in the CMyWindowTraits definition, but generally this is not necessary. ATL also has a few predefined CWinTraits specializations, one of which is perfect for top-level windows like ours, CFrameWinTraits:

Filling in the message map

The ATL message map is one area that is lacking in developer-friendliness, and something that WTL greatly improves on. ClassView does at least give you the ability to add message handers, however ATL doesn't have message-specific macros and automatic parameter unpacking like MFC does. In ATL, there are just three types of message handlers, one for WM_NOTIFY, one for WM_COMMAND, and one for everything else. Let's start by adding handlers for WM_CLOSE and WM_DESTROY to our window.

You'll notice that the handlers get the raw WPARAM and LPARAM values; you have to unpack them yourself when a message uses those parameters. There is also a fourth parameter, bHandled. This parameter is set to TRUE by ATL before the handler is called. If you want ATL's default WindowProc() to handle the message as well after your handler returns, you set bHandled to FALSE. This is different than MFC, where you have to explicitly call the base-class implementation of a message handler.

Let's add a WM_COMMAND handler as well. Assuming our window's menu has an About item with ID IDC_ABOUT:

Notice that the COMMAND_HANDLER macro does unpack the message parameters for you. The NOTIFY_HANDLER macro is similar, and unpacks the WM_NOTIFY message parameters.

Advanced Message Maps and Mix-in Classes

One major difference in ATL is that any C++ class can handle messages, unlike MFC where message-handling duties are split between CWnd and CCmdTarget, plus several classes that have a PreTranslateMessage() method. This ability lets us write what are commonly called "mix-in" classes, so that we can add features to our window simply by adding classes to the inheritance list.

A base class with a message map is usually a template that takes the derived class name as a template parameter, so it can access members of the derived class like m_hWnd (the HWND member in CWindow). Let's look at a mix-in class that paints the window background by handling WM_ERASEBKGND.

Let's go through this new class. First, CPaintBkgnd has two template parameters: the name of the derived class that is using CPaintBkgnd, and a color to use for the background. (The t_ prefix is often used for template parameters that are plain values.)

The constructor and destructor are pretty simple, they create and destroy a brush of the color passed as t_crBrushColor. Next comes the message map, which handles WM_ERASEBKGND. Finally, there's the OnEraseBkgnd() handler which fills in the window with the brush created in the constructor. There are two things of note going on in OnEraseBkgnd(). First, it uses the derived class's window functions (namely GetClientRect()). How do we know that there even is a GetClientRect() in the derived class? The code wouldn't compile if there weren't! The compiler checks that the derived class T contains the methods that we call through the pT variable. Second, OnEraseBkgnd() has to unpack the device context from wParam.

To use this mix-in class with our window, we do two things. First, we add it to the inheritance list:

Any messages that reach the CHAIN_MSG_MAP line without being handled are passed on to the map in CPaintBkgnd. Note that WM_CLOSE, WM_DESTROY, and IDC_ABOUT will not be chained, because once those are handled, the message map search ends. The typedef is necessary because CHAIN_MSG_MAP is a preprocessor macro that takes one parameter; if we wrote CPaintBkgnd<CMyWindow, RGB(0,0,255)> as the parameter, the commas would make the preprocessor think that we're calling it with more than one parameter.

You could conceivably have several mix-in classes in the inheritance list, each with a CHAIN_MSG_MAP macro so that messages are passed to it. This is different from MFC, where each CWnd-derived class can have only one base class, and MFC automatically passes unhandled messages to the base class.

Structure of an ATL EXE

Now that we have a complete (if not entirely useful) main window, let's see how to use it in a program. ATL EXEs have one or more global variables that roughly correspond to the global CWinApp variable in an MFC program (normally called theApp). This area of ATL changed radically between VC6 and VC7, so I will cover the two versions separately.

In VC 6

An ATL executable contains a global CComModule variable that must be called _Module. Here's how we start our stdafx.h:

The first parameter to Init() is only used in COM servers. Since our EXE is not a server, we can pass NULL. ATL doesn't provide its own WinMain() or message pump like MFC, so to get our program running, we create a CMyWindow object and add a message pump.

The only unusual thing in the above code is CWindow::rcDefault, which is a RECT member of CWindow. Using that as the window's initial RECT is like using CW_USEDEFAULT for the width and height with the CreateWindow() API.

Under the hood, ATL uses some assembly-language black magic to connect the main window's handle to its corresponding CMyWindow object. The upside of this is that there is no problem passing CWindow objects between threads, something that fails miserably with CWnds in MFC.

In VC 7

ATL 7 split up the module-management code into several classes. CComModule is still present for backwards-compatibility, but code written in VC 6 that is converted by VC 7 doesn't always compile cleanly (if at all) so I'll cover the new class here.

In VC 7, the ATL headers automatically declare global instances of all the module classes, and the Init() and Term() methods are called for you, so those manual steps aren't necessary. Our stdafx.h therefore looks like:

ATL has no built-in handlers for the OK and Cancel buttons, so we need to code them ourselves, along with a WM_CLOSE handler that is called if the user clicks the close button in the title bar. We also need to handle WM_INITDIALOG so that the keyboard focus is set properly when the dialog appears. Here is the complete class definition with message handlers.

I used one handler for both OK and Cancel to demonstrate the wID parameter, which is set to either IDOK or IDCANCEL, depending on which button is clicked.

Showing the dialog is similar to MFC, you create an object of the new class and call DoModal(). Let's go back to our main window and add a menu with an About item that shows our new about dialog. We'll need to add two message handlers, one for WM_CREATE and one for the new menu item IDC_ABOUT.

One small difference in modal dialogs is where you specify the dialog's parent window. In MFC you pass the parent to the CDialog constructor, but in ATL you pass it as the first parameter to DoModal(). If you don't specify a window, as in the code above, ATL uses the result of GetActiveWindow() (which will be our frame window) as the parent.

The LoadMenu() call also demonstrates one of the CComModule methods, GetResourceInstance(). This returns the HINSTANCE of a module that holds resources, similar to AfxGetResourceHandle(). The default behavior is to return the HINSTANCE of the EXE. (There is also CComModule::GetModuleInstance(), which functions like AfxGetInstanceHandle().)

Note that OnCreate() is different between VC6 and 7, which is due to the different module-management classes. GetResourceInstance() is now in CAtlBaseModule, and we call the global _AtlBaseModule object that ATL sets up for us.

Here is our revised main window and the about dialog:

I'll Get to WTL, I Promise!

But it will be in Part 2. Since I'm writing these articles for MFC developers, I felt that it was best to do an intro to ATL first, before diving into WTL. If this has been your first exposure to ATL, now might be a good time to write some simple apps on your own, so you get the hang of message maps and using mix-in classes. You can also experiment with ClassView's support for ATL message maps, as it can add message handlers for you. To get started in VC 6, right-click the CMyWindow item and pick Add Windows Message Handler on the context menu. In VC 7, right-click the CMyWindow item and pick Properties on the context menu. In the properties window, click the Messages button on the toolbar to see a list of window messages. You can add a handler for a message by going to its row, clicking the right column to turn it into a combo box, clicking the combo box arrow, then clicking the <Add> item in the drop-down list.

In Part II, I will cover the base WTL windowing classes, the WTL AppWizard, and the much nicer message map macros.

Copyright and license

This article is copyrighted material, (c)2003-2005 by Michael Dunn. I realize this isn't going to stop people from copying it all around the 'net, but I have to say it anyway. If you are interested in doing a translation of this article, please email me to let me know. I don't foresee denying anyone permission to do a translation, I would just like to be aware of the translation so I can post a link to it here.

The demo code that accompanies this article is released to the public domain. I release it this way so that the code can benefit everyone. (I don't make the article itself public domain because having the article available only on CodeProject helps both my own visibility and the CodeProject site.) If you use the demo code in your own application, an email letting me know would be appreciated (just to satisfy my curiosity about whether folks are benefiting from my code) but is not required. Attribution in your own source code is also appreciated but not required.

Revision History

License

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

Share

About the Author

Michael lives in sunny Mountain View, California. He started programming with an Apple //e in 4th grade, graduated from UCLA with a math degree in 1994, and immediately landed a job as a QA engineer at Symantec, working on the Norton AntiVirus team. He pretty much taught himself Windows and MFC programming, and in 1999 he designed and coded a new interface for Norton AntiVirus 2000.
Mike has been a a developer at Napster and at his own lil' startup, Zabersoft, a development company he co-founded with offices in Los Angeles and Odense, Denmark. Mike is now a senior engineer at VMware.

He also enjoys his hobbies of playing pinball, bike riding, photography, and Domion on Friday nights (current favorite combo: Village + double Pirate Ship). He would get his own snooker table too if they weren't so darn big! He is also sad that he's forgotten the languages he's studied: French, Mandarin Chinese, and Japanese.

Comments and Discussions

Because the code has a base class trying to access a protected method in a derived class, which isn't allowed. The other way works though (derived accessing a method in the base) by the language rules.

Good article, tempts me to test your knowledge with an uneasy question;)

I once had an interesting task:
A window implemented using WTL had to switch its style from having a shadow to not having one. Basically, I needed to destroy the window, then change one of its CS_xxx styles for the class, and then create the window afresh. The problem with WTL appeared that architecture of TWindowImpl doesn’t allow to change the class style for the window. Class TWindowImpl will always stick with the class style it was created the first time. Very stupid limitation isn’t it? Or maybe there’s something I don’t know, so tell me please, how such switching class styles can be implemented in WTL ;););););)

I look forward to experimenting with ATL/WTL on small projects at work.
Two comments that I would very much appreciate responses:
1) Are there any CE developers out there that have tested the UI performance differences between MFC and WTL/ATL?
2) Are there any Microsoft devs. cruising these WTL forums that can provide insight into the future of the WTL api? (eg. IDE support, etc...)

This is by far the most enjoyable article I have read on CodeProject to date. Thank you.

Mike,
I have worked with C++ templates before, but never with ATL. Excellent job as always. I just got the chance to read the whole article (between studying for a test) and I really enjoyed it. Now off to read Part 2 of your article (after the test). Thanks again for the great article.

I did sent the update of the really obsolete "Easy installation of WTL" to Chris (to reflect the changes caused by your article), just for your information.

Some notes to your article:

The introductory example of B1, D1, and D2 classes should possibly return 0 to avoid the warning. I personally would also write "int main()" instead of "main()". If the lines...

#include <iostream>
usingnamespace std;

were added, it could almost be compiled.

The VC6 complains about "protected" in D2 class.

One question related to your experience. Could you comment on whether

T& self = static_cast<T&>(*this);
self.PrintClassName();

would be better or worse in ATL than the following?

T* pT = static_cast<T*>(this);
pT->PrintClassName();

When introducing message maps, you say that the macros expand to a big switch, which is true, but the cases are related to alternative message maps. It probably should be mentioned that the message macros (used between BEGIN... and END...) expand to if's -- this is visible in Part II. In other words, one should carefully place the messages in the order of their frequency -- if the speed of their processing matters. The if's cannot be automatically optimized. Possibly also the ProcessWindowMessage() could be mentioned here (the case is built inside it).

On CPaintBkgnd class: You could probably emphasize that the reason for showing the class is to show how the WM_ERASEBKGND can be handled explicitly by a programmer. If the different background colour were the only reason, then it is probably easier to use the following:

Thanks for the comments Petr
I write plain "main()" just out of habit from my C days, and it's four fewer letters to type.
As for casting to T* or T&, the end result is the same. The ATL designers chose to use T*, so I follow their lead.
As for the message maps, your points are all correct. I chose not to go into that much detail because I didn't want this article to be like ATL Internals (a great book BTW) where it explains what every macro expands into. The point about putting the message handler macros in order of most to least-often-received is a good one. The handlers themselves aren't cases in a switch, they're if blocks.

--Mike-- THERE IS NO THERE IS NO BUT THERE IS
MAGIC PIXIE DUST BUSINESS GENIE CODE PROJECT

I am using WTL and would like to center dialog box all the time. Looking at CFileDialog source code, I was puzzled why owner is initialized (CFileDialog::DoModal function)
HWND hWndParent = PreModal();
AfxUnhookWindowCreate();
Like this.

Any way at the end the function calls
nResult = ::GetOpenFileName(&m_ofn);
Which is SDK function.
Perhaps you have a clue, how to center CFileDialog every time. It is worth noticing that if you change the location of this dialog box to new position, it remembers the new position, and next time it will open same spot.

As I use WTL alot, I find it very interesing to find that it is slowly but surely becoming "Mainstream" (this also helps when looking for solutions for problems others may have had) , due to the fact that people like yourself write interesting and understandable articles (like this).Bye the bye, another excellent site for WTL/ATL is that from Bjarke Viksoe http://www.viksoe.dk and also the WTL user group is also very good http://groups.yahoo.com/group/wtl/