首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

MFC 程式設計師的 WTL 入門指南 (二):ATL 視窗類別核心機制詳解

     編輯:關於VC++

前置需求與環境準備

  • Platform SDK:必須安裝並設定 VC++ Include/Lib 搜尋路徑 (工具選項 → 目錄)。
  • WTL 7.0:自 Microsoft 網站或 VC 知識庫下載,設定 Include 路徑。
  • MFC 經驗:有助理解訊息映射與框架概念。
  • Win32 API 基礎:熟悉 WPARAM/LPARAM、視窗程序、訊息迴圈。
  • C++ 模板語法:理解樣板類別、靜態多型 (CRTP)。

系列文章整體觀點:WTL vs MFC

MFC 優勢WTL 優勢
成熟完整的 GUI 類別庫無需複雜 Document/View 架構
ClassWizard 強大支援保有 DDX/DDV、命令 UI 自動更新
官方文件與技術支援分隔視窗等部分功能優於 MFC
動態連結執行檔較小靜態連結執行檔極小 (無 MFC DLL 依賴)
穩定性經大規模驗證原始碼可自行修正 Bug 不影響他程式

WTL 與 MFC 視窗可「和平共處」,例如在 MFC CFrameWnd 中嵌入 WTL CSplitterWindow 再包含 MFC CDialog。

ATL 背景知識:從 ActiveX 到視窗封裝

ATL (Active Template Library) 最初為簡化 COM 元件/ActiveX 控件開發而生,僅提供輕量視窗封裝 (CWindow, CWindowImpl, CDialogImpl),WTL 在此基礎上建構完整 UI 框架。

ATL 模板技巧:編譯期多型 (CRTP)

template <class T> class B1 {
    void SayHi() {
        T* pT = static_cast<T*>(this);
        pT->PrintClassName();
    }
    void PrintClassName() { cout << "This is B1"; }
};
class D1 : public B1<D1> {}; // 未覆寫
class D2 : public B1<D2> {
    void PrintClassName() { cout << "This is D2"; }
};
// D1.SayHi() → "This is B1", D2.SayHi() → "This is D2"
  • 無虛函數表,節省記憶體、編譯期綁定、利於優化。
  • static_cast<T*>(this) 為核心關鍵,將基類指標安全轉為衍生類別指標。

ATL 視窗類別核心架構

CWindow:HWND 輕量封裝

  • 封裝幾乎所有以 HWND 為首參數的 API (SetWindowText, DestroyWindow 等)。
  • 公開成員 m_hWnd 與 operator HWND(),方便互操作。
  • 建構/解構極輕量,無物件鏈結,離開作用域不銷毀實際視窗 (不同於 MFC CWnd)。

CWindowImpl:視窗實作核心

  • 包含視窗類別註冊、子類化、訊息映射、WindowProc 等完整實作。
  • 非對話框視窗皆从此衍生,需提供三要素:視窗類別定義、訊息映射鏈、視窗特徵 (CWinTraits)。

對話框實作:CDialogImpl / CAxDialogImpl

  • CDialogImpl:一般對話框。
  • CAxDialogImpl:支援 ActiveX 控件的對話框。

定義視窗實作類別:三要素詳解

1. 視窗類別定義巨集

class CMyWindow : public CWindowImpl<CMyWindow>
{
public:
    DECLARE_WND_CLASS(_T("My Window Class"))
    // 或 DECLARE_WND_CLASS_EX(類別名, 樣式, 背景筆刷)
};

2. 訊息映射鏈

BEGIN_MSG_MAP(CMyWindow)
    MESSAGE_HANDLER(WM_CLOSE, OnClose)
    MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
    COMMAND_ID_HANDLER(IDC_ABOUT, OnAbout)
END_MSG_MAP()

LRESULT OnClose(UINT, WPARAM, LPARAM, BOOL&) { DestroyWindow(); return 0; }
LRESULT OnDestroy(UINT, WPARAM, LPARAM, BOOL&) { PostQuitMessage(0); return 0; }
LRESULT OnAbout(WORD, WORD, HWND, BOOL&) { MessageBox(...); return 0; }
  • 處理函式原型:LRESULT Func(UINT, WPARAM, LPARAM, BOOL& bHandled)。
  • bHandled=TRUE 表示已處理,設 FALSE 則呼叫預設 DefWindowProc。
  • COMMAND_ID_HANDLER/NOTIFY_HANDLER 已自動解碼參數。

3. 視窗特徵

typedef CWinTraits<WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, WS_EX_APPWINDOW> CMyWindowTraits;
class CMyWindow : public CWindowImpl<CMyWindow, CWindow, CMyWindowTraits> { ... }

預設特徵 CFrameWinTraits:WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS + WS_EX_APPWINDOW | WS_EX_WINDOWEDGE。

進階訊息映射:鏈結與嵌入類別

CHAIN_MSG_MAP:訊息鏈結

class CMyWindow : public CWindowImpl<...>, public CPaintBkgnd<CMyWindow, RGB(0,0,255)>
{
    typedef CPaintBkgnd<CMyWindow, RGB(0,0,255)> CPaintBkgndBase;
    BEGIN_MSG_MAP(CMyWindow)
        MESSAGE_HANDLER(WM_CLOSE, OnClose)
        CHAIN_MSG_MAP(CPaintBkgndBase)
    END_MSG_MAP()
};
  • 未處理訊息傳遞給嵌入類別 (CPaintBkgnd 處理 WM_ERASEBKGND 繪製背景)。
  • 需 typedef 避免巨集參數逗號誤判。
  • 可鏈結多個嵌入類別,MFC 單一繼承無法做到。

嵌入類別範例:CPaintBkgnd

template <class T, COLORREF t_crBrushColor>
class CPaintBkgnd : public CMessageMap
{
public:
    CPaintBkgnd() { m_hbrBkgnd = CreateSolidBrush(t_crBrushColor); }
    ~CPaintBkgnd() { DeleteObject(m_hbrBkgnd); }
    BEGIN_MSG_MAP(CPaintBkgnd)
        MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd)
    END_MSG_MAP()
    LRESULT OnEraseBkgnd(UINT, WPARAM wParam, LPARAM, BOOL&)
    {
        T* pT = static_cast<T*>(this);
        HDC dc = (HDC)wParam; RECT rc; pT->GetClientRect(&rc);
        FillRect(dc, &rc, m_hbrBkgnd); return 1;
    }
protected:
    HBRUSH m_hbrBkgnd;
};

ATL 程式架構與 WinMain

// stdafx.h
#define STRICT; #define VC_EXTRALEAN
#include <atlbase.h>
extern CComModule _Module;
#include <atlwin.h>

// main.cpp
CComModule _Module;
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR, int nCmdShow)
{
    _Module.Init(NULL, hInst);
    CMyWindow wndMain;
    if (NULL == wndMain.Create(NULL, CWindow::rcDefault, _T("My First ATL Window"))) return 1;
    wndMain.ShowWindow(nCmdShow); wndMain.UpdateWindow();
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0) > 0) { TranslateMessage(&msg); DispatchMessage(&msg); }
    _Module.Term(); return msg.wParam;
}
  • CComModule _Module 對應 MFC CWinApp theApp,名稱固定為 _Module。
  • CWindow::rcDefault 相當於 CW_USEDEFAULT。
  • ATL 內部以組合語言技巧關聯 HWND 與 C++ 物件,支援跨執行緒傳遞 CWindow 物件。

ATL 對話框實作

class CAboutDlg : public CDialogImpl<CAboutDlg>
{
public:
    enum { IDD = IDD_ABOUT };
    BEGIN_MSG_MAP(CAboutDlg)
        MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
        MESSAGE_HANDLER(WM_CLOSE, OnClose)
        COMMAND_ID_HANDLER(IDOK, OnOKCancel)
        COMMAND_ID_HANDLER(IDCANCEL, OnOKCancel)
    END_MSG_MAP()
    LRESULT OnInitDialog(UINT, WPARAM, LPARAM, BOOL&) { CenterWindow(); return TRUE; }
    LRESULT OnClose(UINT, WPARAM, LPARAM, BOOL&) { EndDialog(IDCANCEL); return 0; }
    LRESULT OnOKCancel(WORD, WORD, HWND, BOOL& bHandled) { EndDialog(wID); return 0; }
};
  • 需自行處理 IDOK/IDCANCEL、WM_CLOSE、WM_INITDIALOG。
  • DoModal() 第一個參數為父視窗 HWND (MFC 為建構式傳遞指標)。
  • _Module.GetResourceInstance() 相當於 AfxGetResourceHandle()。

ATL 主視窗執行畫面 ATL 對話框執行畫面

下一章預告

將介紹 WTL 基礎視窗類別與更好用的訊息映射巨集 (BEGIN_MSG_MAP_EX),正式進入 WTL 核心開發領域。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved