WTL for MFC Programmers, Part I - ATL GUI Classes

WTL for MFC Programmers

本文章總結(jié)自 這篇文章

本章內(nèi)容

  • ATL 背景知識(shí)
  • ATL 窗口類(lèi)
  • ATL 窗口實(shí)現(xiàn)
  • ATL 對(duì)話框?qū)崿F(xiàn)

ATL 背景知識(shí)

WTL 是構(gòu)建于 ATL 之上的一系列附加類(lèi)号显。要學(xué)習(xí) WTL 首先得對(duì) ATL 進(jìn)行一些介紹弛房。

ATL 和 WTL 的發(fā)展歷史

Active Template Library(活動(dòng)模板庫(kù)), 是為了方便進(jìn)行 COM 組件和 ActiveX 控件開(kāi)發(fā)而誕生的。由于 ATL 是為了開(kāi)發(fā) COM 而存在的须床,所以只提供了非常簡(jiǎn)單的界面類(lèi)。直接用 ATL 開(kāi)發(fā)界面程序是比較繁瑣的。所以才會(huì)在此之上封裝 WTL 來(lái)方便開(kāi)發(fā)界面程序。

ATL 風(fēng)格的模版

class CMyWnd : public CWindowImpl<CMyWnd>
{
    // do something ...
};

上面的代碼初看可能覺(jué)得很奇怪咆爽,為啥 CMyWnd 繼承了 CWindowImpl, CWindowImpl 又拿 CMyWnd 當(dāng)模版?這么做不會(huì)報(bào)錯(cuò)嗎置森?這么做有什么作用斗埂?

首先,這樣做不會(huì)報(bào)錯(cuò)凫海,因?yàn)?C++ 的語(yǔ)法解釋說(shuō)即使 CMyWnd 類(lèi)只是被部分定義呛凶,類(lèi)名 CMyWnd 已經(jīng)被列入遞歸繼承列表,是可以使用的行贪。

下面的例子解釋了這種寫(xiě)法如何工作:

template <class T>
class B1
{
public:
    void SayHi()
    {
        T* pT = static_cast<T*>(this);
        pT->PrintClassName();
    }
    void PrintClassName() { printf("This is B1\n"); }
};

class D1 : public B1<D1>
{
    // 沒(méi)有覆寫(xiě)任何函數(shù)
};

class D2 : public B1<D2>
{
public:
    void PrintClassName() { printf("This is D2\n"); }
};

int main()
{
    D1 d1;
    D2 d2;

    d1.SayHi();    // This is B1
    d2.SayHi();    // This is D2

    return 0;
}

上述代碼實(shí)現(xiàn)了類(lèi)似于“虛函數(shù)”的多態(tài)功能漾稀。

通過(guò)這種模版寫(xiě)法模闲, D2 繼承的 B1.SayHi 函數(shù),實(shí)際上被解釋成:

void B1<D2>::SayHi()
{
    D2* pT = static_cast<D2*>(this);
    pT->PrintClassName();
}

SayHi 調(diào)用的是 D2 的 PrintClassName 方法崭捍。

如果不使用這種模版寫(xiě)法尸折,那么 B1 的 SayHi 函數(shù)在調(diào)用 PrintClassName 的時(shí)候,只能去調(diào)用 B1 自己的 PrintClassName 函數(shù)殷蛇,無(wú)法做到調(diào)用 D2 覆寫(xiě)后的 PrintClassName 函數(shù)实夹。

這樣做的好處如下:

  1. 不需要使用指向?qū)ο蟮闹羔槪梢灾苯邮褂脤?duì)象來(lái)調(diào)用多態(tài)接口粒梦;
  2. 節(jié)省內(nèi)存亮航,因?yàn)椴恍枰摵瘮?shù)表;
  3. 因?yàn)闆](méi)有虛函數(shù)表所以不會(huì)發(fā)生在運(yùn)行時(shí)調(diào)用空指針指向的虛函數(shù)谍倦;
  4. 所有的函數(shù)在編譯時(shí)確定(區(qū)別于 C++ 的虛函數(shù)機(jī)制塞赂,在運(yùn)行時(shí)確定調(diào)用哪個(gè)函數(shù))泪勒。有利于編譯程序?qū)Υa的優(yōu)化昼蛀;

回到最初的代碼:

class CMyWnd : public CWindowImpl<CMyWnd>
{
    // do something ...
};

這種寫(xiě)法的作用也就可以理解了。 CMyWnd 中覆寫(xiě)的函數(shù)圆存,將能夠以類(lèi)似多態(tài)的方式被 CWindowImpl 正確調(diào)用叼旋。并且節(jié)省了虛函數(shù)表帶來(lái)的內(nèi)存開(kāi)銷(xiāo)。

ATL 窗口類(lèi)

CWindow:

封裝了所有對(duì) HWND 的操作沦辙,幾乎所有以 HWND 為第一個(gè)參數(shù)的窗口 API 都經(jīng)過(guò)了 CWindow 的封裝夫植。 CWindow 類(lèi)有一個(gè)公有成員 m_hWnd 使你可以直接對(duì)窗口進(jìn)行操作。

CWindowImpl:

繼承自 CWindow, 使用它可以對(duì)窗口消息進(jìn)行處理油讯,從而使窗口具有不同通過(guò)的功能和表現(xiàn)详民。另外它還封裝了 窗口類(lèi)的注冊(cè),窗口的子類(lèi)化 等功能陌兑。

CAxWindow:

繼承自 CWindow, 用于實(shí)現(xiàn)含有 ActiveX 控件的窗口沈跨;

CDialogImpl:

繼承自 CWindow, 用于實(shí)現(xiàn)普通的對(duì)話框;

CAxDialogImpl:

繼承自 CWindow, 用于實(shí)現(xiàn)含有 ActiveX 控件的對(duì)話框兔综;

ATL 窗口實(shí)現(xiàn):

要實(shí)現(xiàn)一個(gè) ATL 窗口饿凛,要按照如下的步驟:

  1. 在 stdafx.h 中添加 ATL 相關(guān)的頭文件:

    #include <atlbase.h>       // 基本 ATL 類(lèi)
    extern CComModule _Module; // 全局 _Module
    #include <atlwin.h>        // 窗口 ATL 類(lèi)
    
  2. 在 main.cpp 中定義 CComModule _Module 并初始化它:

    #include "stdafx.h"
    
    CComModule _Module;
    
    int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                         _In_opt_ HINSTANCE hPrevInstance,
                         _In_ LPWSTR    lpCmdLine,
                         _In_ int       nCmdShow)
    {
        _Module.Init(NULL, hInstance); // 初始化 _Module
    
        // 在這里進(jìn)行 ATL 窗口的創(chuàng)建、消息泵的創(chuàng)建 ...
    
        _Module.Term();                // 結(jié)束 _Module
        return 0;
    }
    

    一個(gè) ATL 程序包含一個(gè) CComModule 類(lèi)型的全局變量 _Module, 這和 MFC 程序都有一個(gè) CWinApp 類(lèi)型的全局變量 theApp 有點(diǎn)兒類(lèi)似软驰,唯一不同的是在 ATL 中這個(gè)變量必須被命名為 _Module.
    _Module 在 main.cpp 中定義并初始化涧窒,并通過(guò) extern 關(guān)鍵字在 stdafx.h 文件中聲明,其他 #include "stdafx.h" 的模塊就可以使用 _Module 來(lái)進(jìn)行一些操作锭亏。

  3. 在 MyWindow.h 中定義自己的窗口 CMyWindow:

    class CMyWindow : public CWindowImpl<CMyWindow, CWindow, CFrameWinTraits>
    {
    public:
        DECLARE_WND_CLASS(_T("My Window Class"))   // 指定窗口類(lèi)名
    
        // 消息映射表
        BEGIN_MSG_MAP(CMyWindow)
            MESSAGE_HANDLER(WM_CLOSE, OnClose)     // 在這里將消息映射到函數(shù)
            MESSAGE_HANDLER(WM_DESTROY, OnDestroy) //
        END_MSG_MAP()
    
        LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
        {
            DestroyWindow();
            return 0;
        }
    
        LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
        {
            PostQuitMessage(0);
            return 0;
        }
    };
    

    注意第一行代碼 class CMyWindow : public CWindowImpl<CMyWindow, CWindow, CFrameWinTraits>
    模板參數(shù)中的第一個(gè)纠吴,這樣寫(xiě)的原因之前已經(jīng)解釋過(guò),是為了實(shí)現(xiàn)類(lèi)似“多態(tài)”的效果慧瘤;
    模板參數(shù)中的第二個(gè)戴已,目前不知道原因膳凝;
    模板參數(shù)中的第三個(gè),用于指定窗口類(lèi)型恭陡,如 WS_OVERLAPPEDWINDOW, WS_EX_APPWINDOW 等蹬音, CFrameWinTraits 是 ATL 預(yù)先定義的特殊類(lèi)型,你也可以自己定義:

    typedef CWinTraits<WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,WS_EX_APPWINDOW> CMyWindowTraits;
    class CMyWindow : public CWindowImpl<CMyWindow, CWindow, CMyWindowTraits>
    
  4. 在 main.cpp 中使用 CMyWindow 類(lèi)創(chuàng)建主窗口:

    #include "stdafx.h"
    #include "MyWindow.h"
    
    CComModule _Module;
    
    int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                         _In_opt_ HINSTANCE hPrevInstance,
                         _In_ LPWSTR    lpCmdLine,
                         _In_ int       nCmdShow)
    {
        _Module.Init(NULL, hInstance);
    
        // 聲明 CMyWindow 對(duì)象
        CMyWindow wndMain;
    
        // 創(chuàng)建窗口
        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;
    }
    

ATL 對(duì)話框?qū)崿F(xiàn):

要實(shí)現(xiàn)一個(gè) ATL 對(duì)話框休玩,和生成 ATL 窗口的方式差不多著淆,只有兩點(diǎn)不同:

  1. 窗口的基類(lèi)是 CDialogImpl 而不是 CWindowImpl;

  2. 你需要在對(duì)話框類(lèi)中定義名稱為 IDD 的公有成員用來(lái)保存對(duì)話框資源的 ID;

    #include "resource.h"
    
    class CAboutDlg : public CDialogImpl<CAboutDlg>
    {
    public:
        enum { IDD = IDD_ABOUT };
    
        BEGIN_MSG_MAP(CAboutDlg)
            MESSAGE_HANDLER(WM_INITDIALOG, OnInitControl)
            MESSAGE_HANDLER(WM_CLOSE, OnClose)
            COMMAND_ID_HANDLER(IDOK, OnOkCancel)
            COMMAND_ID_HANDLER(IDCANCEL, OnOkCancel)
        END_MSG_MAP()
    
        LRESULT OnInitControl(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
        {
            CenterWindow();
            return TRUE;
        }
    
        LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
        {
            EndDialog(0);
            return 0;
        }
    
        LRESULT OnOkCancel(WORD wNotifyCode, WORD wID, HWND hWndCtrl, BOOL& bHandled)
        {
            EndDialog(wID);
            return 0;
        }
    };
    
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市拴疤,隨后出現(xiàn)的幾起案子永部,更是在濱河造成了極大的恐慌,老刑警劉巖呐矾,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件苔埋,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蜒犯,警方通過(guò)查閱死者的電腦和手機(jī)组橄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)罚随,“玉大人玉工,你說(shuō)我怎么就攤上這事√云校” “怎么了遵班?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)潮改。 經(jīng)常有香客問(wèn)我狭郑,道長(zhǎng),這世上最難降的妖魔是什么汇在? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任翰萨,我火速辦了婚禮,結(jié)果婚禮上趾疚,老公的妹妹穿的比我還像新娘缨历。我一直安慰自己,他們只是感情好糙麦,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布辛孵。 她就那樣靜靜地躺著,像睡著了一般赡磅。 火紅的嫁衣襯著肌膚如雪魄缚。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,190評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音冶匹,去河邊找鬼习劫。 笑死,一個(gè)胖子當(dāng)著我的面吹牛嚼隘,可吹牛的內(nèi)容都是我干的诽里。 我是一名探鬼主播,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼飞蛹,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼谤狡!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起卧檐,我...
    開(kāi)封第一講書(shū)人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤墓懂,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后霉囚,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體捕仔,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年盈罐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了榜跌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡暖呕,死狀恐怖斜做,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情湾揽,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布笼吟,位于F島的核電站库物,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏贷帮。R本人自食惡果不足惜戚揭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望撵枢。 院中可真熱鬧民晒,春花似錦、人聲如沸锄禽。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)沃但。三九已至磁滚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背垂攘。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工维雇, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人晒他。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓吱型,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親陨仅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子唁影,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Windows 95中文輸入法編輯器(IME) 微軟 翻譯:TBsoft Software Studio ...
    returntrue閱讀 2,644評(píng)論 0 3
  • 實(shí)驗(yàn)一 unresolved external symbol __endthreadex錯(cuò)誤解決,是因?yàn)闆](méi)有引用M...
    yueyue_projects閱讀 2,670評(píng)論 0 5
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,078評(píng)論 25 707
  • 對(duì)掂名,我愛(ài)他据沈,但是那晚我也拒絕了他。 這個(gè)故事我講了三遍饺蔑,內(nèi)容不同锌介,互相補(bǔ)充,唯一相同的是隱藏了我愛(ài)他的事實(shí)猾警。 1....
    heikf閱讀 408評(píng)論 0 0
  • 認(rèn)識(shí)自己的能力孔祸,清楚自己的優(yōu)缺點(diǎn),并以此來(lái)選擇專業(yè)发皿,是一種辦法崔慧。 結(jié)合自己的興趣愛(ài)好和能力優(yōu)勢(shì) 序號(hào) 學(xué)科興趣類(lèi)型...
    燕妮老師說(shuō)閱讀 434評(píng)論 0 0