天天看點

WTL Makes UI Programming a Joy--中文系列第一章 ATL, the Foundation of WTL

WTL Makes UI Programming a Joy--中文系列第一章 ATL, the Foundation of WTL

WTL Makes UI Programming a Joy,

Part 1: The Basics

第一章 ATL, the Foundation of WTL

The Windows Template Library (WTL) 随着 2000 年 1 月的 Platform SDK 一起釋出 , 作為一個 SDK 的示例代碼 , 由 ATL ( 活動模闆庫 ) 小組成員編寫而成 , 它僅僅是基于 ATL 并封裝了視窗部分的 Win32 API. 此後 , ATL 2.0 包含了一些簡單的視窗封裝類 , 比如 CWindow, CWindowImpl 和 CDialogImpl. 然而 , 與 MFC 比起來 , ATL 已有的視窗類根本不能當回事 . 即使是到了 ATL3.0 也沒有提供對一些常用 UI 屬性的支援 , 如 MDI, 指令條 , DDX, 列印 , GDI, 甚至是連 MFC 中最受歡迎的 CString 類都沒有 . 如果沒有上述這些易用特性 , 那麼 WTL 終究不能令大多數 MFC 程式員滿意 . 是以 , WTL 就是 ATL 小組成員開發出來的一個視窗架構, 他們認為一個完美的視窗架構就應該是這個樣子的 . Table 1 展示了 WTL 與 MFC 相關特性的比較 .

Feature MFC WTL
Stand-alone library Yes No (built on ATL)
AppWizard support Yes Yes
ClassWizard support Yes No
Officially supported by Microsoft Yes No (Supported by volunteers inside MS)
Support for OLE Documents Yes No
Support for Views Yes Yes
Support for Documents Yes No
Basic Win32 & Common Control Wrappers Yes Yes
Advanced Common Control Wrappers (Flat scrollbar, IP Address, Pager Control, etc.) No Yes
Command Bar support (including bitmapped context menus) No (MFC does provide dialog bars) Yes
CString Yes Yes
GDI wrappers Yes Yes
Helper classes (CRect, Cpoint, etc.) Yes Yes
Property Sheets/Wizards Yes Yes
SDI, MDI support Yes Yes
Multi-SDI support No Yes
MRU Support Yes Yes
Docking Windows/Bars Yes No
Splitters Yes Yes
DDX Yes Yes (not as extensive as MFC)
Printing/Print Preview Yes Yes
Scrollable Views Yes Yes
Custom Draw/Owner Draw Wrapper No Yes
Feature MFC WTL
Message/Command Routing Yes Yes
Common Dialogs Yes Yes
HTML Views Yes Yes
Single Instance Applications No No
UI Updating Yes Yes
Template-based No Yes
Size of a statically linked do-nothing SDI application with toolbar, status bar, and menu

228KB +

MSVCRT.DLL (288KB)

24k (with /OPT:NOWIN98)

(+ MSVCRT.DLL if you use CString)

Size of a dynamically linked do-nothing SDI application with toolbar, status bar, and menu

24KB +

MFC42.DLL (972KB) +

MSVCRT.DLL (288KB)

N/A
Runtime Dependencies CRT (+ MFC42.DLL, if dynamically linked) None (CRT if you use CString)

起初是為了支援的 COM 控件及 OLE 屬性頁的一套 ATL 視窗類 , 形成了 WTL 的架構視窗基礎 . ATL 提供了所有的最基本視窗應用 , 包括視窗 / 對話框建立和管理 , 視窗過程處理 , 消息路由 , 視窗子類化 / 超類化 , 消息鍊 . Figure1 給出了 ATL 視窗類層次架構 .

Figure 1 展示了 ATL 視窗類結構.

                Figure 1: ATL Windowing-class Hierarchy

WTL Makes UI Programming a Joy--中文系列第一章 ATL, the Foundation of WTL

為了建立直接基于 ATL 的主視窗或對話框視窗 , 需要分别從 CWindowImpl 或 CDialogImpl 派生自己的視窗類 . Figure2 是一個 SDI 應用程式 , 向你展示了如何利用 ATL 的視窗應用支援來建立主視窗和對話框 .

                Figure 2: Simple SDI Application

WTL Makes UI Programming a Joy--中文系列第一章 ATL, the Foundation of WTL

如圖所示 , 此 SDI 視窗程式包括視窗架構 , 菜單條 , 狀态條和客戶區 . 它提供一個關于對話框 Aboutbox 示範如何利用 ATL 來建立對話框 . 在 VC6 中建立一個 Win32 Application 工程 , 并添加對 ATL 視窗應用的支援 .

  ( 譯者注:原作以 WTL7.1 講解 , 現在 WTL8.0 是最新版本,下載下傳連結:http://sourceforge.net/projects/wtl/ 支援 Vista 特性和 API, 原生支援 VS2005, 也可以在 VS2008 中使用 , 本系列中所提及的 VC6, 均可以 VS2005 中的 VC8 或者 VS2008 的 VC9 來代替 , 相應的設定及界面或許有些許不同 , 當然 , 建議放棄使用 VC6, 因為其對标準 C++ 的支援不是很好 , 以後對此不再提及 )

(譯者再注:http://sourceforge.net/projects/wtl/ 點選“View all files” 你将看到的最新版本是WTL81, 版本号WTL8.1.9127 , 05/07/2009釋出,支援VS2008)

Figure3 給出了源代碼相關檔案 .

                Figure 3: Main Source Files

//---------------------------------------------------------------------

// stdafx.h :

#if !defined(AFX_STDAFX_H__DE06DA2F_25B6_41BA_9B20_A17362C38C8C__INCLUDED_)

#define AFX_STDAFX_H__DE06DA2F_25B6_41BA_9B20_A17362C38C8C__INCLUDED_

#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000

#define STRICT

#ifndef _WIN32_WINNT

#define _WIN32_WINNT 0x0400

#endif

#define _ATL_APARTMENT_THREADED

#include <atlbase.h>

//You may derive a class from CComModule and use it if you want to override

//something, but do not change the name of _Module

extern CComModule _Module;

#include <atlwin.h>

//{{AFX_INSERT_LOCATION}}

// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_STDAFX_H__DE06DA2F_25B6_41BA_9B20_A17362C38C8C__INCLUDED)

//---------------------------------------------------------------------

//MainFrame.H

#pragma once

#ifndef _MAINFRAME_H_

#define _MAINFRAME_H_

#include "commctrl.H"

class CMainFrame : public CWindowImpl<CMainFrame,CWindow, CWinTraits<WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN , WS_EX_APPWINDOW | WS_EX_WINDOWEDGE> >

{

public:

    CMainFrame():m_hWndStatusBar(NULL),m_hBmp(NULL) {

        m_hBmp =

LoadBitmap(_Module.GetResourceInstance(),

MAKEINTRESOURCE(IDB_ATLWINDOWING));

    }

    ~CMainFrame() {

        if(m_hBmp) {

            ::DeleteObject(m_hBmp);

            m_hBmp = NULL;

        }

    }

    BEGIN_MSG_MAP(CMainFrame)

        MESSAGE_HANDLER(WM_PAINT, OnPaint)

        MESSAGE_HANDLER(WM_CREATE, OnCreate)

        MESSAGE_HANDLER(WM_SIZE, OnSize)

        MESSAGE_HANDLER(WM_CLOSE, OnClose)

        COMMAND_ID_HANDLER(ID_FILE_EXIT, OnFileExit)

        COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAbout)

    END_MSG_MAP()

    void OnFinalMessage(HWND ) {}

    LRESULT OnCreate(UINT, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {

        DefWindowProc();

        m_hWndStatusBar = ::CreateStatusWindow(WS_CHILD | WS_VISIBLE |

WS_CLIPCHILDREN | WS_CLIPSIBLINGS | SBARS_SIZEGRIP,

_T("Ready"), m_hWnd, 1);

        return 0L;

    }

    LRESULT OnSize(UINT, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {

        DefWindowProc();

        ::SendMessage(m_hWndStatusBar, WM_SIZE, 0, 0);

        return 0L;

    }

    LRESULT OnPaint(UINT, WPARAM, LPARAM, BOOL&) {

           PAINTSTRUCT ps;

          HDC         hdc = BeginPaint(&ps);

          RECT    rect; GetClientRect(&rect);

        //Bitmap

        HDC hDCMem =  ::CreateCompatibleDC(hdc);

        HBITMAP hBmpOld = (HBITMAP)::SelectObject(hDCMem,m_hBmp);

        BITMAP bmp;

        ::GetObject(m_hBmp, sizeof(BITMAP), &bmp);

        SIZE size = { bmp.bmWidth,bmp.bmHeight };

        ::BitBlt(hdc, rect.left, rect.top, (size.cx), (size.cy), hDCMem,

0,0,SRCCOPY);

        //cleanup

        ::SelectObject(hDCMem,hBmpOld);

        ::DeleteDC(hDCMem);

        hDCMem = NULL;

          EndPaint(&ps);

          return 0;

}

    LRESULT OnClose(UINT,WPARAM,LPARAM, BOOL&) {

        DestroyWindow();

        PostQuitMessage(0);

        return 0L;

    }  

    LRESULT OnFileExit(WORD,WORD wID, HWND,BOOL&) {

        SendMessage(WM_CLOSE);

        return 0L;

    }

    LRESULT OnAbout(WORD, WORD wID, HWND, BOOL&) {

        CAboutDialog dlg;

        dlg.DoModal();

        return 0L;

    }

private:

    struct CAboutDialog : public CDialogImpl<CAboutDialog> {

        enum { IDD = IDD_ABOUT };

        BEGIN_MSG_MAP(CAboutDialog)

            COMMAND_ID_HANDLER(IDOK, OnClose)

        END_MSG_MAP()

        LRESULT OnClose(WORD, WORD wID, HWND, BOOL&) {

            EndDialog(wID);

            return 0L;

        }  

    };

    HWND        m_hWndStatusBar;

    HBITMAP     m_hBmp;

};

#endif

//---------------------------------------------------------------------

//AtlHelloWindowing.Cpp

#include "stdafx.h"

#include "resource.h"

#include "MainFrame.H"

CComModule _Module;

extern "C" int WINAPI

_tWinMain(HINSTANCE hInstance,HINSTANCE, LPTSTR lpCmdLine, int nShowCmd) {

lpCmdLine = GetCommandLine(); //this line necessary for _ATL_MIN_CRT

_Module.Init(0, hInstance,NULL);

//Load Common Controls

    INITCOMMONCONTROLSEX iccx;

    iccx.dwSize = sizeof(iccx);

    iccx.dwICC =  ICC_WIN95_CLASSES  |ICC_BAR_CLASSES ; 

    ::InitCommonControlsEx(&iccx);

    HMENU hMenu = LoadMenu(_Module.GetResourceInstance(),

MAKEINTRESOURCE(IDR_MAINFRAME));

    CMainFrame wndFrame;

    wndFrame.GetWndClassInfo().m_wc.hIcon = ::LoadIcon(_Module.GetResourceInstance(),

MAKEINTRESOURCE(IDI_FORM));

    wndFrame.GetWndClassInfo().m_wc.style = 0;//CS_HREDRAW|CS_VREDRAW;

    wndFrame.Create(GetDesktopWindow(), CWindow::rcDefault,

_T("ATL Windowing is cool"), 0, 0, (UINT)hMenu);

    wndFrame.ShowWindow(nShowCmd);

    wndFrame.UpdateWindow();

    MSG msg;

while (GetMessage(&msg, 0, 0, 0)) { 

        TranslateMessage(&msg);

          DispatchMessage(&msg);

    }

_Module.Term();

    return 0;

}

在 MainFrame.h 中 , CMainFrame 有兩個資料成員 , 狀态條視窗句柄和一個位圖句柄 , 在 WM_CREATE 消息映射函數中 CMainFrame 通過呼叫 API 函數 ( 位于 comctl32.dll 中 )CrateStatusWindow() 建立狀态條視窗 , 并在 WM_SIZE 消息映射函數中調整狀态條尺寸 . 在 WM_PAINT 消息映射函數中顯示一個 GDI 位圖 . ATL 并不提供對繪圖操作的支援 , 是以所有繪制操作都是調原生 Win32 API.

MESSAGE_HANDLER 宏要求你自己解密消息參數 ( 大部分消息映射函數中的消息參數 LPARAM 和 WPARAM 具體内容得自己根據 SDK 查閱 ), ATL 通過一系列宏對 WM_COMMAND 和 WM_NOTIFY 進行了解密。 MainFrame.h 中通過 COMMAND_ID_HANDLER 來映射菜單指令 .

最後, CAboutDialog 是繼承自 CDialogImpl 的對話框 , 在 OnAbout 消息映射函數中通過函數 DoModal () 啟動 CAboutDialog .

HelloAtlWindowing.cpp 中 , ATL 應用程式的入口點函數 _tWinMain (), 在 _tWinMain () 中建立了一個 CMainFrame 類的執行個體 , 并調用其 Create (), ShowWindow () 和 UpdateWindow () 成員函數 , 這些成員函數是對 Win32 API 函數 CreateWindow (), ShowWindow () 和 UpdateWindow () 的包裝 . 最後 , 因為這是一個視窗程式 , 是以程式通過标準的 GetMessage-DispatchMessage () 啟動消息循環處理 . 如果你熟悉 Win32 程式設計 , 那麼你會發現這裡缺少 RegisterClass () 和視窗過程函數 WndProc (), 但是你會發現其它一些十分漂亮的東西 . 如果你熟悉 MFC , 你會十分驚訝 , ATL 并不提供諸如自動支援建立狀态條 , 菜單 ( 在此先不提及工具條 ) 以及啟動消息循環的細節 . 很明顯 , ATL 很 有用但同時它在這方面走的并不遠 ( 對視窗架構的處理涉及不多 ), 别忘了 , ATL 牢牢紮根于 COM . 對視窗架構的支援 隻是 ATL 的一個副産品而不是它存在的最主要的目的 . ATL 并不支援 MDI , Multi-SDI , Explorer-style 應用程式 , 也不支援通用控件 , DDX , 以及 GDI , 正因為如此 , WTL 出現了 .

          The WTL Way

To be continued...

繼續閱讀