青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

隨筆-250  評(píng)論-20  文章-55  trackbacks-0
README.TXT

在你開(kāi)始使用WTL或著在本文章的討論區(qū)張貼消息之前,我想請(qǐng)你先閱讀下面的材料。

你需要開(kāi)發(fā)平臺(tái)SDK(Platform SDK)。你要使用WTL不能沒(méi)有它,你可以使用在線升級(jí)安裝開(kāi)發(fā)平臺(tái)SDK,也可以下載全部文件后在本地安裝。在使用之前要將SDK的包含文件(.h頭文件)和庫(kù)文件(.Lib文件)路徑添加到VC的搜索目錄,SDK有現(xiàn)成的工具完成這個(gè)工作,這個(gè)工具位于開(kāi)發(fā)平臺(tái)SDK程序組的“Visual Studio Registration”文件夾里。

你需要安裝 WTL。你可以從微軟的網(wǎng)站上下載WTL的7.0版,(*編輯注:WTL也可以到VC知識(shí)庫(kù)http://www.vckbase.com/tools下載) 在安裝之前可以先查看“Introduction to WTL - Part 1”和“Easy installation of WTL”這兩篇文章,了解一下所要安裝的文件的信息,雖然現(xiàn)在這些文章有些過(guò)時(shí),但還是可以提供很多有用的信息。有一件我認(rèn)為不該在本篇文章中提到的事是告訴VC如何搜索WTL的包含文件路徑,如果你用的VC6,用鼠標(biāo)點(diǎn)擊 Tools\Options,轉(zhuǎn)到Directories標(biāo)簽頁(yè),在顯示路徑的列表框中選擇Include Files,然后將WTL的包含文件的存放路徑添加到包含文件搜索路徑列表中。

你需要了解MFC。很好地了解MFC將有助于你理解后面提到的有關(guān)消息映射的宏并能夠編輯那些標(biāo)有“不要編輯(DO NOT EDIT)”的代碼而不會(huì)出現(xiàn)問(wèn)題。

你需要清楚地知道如何使用Win32 API編程。如果你是直接從MFC開(kāi)始學(xué)習(xí)Windows編程,沒(méi)有學(xué)過(guò)API級(jí)別的消息處理方式,那很不幸你會(huì)在使用WTL時(shí)遇到麻煩。如果不了解Windows消息中WPARAM參數(shù)和LPARAM參數(shù)的意義,應(yīng)該明白需要讀一些這方面的文章(在CodeProject有大量的此類文章)。

你需要知道 C++ 模板的語(yǔ)法,你可以到VC Forum FAQ 相關(guān)的連接尋求答案。

我只是討論了一些涵蓋VC 6的特點(diǎn),不過(guò)據(jù)我了解所有的程序都可以在VC 7上使用。由于我不使用VC 7,我無(wú)法對(duì)那些在VC 7中出現(xiàn)的問(wèn)題提供幫助,不過(guò)你還是可以放心的在此張貼你的問(wèn)題,因?yàn)槠渌娜丝赡軙?huì)幫助你。

對(duì)本系列文章的總體介紹

WTL 具有兩面性,確實(shí)是這樣的。它沒(méi)有MFC的界面(GUI)類庫(kù)那樣功能強(qiáng)大,但是能夠生成很小的可執(zhí)行文件。如果你象我一樣使用MFC進(jìn)行界面編程,你會(huì)覺(jué)得MFC提供的界面控件封裝使用起來(lái)非常舒服,更不用說(shuō)MFC內(nèi)置的消息處理機(jī)制。當(dāng)然,如果你也象我一樣不希望自己的程序僅僅因?yàn)槭褂昧薓FC的框架就增加幾百K的大小的話,WTL就是你的選擇。當(dāng)然,我們還要克服一些障礙:

  • ATL樣式的模板類初看起來(lái)有點(diǎn)怪異
  • 沒(méi)有類向?qū)У闹С?,所以要手工處理所有的消息映射?
  • MSDN沒(méi)有正式的文檔支持,你需要到處去收集有關(guān)的文檔,甚至是查看WTL的源代碼。
  • 買不到參考書籍
  • 沒(méi)有微軟的官方支持
  • ATL/WTL的窗口與MFC的窗口有很大的不同,你所了解的有關(guān)MFC的知識(shí)并不全部適用與WTL。

從另一方面講,WTL也有它自身的優(yōu)勢(shì):

  • 不需要學(xué)習(xí)或掌握復(fù)雜的文檔/視圖框架。
  • 具有MFC的基本的界面特色,比如DDX/DDV和命令狀態(tài)的自動(dòng)更新功能(譯者加:比如菜單的Check標(biāo)記和Enable標(biāo)記)。
  • 增強(qiáng)了一些MFC的特性(比如更加易用的分隔窗口)。
  • 可生成比靜態(tài)鏈接的MFC程序更小的可執(zhí)行文件(譯者加:WTL的所有源代碼都是靜態(tài)鏈接到你的程序中的)。
  • 你可以修正自己使用的WTL中的錯(cuò)誤(BUG)而不會(huì)影響其他的應(yīng)用程序(相比之下,如果你修正了有BUG的MFC/CRT動(dòng)態(tài)庫(kù)就可能會(huì)引起其它應(yīng)用程序的崩潰。
  • 如果你仍然需要使用MFC,MFC的窗口和ATL/WTL的窗口可以“和平共處”。(例如我工作中的一個(gè)原型就使用了了MFC的CFrameWnd,并在其內(nèi)包含了WTL的CSplitterWindow,在CSplitterWindow中又使用了MFC的CDialogs -- 我并不是為了炫耀什么,只是修改了MFC的代碼使之能夠使用WTL的分割窗口,它比MFC的分割窗口好的多)。

在這一系列文章中,我將首先介紹ATL的窗口類,畢竟WTL是構(gòu)建與ATL之上的一系列附加類,所以需要很好的了解ATL的窗口類。介紹完ATL之后我將介紹WTL的特性以并展示它是如何使界面編程變得輕而易舉。

對(duì)第一章的簡(jiǎn)單介紹

WTL是個(gè)很酷的工具,在理解這一點(diǎn)之前需要首先介紹ATL。WTL是構(gòu)建與ATL之上的一系列附加類,如果你是個(gè)嚴(yán)格使用MFC的程序員那么你可能沒(méi)有機(jī)會(huì)接觸到ATL的界面類,所以請(qǐng)容忍我在開(kāi)始WTL之前先羅索一些別的東西,繞道來(lái)介紹一下ATL是很有必要地。

在本文的第一部分,我將給出一點(diǎn)ATL的背景知識(shí),包括一些編寫ATL代碼必須知道的基本知識(shí),快速的解釋一些令人不知所措的ATL模板類和基本的ATL窗口類。

ATL 背景知識(shí) ATL 和 WTL 的發(fā)展歷史

“活動(dòng)模板庫(kù)”(Active Template Library)是一個(gè)很古怪的名字,不是嗎?那些年紀(jì)大的人可能還記得它最初被稱為“網(wǎng)絡(luò)組件模板庫(kù)”,這可能是它更準(zhǔn)確的稱呼,因?yàn)锳TL的目的就是使編寫組件對(duì)象和ActiveX控件更容易一些(ATL是在微軟開(kāi)發(fā)新產(chǎn)品ActiveX-某某的過(guò)程中開(kāi)發(fā)的,那些ActiveX-某某現(xiàn)在被稱為某某.NET)。由于ATL是為了便于編寫組件對(duì)象而存在的,所以只提供了簡(jiǎn)單的界面類,相當(dāng)于MFC的窗口類(CWnd)和對(duì)話框類(CDialog)。幸運(yùn)的是這些類非常的靈活,能夠在其基礎(chǔ)上構(gòu)建象WTL這樣的附加類。

WTL現(xiàn)在已經(jīng)是第二次修正了,最初的版本是3.1,現(xiàn)在的版本是7(WTL的版本號(hào)之所以這樣選擇是為了與ATL的版本匹配,所以不存在1和2這樣的版本號(hào))。WTL 3.1可以與VC 6和VC 7一起使用,但是在VC 7下需要定義幾個(gè)預(yù)處理標(biāo)號(hào)。WTL 7向下兼容WTL 3.1,并且不作任何修改就可以與VC 7一起使用,現(xiàn)在看來(lái)沒(méi)有任何理由還使用3.1來(lái)進(jìn)行新的開(kāi)發(fā)工作。

ATL-style 模板

即使你能夠毫不費(fèi)力地閱讀C++的模板類代碼,仍然有兩件事可能會(huì)使你有些頭暈,以下面這個(gè)類的定義為例:

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

這樣作是合法的,因?yàn)镃++的語(yǔ)法解釋說(shuō)即使CMyWnd類只是被部分定義,類名CMyWnd已經(jīng)被列入遞歸繼承列表,是可以使用的。將類名作為模板類的參數(shù)是因?yàn)锳TL要做另一件詭秘的事情,那就是編譯期間的虛函數(shù)調(diào)用機(jī)制。

如果你想要了解它是如何工作地,請(qǐng)看下面的例子:

template <class T>
class B1
{
public: 
    void SayHi() 
    {
        T* pT = static_cast<T*>(this);   // HUH?? 我將在下面解釋
 
        pT->PrintClassName();
    }
protected:
    void PrintClassName() { cout << "This is B1"; }
};
 
class D1 : public B1<D1>
{
    // No overridden functions at all
};
 
class D2 : public B1<D2>
{
protected:
    void PrintClassName() { cout << "This is D2"; }
};
 
main()
{
    D1 d1;
    D2 d2;
 
    d1.SayHi();    // prints "This is B1"
    d2.SayHi();    // prints "This is D2"
}

這句代碼static_cast<T*>(this) 就是竅門所在。它根據(jù)函數(shù)調(diào)用時(shí)的特殊處理將指向B1類型的指針this指派為D1或D2類型的指針,因?yàn)槟0宕a是在編譯其間生成的,所以只要編譯器生成正確的繼承列表,這樣指派就是安全的。(如果你寫成:

class D3 : public B1<D2>

就會(huì)有麻煩) 之所以安全是因?yàn)閠his對(duì)象只可能是指向D1或D2(在某些情況下)類型的對(duì)象,不會(huì)是其他的東西。注意這很像C++的多態(tài)性(polymorphism),只是SayHi()方法不是虛函數(shù)。

要解釋這是如何工作的,首先看對(duì)每個(gè)SayHi()函數(shù)的調(diào)用,在第一個(gè)函數(shù)調(diào)用,對(duì)象B1被指派為D1,所以代碼被解釋成:

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

由于D1沒(méi)有重載PrintClassName(),所以查看基類B1,B1有PrintClassName(),所以B1的PrintClassName()被調(diào)用。

現(xiàn)在看第二個(gè)函數(shù)調(diào)用SayHi(),這一次對(duì)象被指派為D2類型,SayHi()被解釋成:

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

這一次,D2含有PrintClassName()方法,所以D2的PrintClassName()方法被調(diào)用。

這種技術(shù)的有利之處在于:

  • 不需要使用指向?qū)ο蟮闹羔槨?
  • 節(jié)省內(nèi)存,因?yàn)椴恍枰摵瘮?shù)表。
  • 因?yàn)闆](méi)有虛函數(shù)表所以不會(huì)發(fā)生在運(yùn)行時(shí)調(diào)用空指針指向的虛函數(shù)。
  • 所有的函數(shù)調(diào)用在編譯時(shí)確定(譯者加:區(qū)別于C++的虛函數(shù)機(jī)制使用的動(dòng)態(tài)編連),有利于編譯程序?qū)Υa的優(yōu)化。

節(jié)省虛函數(shù)表在這個(gè)例子中看起來(lái)無(wú)足輕重(每個(gè)虛函數(shù)只有4個(gè)字節(jié)),但是設(shè)想一下如果有15個(gè)基類,每個(gè)類含有20個(gè)方法,加起來(lái)就相當(dāng)可觀了。

ATL 窗口類

好了,關(guān)于ATL的背景知識(shí)已經(jīng)講的構(gòu)多了,到了該正式講ATL的時(shí)候了。ATL在設(shè)計(jì)時(shí)接口定義和實(shí)現(xiàn)是嚴(yán)格區(qū)分開(kāi)的,這在窗口類的設(shè)計(jì)中是最明顯的,這一點(diǎn)類似于COM,COM的接口定義和實(shí)現(xiàn)是完全分開(kāi)的(或者可能有多個(gè)實(shí)現(xiàn))。

ATL有一個(gè)專門為窗口設(shè)計(jì)的接口,可以做全部的窗口操作,這就是CWindow。它實(shí)際上就是對(duì)HWND操作的包裝類,對(duì)幾乎所有以HWND句柄為第一個(gè)參數(shù)的窗口API的進(jìn)行了封裝,例如:SetWindowText() 和 DestroyWindow()。CWindow類有一個(gè)公有成員m_hWnd,使你可以直接對(duì)窗口的句柄操作,CWindow還有一個(gè)操作符HWND,你可以講CWindow對(duì)象傳遞給以HWND為參數(shù)的函數(shù),但這與CWnd::GetSafeHwnd()(譯者加:MFC的方法)沒(méi)有任何等同之處。

CWindow 與 MFC 的CWnd類有很大的不同,創(chuàng)建一個(gè)CWindow對(duì)象占用很少的資源,因?yàn)橹挥幸粋€(gè)數(shù)據(jù)成員,沒(méi)有MFC窗口中的對(duì)象鏈,MFC內(nèi)部維持這一個(gè)對(duì)象鏈,此對(duì)象鏈將HWND映射到CWnd對(duì)象。還有一點(diǎn)與MFC的CWnd類不同的是當(dāng)一個(gè)CWindow對(duì)象超出了作用域,它關(guān)聯(lián)的窗口并不被銷毀掉,這意味著你并不需要隨時(shí)記得分離你所創(chuàng)建的臨時(shí)CWindow對(duì)象。

在ATL類中對(duì)窗口過(guò)程的實(shí)現(xiàn)是CWindowImpl。CWindowImpl 含有所有窗口實(shí)現(xiàn)代碼,例如:窗口類的注冊(cè),窗口的子類化,消息映射以及基本的WindowProc()函數(shù),可以看出這與MFC的設(shè)計(jì)有很大的不同,MFC將所有的代碼都放在一個(gè)CWnd類中。

還有兩個(gè)獨(dú)立的類包含對(duì)話框的實(shí)現(xiàn),它們分別是CDialogImpl 和 CAxDialogImpl,CDialogImpl 用于實(shí)現(xiàn)普通的對(duì)話框而CAxDialogImpl實(shí)現(xiàn)含有ActiveX控件的對(duì)話框。

定義一個(gè)窗口的實(shí)現(xiàn)

任何非對(duì)話框窗口都是從CWindowImpl 派生的,你的新類需要包含三件事情:

  1. 一個(gè)窗口類的定義
  2. 一個(gè)消息映射鏈
  3. 窗口使用的默認(rèn)窗口類型,稱為window traits

窗口類的定義通過(guò)DECLARE_WND_CLASS宏或DECLARE_WND_CLASS_EX宏來(lái)實(shí)現(xiàn)。這輛個(gè)宏定義了一個(gè)CWndClassInfo結(jié)構(gòu),這個(gè)結(jié)構(gòu)封裝了WNDCLASSEX結(jié)構(gòu)。DECLARE_WND_CLASS宏讓你指定窗口類的類名,其他參數(shù)使用默認(rèn)設(shè)置,而DECLARE_WND_CLASS_EX宏還允許你指定窗口類的類型和窗口的背景顏色,你也可以用NULL作為類名,ATL會(huì)自動(dòng)為你生成一個(gè)類名。

讓我們開(kāi)始定義一個(gè)新類,在后面的章節(jié)我會(huì)逐步的完成這個(gè)類的定義。

class CMyWindow : public CWindowImpl<CMyWindow>
{
public:
    DECLARE_WND_CLASS(_T("My Window Class"))
};

接下來(lái)是消息映射鏈,ATL的消息映射鏈比MFC的簡(jiǎn)單的多,ATL的消息映射鏈被展開(kāi)為switch語(yǔ)句,switch語(yǔ)句正確的消息處理者并調(diào)用相應(yīng)的函數(shù)。使用消息映射鏈的宏是BEGIN_MSG_MAP 和 END_MSG_MAP,讓我們?yōu)槲覀兊拇翱谔砑右粋€(gè)空的消息映射鏈。

class CMyWindow : public CWindowImpl<CMyWindow>
{
public:
    DECLARE_WND_CLASS(_T("My Window Class"))
 
    BEGIN_MSG_MAP(CMyWindow)
    END_MSG_MAP()
};

我將在下一節(jié)展開(kāi)講如何如何添加消息處理到消息映射鏈。最后,我們需要為我們的窗口類定義窗口的特征,窗口的特征就是窗口類型和擴(kuò)展窗口類型的聯(lián)合體,用于創(chuàng)建窗口時(shí)指定窗口的類型。窗口類型被指定為參數(shù)模板,所以窗口的調(diào)用者不需要為指定窗口的正確類型而煩心,下面是是同ATL類CWinTraits定義窗口類型的例子:

typedef CWinTraits<WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,WS_EX_APPWINDOW> CMyWindowTraits;
 
class CMyWindow : public CWindowImpl<CMyWindow, CWindow, CMyWindowTraits>
{
public:
    DECLARE_WND_CLASS(_T("My Window Class"))
 
    BEGIN_MSG_MAP(CMyWindow)
    END_MSG_MAP()
};

調(diào)用者可以重載CMyWindowTraits的類型定義,但是一般情況下這是沒(méi)有必要的,ATL提供了幾個(gè)預(yù)先定義的特殊的類型,其中之一就是CFrameWinTraits,一個(gè)非常棒的框架窗口:

typedef CWinTraits<WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
WS_EX_APPWINDOW | WS_EX_WINDOWEDGE> CFrameWinTraits;
填寫消息映射鏈

ATL的消息映射鏈?zhǔn)菍?duì)開(kāi)發(fā)者不太友好的部分,也是WTL對(duì)其改進(jìn)最大的部分。類向?qū)е辽倏梢宰屇闾砑酉㈨憫?yīng),然而ATL沒(méi)有消息相關(guān)的宏和象MFC那樣的參數(shù)自動(dòng)展開(kāi)功能,在ATL中只有三種類型的消息處理,一個(gè)是WM_NOTIFY,一個(gè)是WM_COMMAND,第三類是其他窗口消息,讓我們開(kāi)始為我們的窗口添加WM_CLOSE 和 WM_DESTROY的消息相應(yīng)函數(shù)。

class CMyWindow : public CWindowImpl<CMyWindow, CWindow, CFrameWinTraits>
{
public:
    DECLARE_WND_CLASS(_T("My Window Class"))
 
    BEGIN_MSG_MAP(CMyWindow)
        MESSAGE_HANDLER(WM_CLOSE, OnClose)
        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;
    }
};

你可能注意到消息響應(yīng)函數(shù)的到的是原始的WPARAM 和 LPARAM值,你需要自己將其展開(kāi)為相應(yīng)的消息所需要的參數(shù)。還有第四個(gè)參數(shù)bHandled,這個(gè)參數(shù)在消息相應(yīng)函數(shù)調(diào)用被ATL設(shè)置為TRUE,如果在你的消息響應(yīng)處理完之后需要ATL調(diào)用默認(rèn)的WindowProc()處理該消息,你可以講bHandled設(shè)置為FALSE。這與MFC不同,MFC是顯示的調(diào)用基類的響應(yīng)函數(shù)來(lái)實(shí)現(xiàn)的默認(rèn)的消息處理的。

讓我們也添加一個(gè)對(duì)WM_COMMAND消息的處理,假設(shè)我們的窗口有一個(gè)ID為IDC_ABOUT的About菜單:

class CMyWindow : public CWindowImpl<CMyWindow, CWindow, CFrameWinTraits>
{
public:
    DECLARE_WND_CLASS(_T("My Window Class"))
 
    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 uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        DestroyWindow();
        return 0;
    }
 
    LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        PostQuitMessage(0);
        return 0;
    }
 
    LRESULT OnAbout(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
    {
        MessageBox ( _T("Sample ATL window"), _T("About MyWindow") );
        return 0;
    }
};

需要注意得是COMMAND_HANDLER宏已經(jīng)將消息的參數(shù)展開(kāi)了,同樣,NOTIFY_HANDLER宏也將WM_NOTIFY消息的參數(shù)展開(kāi)了。

高級(jí)消息映射鏈和嵌入類

ATL的另一個(gè)顯著不同之處就是任何一個(gè)C++類都可以響應(yīng)消息,而MFC只是將消息響應(yīng)任務(wù)分給了CWnd類和CCmdTarget類,外加幾個(gè)有PreTranslateMessage()方法的類。ATL的這種特性允許我們編寫所謂的“嵌入類”,為我們的窗口添加特性只需將該類添加到繼承列表中就行了,就這么簡(jiǎn)單!

一個(gè)基本的帶有消息映射鏈的類通常是模板類,將派生類的類名作為模板的參數(shù),這樣它就可以訪問(wèn)派生類中的成員,比如m_hWnd(CWindow類中的HWND成員)。讓我們來(lái)看一個(gè)嵌入類的例子,這個(gè)嵌入類通過(guò)響應(yīng)WM_ERASEBKGND消息來(lái)畫窗口的背景。

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 uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        T*   pT = static_cast<T*>(this);
        HDC  dc = (HDC) wParam;
        RECT rcClient;
 
        pT->GetClientRect ( &rcClient );
        FillRect ( dc, &rcClient, m_hbrBkgnd );
        return 1;    // we painted the background
    }
 
protected:
    HBRUSH m_hbrBkgnd;
};

讓我們來(lái)研究一下這個(gè)新類。首先,CPaintBkgnd有兩個(gè)模板參數(shù):使用CPaintBkgnd的派生類的名字和用來(lái)畫窗口背景的顏色。(t_ 前綴通常用來(lái)作為模板類的模板參數(shù)的前綴)CPaintBkgnd也是從CMessageMap派生的,這并不是必須的,因?yàn)樗行枰憫?yīng)消息的類只需使用BEGIN_MSG_MAP宏就足夠了,所以你可能看到其他的一些嵌入類的例子代碼,它們并不是從該基類派生的。

構(gòu)造函數(shù)和析構(gòu)函數(shù)都相當(dāng)簡(jiǎn)單,只是創(chuàng)建和銷毀Windows畫刷,這個(gè)畫刷由參數(shù)t_crBrushColor決定顏色。接著是消息映射鏈,它響應(yīng)WM_ERASEBKGND消息,最后由響應(yīng)函數(shù)OnEraseBkgnd()用構(gòu)造函數(shù)創(chuàng)建的畫刷填充窗口的背景。在OnEraseBkgnd()中有兩件事需要注意,一個(gè)是它使用了一個(gè)派生的窗口類的方法(即GetClientRect()),我們?nèi)绾沃琅缮愔杏蠫etClientRect()方法呢?如果派生類中沒(méi)有這個(gè)方法我們的代碼也不會(huì)有任何抱怨,由編譯器確認(rèn)派生類T是從CWindow派生的。另一個(gè)是OnEraseBkgnd()沒(méi)有將消息參數(shù)wParam展開(kāi)為設(shè)備上下文(DC)。(WTL最終會(huì)解決這個(gè)問(wèn)題,我們很快就可以看到,我保證)

要在我們的窗口中使用這個(gè)嵌入類需要做兩件事:首先,將它加入到繼承列表:

class CMyWindow : public CWindowImpl<CMyWindow, CWindow, CFrameWinTraits>,
                  public CPaintBkgnd<CMyWindow, RGB(0,0,255)>

其次,需要CMyWindow將消息傳遞給CPaintBkgnd,就是將其鏈入到消息映射鏈,在CMyWindow的消息映射鏈中加入CHAIN_MSG_MAP宏:

class CMyWindow : public CWindowImpl<CMyWindow, CWindow, CFrameWinTraits>,
                  public CPaintBkgnd<CMyWindow, RGB(0,0,255)> 
{
...
typedef CPaintBkgnd<CMyWindow, RGB(0,0,255)> CPaintBkgndBase;
 
    BEGIN_MSG_MAP(CMyWindow)
        MESSAGE_HANDLER(WM_CLOSE, OnClose)
        MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
        COMMAND_HANDLER(IDC_ABOUT, OnAbout)
        CHAIN_MSG_MAP(CPaintBkgndBase)
    END_MSG_MAP()
...
};

任何CMyWindow沒(méi)有處理的消息都被傳遞給CPaintBkgnd。應(yīng)該注意的是WM_CLOSE,WM_DESTROY和IDC_ABOUT消息將不會(huì)傳遞,因?yàn)檫@些消息一旦被處理消息映射鏈的查找就會(huì)中止。使用typedef是必要地,因?yàn)楹晔穷A(yù)處理宏,只能有一個(gè)參數(shù),如果我們將CPaintBkgnd<CMyWindow, RGB(0,0,255)>作為參數(shù)傳遞,那個(gè)“,”會(huì)使預(yù)處理器認(rèn)為我們使用了多個(gè)參數(shù)。

你可以在繼承列表中使用多個(gè)嵌入類,每一個(gè)嵌入類使用一個(gè)CHAIN_MSG_MAP宏,這樣消息映射鏈就會(huì)將消息傳遞給它。這與MFC不同,MFC地CWnd派生類只能有一個(gè)基類,MFC自動(dòng)將消息傳遞給基類。

ATL程序的結(jié)構(gòu)

到目前為止我們已經(jīng)有了一個(gè)完整地主窗口類(即使不完全有用),讓我們看看如何在程序中使用它。一個(gè)ATL程序包含一個(gè)CComModule類型的全局變量_Module,這和MFC的程序都有一個(gè)CWinApp類型的全局變量theApp有些類似,唯一不同的是在ATL中這個(gè)變量必須命名為_(kāi)Module。

下面是stdafx.h文件的開(kāi)始部分:

// stdafx.h:
#define STRICT
#define VC_EXTRALEAN
 
#include <atlbase.h>        // 基本的ATL類
extern CComModule _Module;  // 全局_Module
#include <atlwin.h>         // ATL窗口類

atlbase.h已經(jīng)包含最基本的Window編程的頭文件,所以我們不需要在包含windows.h,tchar.h之類的頭文件。在CPP文件中聲明了_Module變量:

// main.cpp:
CComModule _Module;

CComModule含有程序的初始化和關(guān)閉函數(shù),需要在WinMain()中顯示的調(diào)用,讓我們從這里開(kāi)始:

// main.cpp:
CComModule _Module;
 
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hInstPrev,
                   LPSTR szCmdLine, int nCmdShow)
{
    _Module.Init(NULL, hInst);
    _Module.Term();
}

Init()的第一個(gè)參數(shù)只有COM的服務(wù)程序才有用,由于我們的EXE不含有COM對(duì)象,我們只需將NULL傳遞給Init()就行了。ATL不提供自己的WinMain()和類似MFC的消息泵,所以我們需要?jiǎng)?chuàng)建CMyWindow對(duì)象并添加消息泵才能使我們的程序運(yùn)行。

// main.cpp:
#include "MyWindow.h"
CComModule _Module;
 
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hInstPrev,
                   LPSTR szCmdLine, int nCmdShow)
{
    _Module.Init(NULL, hInst);
 
    CMyWindow wndMain;
    MSG msg;
 
    // Create & show our main window
    if ( NULL == wndMain.Create ( NULL, CWindow::rcDefault, 
                                 _T("My First ATL Window") ))
    {
        // Bad news, window creation failed
        return 1;
     }
 
    wndMain.ShowWindow(nCmdShow);
    wndMain.UpdateWindow();
 
    // Run the message loop
    while ( GetMessage(&msg, NULL, 0, 0) > 0 )
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
 
    _Module.Term();
    return msg.wParam;
}

上面的代碼唯一需要說(shuō)明的是CWindow::rcDefault,這是CWindow中的成員(靜態(tài)數(shù)據(jù)成員),數(shù)據(jù)類型是RECT。和調(diào)用CreateWindow() API時(shí)使用CW_USEDEFAULT指定窗口的寬度和高度一樣,ATL使用rcDefault作為窗口的最初大小。

在ATL代碼內(nèi)部,ATL使用了一些類似匯編語(yǔ)言的魔法將主窗口的句柄與相應(yīng)的CMyWindow對(duì)象聯(lián)系起來(lái),在外部看來(lái)就是可以毫無(wú)問(wèn)題的在線程之間傳遞CWindow對(duì)象,而MFC的CWnd卻不能這樣作。

這就是我們的窗口:

 [First ATL window - 4K]

我得承認(rèn)這確實(shí)沒(méi)有什么激動(dòng)人心的地方。我們將添加一個(gè)About菜單并顯示一個(gè)對(duì)話框,主要是為它增加一些情趣。

ATL中的對(duì)話框

我們前面提到過(guò),ATL有兩個(gè)對(duì)話框類,我們的About對(duì)話框使用CDialogImpl。生成一個(gè)新對(duì)話框和生成一個(gè)主窗口幾乎一樣,只有兩點(diǎn)不同:

  1. 窗口的基類是CDialogImpl而不是CWindowImpl。
  2. 你需要定義名稱為IDD的公有成員用來(lái)保存對(duì)話框資源的ID。

現(xiàn)在開(kāi)始為About對(duì)話框定義一個(gè)新類:

class CAboutDlg : public CDialogImpl<CAboutDlg>
{
public:
    enum { IDD = IDD_ABOUT };
 
    BEGIN_MSG_MAP(CAboutDlg)
    END_MSG_MAP()
};

ATL沒(méi)有在內(nèi)部實(shí)現(xiàn)對(duì)“OK”和“Cancel”兩個(gè)按鈕的響應(yīng)處理,所以我們需要自己添加這些代碼,如果用戶用鼠標(biāo)點(diǎn)擊標(biāo)題欄的關(guān)閉按鈕,WM_CLOSE的響應(yīng)函數(shù)就會(huì)被調(diào)用。我們還需要處理WM_INITDIALOG消息,這樣我們就能夠在對(duì)話框出現(xiàn)時(shí)正確的設(shè)置鍵盤焦點(diǎn),下面是完整的類定義和消息響應(yīng)函數(shù)。

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 uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        CenterWindow();
        return TRUE;    // let the system set the focus
    }
 
    LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        EndDialog(IDCANCEL);
        return 0;
    }
 
    LRESULT OnOKCancel(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
    {
        EndDialog(wID);
        return 0;
    }
};

我使用一個(gè)消息響應(yīng)函數(shù)同時(shí)處理“OK”和“Cancel”兩個(gè)按鈕的WM_COMMAND消息,因?yàn)槊铐憫?yīng)函數(shù)的wID參數(shù)就已經(jīng)指明了消息是來(lái)自“OK”按鈕還是來(lái)自“Cancel”按鈕。

顯示對(duì)話框的方法與MFC相似,創(chuàng)建一個(gè)新對(duì)話框類的實(shí)例,然后調(diào)用DoModal()方法?,F(xiàn)在我們返回主窗口,添加一個(gè)帶有About菜單項(xiàng)的菜單用來(lái)顯示我們的對(duì)話框,這需要再添加兩個(gè)消息響應(yīng)函數(shù),一個(gè)是響應(yīng)WM_CREATE,另一個(gè)是響應(yīng)菜單的IDC_ABOUT命令。

class CMyWindow : public CWindowImpl<CMyWindow, CWindow, CFrameWinTraits>,
                  public CPaintBkgnd<CMyWindow,RGB(0,0,255)>
{
public:
    BEGIN_MSG_MAP(CMyWindow)
        MESSAGE_HANDLER(WM_CREATE, OnCreate)
        COMMAND_ID_HANDLER(IDC_ABOUT, OnAbout)
        // ...
        CHAIN_MSG_MAP(CPaintBkgndBase)
    END_MSG_MAP()
 
    LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
    HMENU hmenu = LoadMenu ( _Module.GetResourceInstance(),
                             MAKEINTRESOURCE(IDR_MENU1) );
 
        SetMenu ( hmenu );
        return 0;
    }
 
    LRESULT OnAbout(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
    {
    CAboutDlg dlg;
 
        dlg.DoModal();
        return 0;
    }
    // ...
};

在指定對(duì)話框的父窗口的方式上有些不同,MFC是通過(guò)構(gòu)造函數(shù)將父窗口的指針傳遞給對(duì)話框而在ATL中是將父窗口的指針作為DoModal()方法的第一個(gè)參數(shù)傳遞給對(duì)話框的,如果象上面的代碼一樣沒(méi)有指定父窗口,ATL會(huì)使用GetActiveWindow()得到的窗口(也就是我們的主框架窗口)作為對(duì)話框的父窗口。

對(duì)LoadMenu()方法的調(diào)用展示了CComModule的另一個(gè)方法-GetResourceInstance(),它返回你的EXE的HINSTANCE實(shí)例,和MFC的AfxGetResourceHandle()方法相似。(當(dāng)然還有CComModule::GetModuleInstance(),它相當(dāng)于MFC的AfxGetInstanceHandle()。)

這就是主窗口和對(duì)話框的顯示效果:

 [About box - 5K]

我會(huì)繼續(xù)講WTL,我保證!

我會(huì)繼續(xù)講WTL的,只是會(huì)在第二部分。我覺(jué)得既然這些文章是寫給使用MFC的開(kāi)發(fā)者的,所以有必要在投入WTL之前先介紹一些ATL。如果你是第一次接觸到ATL,那現(xiàn)在你就可以嘗試寫一些小程序,處理消息和使用嵌入類,你也可以嘗試用類向?qū)еС窒⒂成滏?,使它能夠自?dòng)添加消息響應(yīng)。現(xiàn)在就開(kāi)始,右鍵單擊CMyWindow項(xiàng),在彈出的上下文菜單中單擊“Add Windows Message Handler”菜單項(xiàng)。

在第二部分,我將全面介紹基本的WTL窗口類和一個(gè)更好的消息映射宏。

posted on 2007-03-09 11:46 jay 閱讀(420) 評(píng)論(0)  編輯 收藏 引用 所屬分類: WTL

只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            免费一级欧美在线大片| 欧美多人爱爱视频网站| 精品二区视频| 国内一区二区三区| 国产日韩欧美在线看| 国产午夜精品美女视频明星a级| 国产精品日韩专区| 国产日韩欧美一区二区三区在线观看| 国产啪精品视频| 精品福利av| 在线视频欧美日韩| 久久成人精品无人区| 久久在线播放| 亚洲大片在线| 亚洲激情网址| 亚洲午夜91| 久久久999| 欧美激情一区二区| 国产欧美另类| 亚洲精品一品区二品区三品区| 亚洲精品在线观看视频| 亚洲一二区在线| 久久这里只有| av成人国产| 久久久噜噜噜久久中文字幕色伊伊| 久久婷婷蜜乳一本欲蜜臀| 欧美伦理视频网站| 国产亚洲精品激情久久| 日韩视频不卡| 久久精品亚洲精品| 亚洲精品一区二区三区蜜桃久| 亚洲欧美网站| 欧美乱大交xxxxx| 在线观看亚洲视频| 亚洲欧美国产另类| 亚洲国产高清视频| 欧美在线免费观看亚洲| 欧美日韩国产一区二区| 在线日韩欧美| 久久久久久自在自线| 一本色道综合亚洲| 模特精品在线| 伊人久久综合97精品| 欧美一区二区在线看| 一本一本久久a久久精品综合麻豆| 久热精品在线视频| 国语自产精品视频在线看8查询8 | 激情小说另类小说亚洲欧美| 亚洲视频成人| 亚洲欧洲三级| 亚洲免费中文| 国产精品夜夜夜一区二区三区尤| 亚洲精品乱码久久久久久日本蜜臀| 欧美在线啊v| 亚洲欧美激情四射在线日| 欧美视频在线一区| 在线视频日韩| 日韩西西人体444www| 欧美精品在线一区二区三区| 亚洲黄色免费电影| 欧美本精品男人aⅴ天堂| 欧美资源在线观看| 国内精品国语自产拍在线观看| 欧美一区亚洲二区| 亚洲欧美日韩另类精品一区二区三区| 国产精品久久国产精麻豆99网站| aa亚洲婷婷| 在线亚洲免费| 国产欧美日韩在线| 久久久久久久高潮| 久久久精品国产免费观看同学| 一区在线视频| 亚洲国产精品v| 欧美视频日韩| 久久都是精品| 久久在线免费观看| 99精品热视频| 国产精品99久久久久久有的能看| 国产精品热久久久久夜色精品三区 | 亚洲视频在线观看网站| 国产精品成人免费| 欧美在线观看视频一区二区三区 | 欧美一区二区三区四区夜夜大片| 国产日韩精品一区| 免费观看成人鲁鲁鲁鲁鲁视频 | 欧美成人免费一级人片100| 亚洲精品专区| 亚洲字幕在线观看| 在线观看久久av| 亚洲激情网址| 国产亚洲欧美一区在线观看| 母乳一区在线观看| 欧美另类在线播放| 久久精品五月| 欧美日韩一区二区视频在线| 久久久久久婷| 国产精品igao视频网网址不卡日韩| 久久精品国产2020观看福利| 欧美黑人在线播放| 久久精品av麻豆的观看方式| 欧美高清不卡| 久久久久一区二区三区| 欧美日韩精品综合| 久久香蕉国产线看观看网| 欧美成熟视频| 另类综合日韩欧美亚洲| 国产精品高潮在线| 国产一区二区三区免费观看| 欧美高清一区| 国产精品美女久久久免费| 免费看亚洲片| 国产酒店精品激情| 亚洲国产精品久久人人爱蜜臀 | 在线成人激情视频| 国产精品99久久久久久人| 亚洲激情网站| 久久裸体艺术| 久久不射网站| 欧美性片在线观看| 亚洲人成网站999久久久综合| 精东粉嫩av免费一区二区三区| 国产精品99久久久久久白浆小说| 亚洲精品一区二区三区在线观看| 久久久久久国产精品一区| 亚洲欧美精品suv| 欧美日产国产成人免费图片| 欧美成人午夜77777| 韩日午夜在线资源一区二区| 香蕉av777xxx色综合一区| 亚洲一区欧美激情| 欧美三级电影精品| 最新国产成人av网站网址麻豆| 亚洲国产乱码最新视频 | 国产嫩草影院久久久久 | 欧美激情第二页| 欧美v日韩v国产v| 精品96久久久久久中文字幕无| 亚洲男人av电影| 亚洲男人的天堂在线| 欧美性开放视频| 999亚洲国产精| 亚洲影院在线观看| 国产精品伦一区| 午夜精品电影| 久久精品国产77777蜜臀| 国产欧美日韩免费看aⅴ视频| 亚洲欧美日韩精品久久久久| 久久久综合视频| 亚洲福利视频网| 免费看黄裸体一级大秀欧美| 亚洲人成啪啪网站| 在线视频你懂得一区二区三区| 国产精品美女久久福利网站| 欧美一级在线播放| 欧美成人午夜影院| 中文亚洲免费| 国产日韩欧美在线播放不卡| 久久一区二区精品| 亚洲精品美女91| 性娇小13――14欧美| 国模一区二区三区| 免费精品99久久国产综合精品| 亚洲精选中文字幕| 欧美一区二区三区在线| 精品成人乱色一区二区| 欧美激情国产日韩| 亚洲影院免费| 亚洲福利小视频| 久久视频这里只有精品| 影音先锋中文字幕一区| 欧美二区在线看| 亚洲男同1069视频| 免费亚洲电影| 亚洲视频欧美视频| 黄色综合网站| 欧美日韩一本到| 久久免费偷拍视频| 宅男噜噜噜66国产日韩在线观看| 久久久久久有精品国产| 亚洲夜间福利| 亚洲激情欧美激情| 国产一区二区三区日韩| 欧美男人的天堂| 久久久久一区| 亚洲综合电影| 亚洲精品在线一区二区| 裸体女人亚洲精品一区| 亚洲免费视频中文字幕| 亚洲精品网址在线观看| 国产最新精品精品你懂的| 欧美日韩一区在线| 老妇喷水一区二区三区| 小嫩嫩精品导航| 99这里只有精品| 亚洲黄色av一区| 欧美高清不卡| 另类专区欧美制服同性| 性娇小13――14欧美| 午夜激情久久久| 亚洲一区二区高清视频|