A brief history of WTL


For years, the Microsoft Foundation Classes (MFC) framework was the only choice for most Windows developers.
Then Microsoft released the ActiveX Template Library (ATL) in order to make the development of COM/ActiveX objects easier as ATL provides access to COM features not available in MFC. ATL, now the Active Template Library, also includes GUI classes (lightweight wrappers around the Win32 API) but not enough to cover all UI needs.
Since ATL 3.0, Microsoft have made available the Windows Template Library (WTL), a high performance, template-based application framework built on top of both ATL and the Win32 API.

WTL relies on the ATL windowing services and extends it, boasting a comprehensive set of wrappers for all the standard and common window controls, the system dialogs, and all the GDI objects.

Although WTL is not officially supported by Microsoft (rumor has it it is perceived internally as a threat to MFC and .NET), it went through two major revisions as a Microsoft-owned project and is now an open-source (CPL) project led by Nenad Stefanovic (chief engineer on WTL at Microsoft).

Anatomy of a basic WTL application


Although its use is not mandatory, the ATL/WTL Application Wizard provides developers with a handy shortcut by generating the application's GUI skeleton, albeit not necessarily in the most optimal fashion. The wizard will create by default the following files for an SDI application:
  • MyProject.cpp: the main source file for the project;
  • MainFrm.h: the source file for the main frame;
  • MyProjectView.h: the source file for the view (i.e., a child window for the client area of the main frame);
  • AboutDlg.h: the source file for the About dialog;
  • stdafx.h, stdafx.cpp: the header file that contains the standard includes (e.g., the WTL header files) and the source file that compiles it;
  • MyProject.rc, resource.h, toolbar.bmp, MyProject.ico: the resource script and various resource files it references;
  • MyProject.manifest: the manifest file embedded into the executable to support the XP Themes.
The first three files are the critical ones and are worth a closer inspection.

MyProject.cpp
int WINAPI _tWinMain(
        HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow)
is the starting point of the application. This function performs the necessary initialisations:
// initialise the COM library on the current thread
// call not required if COM is not to be used!
::CoInitialize(NULL);
// register specific control classes from the common control DLL
AtlInitCommonControls(ICC_COOL_CLASSES | ICC_BAR_CLASSES);
// initialise the COM server module
_Module.Init(NULL, hInstance);
before calling:
int Run(LPTSTR /*lpstrCmdLine*/ = NULL, int nCmdShow = SW_SHOWDEFAULT)

Run creates the main frame
// instantiate a main frame object
CMainFrame wndMain;
wndMain.CreateEx();
and sets up the message loop
// instantiate a message loop object and attach it to the module
CMessageLoop theLoop;
_Module.AddMessageLoop(&theLoop);
...
// start the message loop
theLoop.Run();

Eventually and at the user's request, the application will terminate after releasing its system resources.
// release the message loop
_Module.RemoveMessageLoop();
...
// back in _tWinMain; release the module's data members
_Module.Term();
// close the COM library, unload all DLLs, and free all resources maintained by the thread
::CoUninitialize();

MainFrm.h
This is the place where the code for the main frame is located. Please note the client area is not managed here but everything else is.

The frame inherits its behaviour from several key classes:
class CMainFrame :
        // implement a frame
        public CFrameWindowImpl<CMainFrame>,
        // support a UI update map
        public CUpdateUI<CMainFrame>,
        // prefilter the frame messages
        public CMessageFilter,
        // perform idle-time processing
        public CIdleHandler

The window is then defined and its resource ID provided. WTL also parses the resource file to find a string (for the frame title), a menu, an accelerator, and/or an icon with the same ID.
DECLARE_FRAME_WND_CLASS(NULL, IDR_MAINFRAME)
The message filtering and idle-time processing methods follow:
// as required by CMessageFilter...
virtual BOOL PreTranslateMessage(MSG* pMsg)
{
        if(CFrameWindowImpl<CMainFrame>::PreTranslateMessage(pMsg))
                return TRUE;
 
        // delegate to the view's own message filtering
        return m_view.PreTranslateMessage(pMsg);
}
 
// as required by CIdleHandler...
virtual BOOL OnIdle()
{
        UIUpdateToolBar();
        return FALSE;
}
before the message maps:
// UI update handlers for the View|Toolbar and View|Status Bar menu items
BEGIN_UPDATE_UI_MAP(CMainFrame)
        UPDATE_ELEMENT(ID_VIEW_TOOLBAR, UPDUI_MENUPOPUP)
        UPDATE_ELEMENT(ID_VIEW_STATUS_BAR, UPDUI_MENUPOPUP)
END_UPDATE_UI_MAP()
 
// default message map
BEGIN_MSG_MAP(CMainFrame)
        // frame creation handler
        MESSAGE_HANDLER(WM_CREATE, OnCreate)
        // File|Exit menu item handler
        COMMAND_ID_HANDLER(ID_APP_EXIT, OnFileExit)
        // File|New menu item handler
        COMMAND_ID_HANDLER(ID_FILE_NEW, OnFileNew)
        // View|Toolbar menu item handler
        COMMAND_ID_HANDLER(ID_VIEW_TOOLBAR, OnViewToolBar)
        // View|Status Bar menu item handler
        COMMAND_ID_HANDLER(ID_VIEW_STATUS_BAR, OnViewStatusBar)
        // Help|About MyProject menu item handler
        COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
        // chain to base classes' default message maps
        CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
        CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
END_MSG_MAP()

The remaining of the file contains the code for the handlers listed above.

MyProjectView.h
This file handles the content of the frame, its client area. The code should present no problems as it is reminiscent of the frame's.
class CMyProjectView : public CWindowImpl<CMyProjectView>
{
public:
        DECLARE_WND_CLASS(NULL)
 
        BOOL PreTranslateMessage(MSG* pMsg)
        {
                pMsg;
                return FALSE;
        }
 
        // default message map
        BEGIN_MSG_MAP(CMyProjectView)
                // map WM_PAINT to its handler
                MESSAGE_HANDLER(WM_PAINT, OnPaint)
        END_MSG_MAP()
In oder to actually display anything on the view, the WM_PAINT message handler can be used.
        // WM_PAINT message handler
        LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
        {
                CPaintDC dc(m_hWnd);
 
                //TODO: Add your drawing code here, e.g.:
                //dc.TextOut(0,0, TEXT("HelloWorld"));
 
                return 0;
        }
};

AboutDlg.h
This file contains the source code for the About dialog.

Instead of CFrameWindowImpl (CMainFrame) or CWindowImpl (CMyProjectView), CDialogImpl is used as the base class.
class CAboutDlg : public CDialogImpl<CAboutDlg>
{
public:
        // define the associated resource ID
        enum { IDD = IDD_ABOUTBOX };
 
        // default message map
        BEGIN_MSG_MAP(CAboutDlg)
                // dialog creation handler
                MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
                // OK button handler
                COMMAND_ID_HANDLER(IDOK, OnCloseCmd)
                // Cancel button handler
                COMMAND_ID_HANDLER(IDCANCEL, OnCloseCmd)
        END_MSG_MAP()
 
        // dialog creation handler
        LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
        {
                CenterWindow(GetParent());
                return TRUE;
        }
 
        // OK and Cancel buttons handler
        LRESULT OnCloseCmd(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
        {
                EndDialog(wID);
                return 0;
        }
};