MFC程序員的WTL指南_第1頁
MFC程序員的WTL指南_第2頁
MFC程序員的WTL指南_第3頁
MFC程序員的WTL指南_第4頁
MFC程序員的WTL指南_第5頁
已閱讀5頁,還剩160頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

MICROSOFT

MFC程序員的WTL指南

2008

[鍵入公司地址]

序言3

第一章ATL界面類4

1.1、對本書的總體介紹4

1.2、對這一章的簡單介紹5

1.2.1、ATL背景知識ATL和WTL的發(fā)展歷史5

1.2.2、ATL風格模板6

1.3、ATL窗口類8

1.3.1,定義一個窗口的實現(xiàn)8

1.3.2、填寫消息映射鏈9

1.3.3、高級消息映射鏈和嵌入類11

1.3.4、ATL程序的結構13

1.3.5、ATL中的對話框15

第二章WTL界面基類18

2.1、WTL總體印象18

2.2、開始寫WTL程序18

2.2.1、WTL對消息映射的增強20

2.2.2、從WTL的應用程序生成向導能得到什么24

第三章工具條與狀態(tài)條35

3.1、主窗口的工具條和狀態(tài)條35

第四章對話框與控件48

第五章高級對話框用戶界面類67

第六章包容ActiveX控件82

第七章分隔窗口96

第八章屬性頁與向導112

第九章GDI類,通用對話框,初始化類127

9.1、GDI封裝類127

9.1.1、封裝類的通用函數(shù)128

9.2.3、與MFC封裝類的不同之處130

9.3、資源裝載(Resource-Loading)函數(shù)130

9.4、使用通用對話框133

9.4.1>CFileDialog類134

9.4.2、CFolderDialog類136

9.5、其它有用的類和全局函數(shù)138

9.5.1、對結構的封裝138

9.5.2、處理雙類型參數(shù)的類138

9.6、其它工具類138

9.7、全局函數(shù)140

9.8、宏142

9.9、例子工程143

第十章支持拖放操作146

序言

我一直在尋找這樣一個類庫:他對Windows的窗口提供面向對象的封裝,有靈活的消息響應機

制和比較完備的界面框架解決方案,對標準控件提供簡練實用的封裝,支持操作系統(tǒng)的新特性,支

持功能擴充和二次開發(fā),有代碼自動生成向導機制,生成的程序使用較少的系統(tǒng)資源,最后是有完

全的代碼支持和文檔支持。

你會說那就用MFC吧!

是的,我一直使用MFC,但我對MFC已經(jīng)越來越厭倦了。陳舊的類庫使得它無法支持操作系統(tǒng)

的新特性(MFC的類庫從4.21版之后就沒有更新了,而那時是1998年,人們使用Windows95和

windowsNT4),臃腫的消息映射機制和為了兼容性而保留下來的代碼使得程序效率低下,面面俱到

的框架結構使得生成的應用程序龐大并占用過多的系統(tǒng)資源。當一個功能簡單的程序使用動態(tài)鏈接

也超過200K,占用3%-4%的系統(tǒng)資源時,我決定放棄MFC,尋找一個新的功能類似的類庫。我研

究過很多類似的代碼,不是過于簡單,無法用于應用程序的開發(fā),就是缺乏代碼和文檔的支持。在

CodeProject上有一個名為Class的類庫,我也研究過它的代碼,具備了基本的界面框架,對控件也有

了簡單的封裝,但是不實用,龐大的虛函數(shù)機制使得對象非常臃腫,無法減少對資源的占用。我甚

至仿照MFC做了一個簡單的類庫miniGUL形成了基本的框架解決方案,但是最后放棄了,原因很

簡單:無法用于應用程序的開發(fā)。-個應用程序界面框架錯綜復雜,要考慮的事情太多,開發(fā)者不

可能在應用程序和界面框架兩線作戰(zhàn)。就在我即將絕望的時候,我遇到了WTL。

由于工作的需要經(jīng)常開發(fā)一些COM組件,在要求不能使用MFC的場合就是用ATL。ATL提供

了對窗口的面向對象地封裝和簡單的消息映射機制,但是ATL過于簡單,用它開發(fā)應用程序幾乎不

可能。要想讓ATL具備界面框架解決方案的功能還需要做很多事情,幸運的是WTL就做了這些事

情。WTL是個很奇特的東西,它由微軟公司一群熱情的程序員維護,它從未出現(xiàn)在微軟的官方產(chǎn)品

名單上,但可以從微軟的官方網(wǎng)站下載最新的WTL。它沒有正式的文檔支持,用WTL做關鍵字在

MSDN中檢索只能得到0個結果,但是全世界的開發(fā)網(wǎng)站上都有針對WTL的討論組和郵件列表,任

何問題都會得到熱情的解答。我認真地對比了MFC和WTL,發(fā)現(xiàn)二者有很多相通之處,MFC的功

能幾乎都能在WTL中實現(xiàn),只是方法不同而已。我?guī)缀醪毁M吹灰之力就將以前寫的一個MFC程序

用WTL改寫了,使用靜態(tài)鏈接的WTL程序比使用動態(tài)鏈接的MFC程序還要小,資源占用只有MFC

程序的一半。

但是一時的熱情不能解決文檔缺乏的困擾,雖然網(wǎng)上有很多使用WTL的例子和說明文章,兒乎

把MFC能實現(xiàn)的各種稀奇古怪的效果都實現(xiàn)了。就在這個時候我看到了邁克爾.敦(MichaelDunn)

的“WTLforMFCProgrammers”系列文章,我的感覺和1995年我第一次見到MSDN時一樣,幾乎是

迫不及待地將其讀完,同時也萌發(fā)了將其翻譯成漢語的沖動。于是給Michael寫了封郵件,希望能夠

得到授權將他的文章翻譯成漢語(事實上在這之前我已經(jīng)翻譯了兩章了)。在得到授權確認后才發(fā)現(xiàn)這

個工作是多么的困難,但為時已晚,只能硬著頭皮撐下去。

第一章ATL界面類

你需要開發(fā)平臺SDK。你要使用WTL不能沒有它,你可以使用在線升級安裝開發(fā)平臺SDK,

也可以下載全部文件后在本地安裝。在使用之前要將SDK的包含文件(.h頭文件)和庫文件(.Lib

文件)路徑添加到VC的搜索目錄,SDK有現(xiàn)成的工具完成這個工作,這個工具位于開發(fā)平臺SDK

程序組的“VisualStudioRegistration”文件夾里(這是針對VC++6.0的)。

你需要安裝WTL。你可以從微軟的網(wǎng)站上下載WTL的8.()版,在安裝之前可以先查看“Intro

ductiontoWTL-Part1”和“EasyinstallationofWTL”這兩篇文章,了解一下所要安裝的文件的信息,

雖然現(xiàn)在這些文章有些過時,但還是可以提供很多有用的信息。有一件我認為不該在本篇文章中提

到的事是告訴VC如何搜索WTL的包含文件路徑,如果你用的VC6,用鼠標點擊Tools\Options,轉

到Directories標簽頁,在顯示路徑的列表框中選擇IncludeFiles,然后將WTL的包含文件的存放路

徑添加到包含文件搜索路徑列表中。

你需要了解MFC。很好地了解MFC將有助于你理解后面提到的有關消息映射的宏并能夠編輯那

些標有“不要編輯(DONOTEDIT)”的代碼而不會出現(xiàn)問題。

你需要清楚地知道如何使用Win32API編程。如果你是直接從MFC開始學習Windows編程,

沒有學過API級別的消息處理方式,那很不幸你會在使用WTL時遇到麻煩。如果不了解Windows

消息中WPARAM參數(shù)和LPARAM參數(shù)的意義,則需要讀一些這方面的文章(在CodeProject有大

量的此類文章)。

你需要知道C++模板的語法,你可以到VCForumFAQ相關的連接尋求答案。

我只是討論了一些涵蓋VC6的特點,不過據(jù)我了解所有的程序都可以在VC7上使用。由于我

不使用VC7,我無法對那些在VC7中出現(xiàn)的問題提供幫助,不過你還是可以放心的在此張貼你的

問題,因為其他的人可能會幫助你。

1.1、對本書的總體介紹

WTL具有兩面性,確實是這樣的。雖然它沒有MFC的界面類庫那樣強大的功能,但是能夠生

成很小的應用程序。如果你象我一樣使用MFC進行界面編程,你會覺得MFC提供的界面控件封裝

類使用起來非常舒服,更不用說MFC內置的消息處理機制。當然,如果你也象我■?樣不希望自己的

程序僅僅因為使用了MFC的框架就增加兒百K的大小的話,WTL就是你的選擇。當然,我們還要

克服一些障礙:

1>ATL樣式的模板類初看起來有點怪異:

2>沒有類向導的支持,所以要手工處理所有的消息映射(名叫VisualFC的第三方插件可以提供

相應向導);

3>MSDN沒有正式的文檔支持,你需要到處去收集有關的文檔,甚至是查看WTL的源代碼;

4>買不到參考書籍;

5>沒有微軟的官方支持;

6>ATL/WTL的窗口與MFC的窗口有很大的不同,你所了解的有關MFC的知識并不完全適用于

WTLo

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

1>不需要學習或掌握復雜的文檔/視圖框架;

2>具有MFC的基本的界面特色,比如DDX/DDV和命令狀態(tài)UI更新功能(比如菜單的Check

標記和Enable標記等);

3>增強了一些MFC的特性(比如更加易用的分隔窗口);

4>可生成比靜態(tài)鏈接的MFC程序更小的應用程序(WTL的所有源代碼都是靜態(tài)鏈接到你的程序

中的,除了CRT之類);

5>你可以修正自己使用的WTL中的BUG,而不會影響其他的應用程序(相比之下,如果你修正

了有BUG的MFC/CRT動態(tài)庫就可能會引起其它應用程序的崩潰;

6>如果你仍然需要使用MFC,MFC的窗口和ATL/WTL的窗口可以“和平共處”。

在本文中,我將首先介紹ATL的窗口類,畢竟WTL是構建于ATL之上的,并附加了一系列類,

所以需要很好地了解ATL的窗口類。介紹完ATL之后我將介紹WTL的特性,并展示它是如何使界

面編程變得輕而易舉。

1.2、對這一章的簡單介紹

WTL是個很酷的工具,在理解這一點之前需要首先介紹ATL。WTL是構建與ATL之上的,并

附加了一系列類。如果你是個使用MFC的程序員,你可能沒有機會接觸到ATL的界面類,所以請容

忍我在開始WTL之前先羅索一些別的東西,繞道來介紹一下ATL是很有必要地。

在本文的第一部分,我將給出一點ATL的背景知識,包括一些編寫ATL代碼所必須知道的基礎

知識,快速地解釋一些令人不知所措的ATL模板類和基本的ATL窗口類。

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

ATL”活動模板庫”是一個很古怪的名字。那些年紀大的人可能還記得,它最初被稱為“網(wǎng)絡組件

模板庫”,這可能是它更準確的稱呼,因為ATL的目的就是使編寫組件對象和ActiveX控件更容易一

些(ATL是在微軟開發(fā)新產(chǎn)品ActiveX-某某的過程中開發(fā)的,那些ActiveX-某某現(xiàn)在被稱為某某.

NET)o由于ATL只是為了便于編寫組件對象而存在的,所以僅提供了簡單的界面類,相當于MFC

的窗口類(CWnd)和對話框類(CDialog)。幸運地是,這些類非常的靈活,能夠在其基礎上構建象

WTL這樣的附加類。

WTL現(xiàn)在已經(jīng)是第三次修正版了,最初的版本是3.1,現(xiàn)在的最新版本是&0(WTL的版本號之

所以這樣選擇,是為了與ATL的版本匹配,所以不存在1和2這樣的版本號)。WTL3.1可以與VC

6和VC9一起使用,但是在VC9下需要定義幾個預處理標號。WTL8向下兼容WTL3.1,并且不

作任何修改就可以與VC9一起使用,現(xiàn)在看來沒有任何理由還使用3.1來進行新的開發(fā)工作。

1.2.2、ATL風格模板

即使你能夠毫不費力地閱讀C++的模板類代碼,仍然有兩件事可能會使你有些頭暈,以下面這

個類的定義為例:

classCMyWnd:publicCWindowlmpl<CMyWnd>

};

這樣作是合法的,因為C++的語法解釋說即使CMyWnd類只是被部分定義,類名CMyWnd已

經(jīng)被列入遞歸繼承列表,是可以使用的。將類名作為模板類的參數(shù)是因為ATL要做另一件詭秘的事

情,那就是編譯期間的虛函數(shù)調用機制。

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

template<classT>

classBl

(

public:

voidSayHi()

(

T*pT=static_cast<T*>(this);//HUH??我將在卜面解釋

pT->PrintClassName();

)

protected:

voidPrintClassName(){cout?"ThisisBl";}

);

classDI:publicB1<D1>

(

//Nooverriddenfunctionsatall

};

classD2:publicB1<D2>

(

protected:

voidPrintClassName(){cout?"ThisisD2";}

};

main()

(

Dldl;

D2d2;

dl.SayHi();//打印"ThisisBl"

d2.SayHi();〃打印"ThisisD2"

這句代碼static_cast<T*>(this)就是竅門所在。它根據(jù)函數(shù)調用時的特殊處理將指向B1類型的

指針由is指派為D1或D2類型的指針,因為模板代碼是在編譯其間生成的,所以只要編譯器生成正

確的繼承列表,這樣指派就是安全的。如果你寫成:

classD3:publicB1<D2>

就會有麻煩。之所以安全是因為this對象只可能是指向D1或D2(在某些情況下)類型的對象,

不會是其他的東西。注意這很像C++的多態(tài)性,只是SayHi()方法不是虛函數(shù)。

要解釋這是如何工作的,首先看對每個SayHi()函數(shù)的調用,在第一個函數(shù)調用,對象B1被指

派為D1,所以代碼被解釋成:

voidBl<Dl>::SayHi()

(

DI*pT=static_cast<Dl*>(this);

pT->PrintClassName();

由于Dl沒有重載PrintClassNameO,所以查看基類Bl,Bl有PrintClassName(),所以Bl的Print

ClassName。被調用。

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

voidBl<D2>::SayHi()

(

D2*pT=static_cast<D2*>(this);

pT->PrintClassName();

這一次,D2含有PrintClassNameO方法,所以D2的PrintClassNameO方法被調用。

這種技術的有利之處在于:

1>不需要使用指向對象的指針;

2>節(jié)省內存,因為不需要虛函數(shù)表;

3>因為沒有虛函數(shù)表所以不會發(fā)生在運行時調用空指針指向的虛函數(shù);

4>所有的函數(shù)調用在編譯時確定(區(qū)別于C++的虛函數(shù)機制使用的動態(tài)鏈接),有利于編譯程序

對代碼的優(yōu)化。

節(jié)省虛函數(shù)表在這個例子中看起來無足輕重(每個虛函數(shù)只有4個字節(jié)),但是設想一下如果有

15個基類,每個類含有20個方法,加起來就相當可觀了。

1.3、ATL窗口類

好了,關于ATL的背景知識已經(jīng)講的夠多了,該正式講ATL的了。ATL在設計時,接口定義和

實現(xiàn)是嚴格區(qū)分開的,這在窗口類的設計中最為明顯。這一點類似于COM,COM的接口定義和實

現(xiàn)是完全分開的(或者可能有多個實現(xiàn))。

ATL有一個專門為窗口設計的類,可以做全部的窗口操作,這就是CWindow。它實際上就是對

HWND操作的包裝類,對幾乎所有以HWND句柄為第一個參數(shù)的窗口API的進行了封裝,例如:

SetWindowText()和DestroyWindow()oCWindow類有一個公有成員m_hWnd,使你可以直接對窗口

的句柄操作,CWindow還看一個操作符HWND,你可以將CWindow對象傳遞給以HWND為參數(shù)

的函數(shù),但這與CWnd::GetSafeHwnd()(譯者加I:MFC的方法)沒有任何相同之處。

CWindow與MFC的CWnd類有很大的不同,創(chuàng)建一個CWindow對象占用很少的資源,因為

只有一個數(shù)據(jù)成員,沒有MFC窗口中的對象鏈。MFC內部維持這個對象鏈,此對象鏈將HWND映

射到CWnd對象。還有--點與MFC的CWnd類不同的是:當一個CWindow對象超出了作用域,它

所關聯(lián)的窗口并不被銷毀掉,這意味著你并不需要隨時記得分離你所創(chuàng)建的臨時CWindow對象。

在ATL類中對窗口過程的實現(xiàn)是CWindowImpl。CWindowImpl包含有所有窗口實現(xiàn)代碼,例

如:窗口類的注冊,窗口的子類化,消息映射以及基本的WindowProc。函數(shù)。可以看出這與MFC的

設計有很大的不同,MFC將所有的代碼都放在CWnd類中。

另外兩個獨立的類是對話框的實現(xiàn),它們分別是CDialoglmpI和CAxDialoglmpLCDialoglmpl用

于實現(xiàn)普通的對話框,而CAxDialoglmpl實現(xiàn)含有ActiveX控件的對話框。

1.3.1、定義一個窗口的實現(xiàn)

任何非對話框窗口都是從CWindowImpl派生的,你的新類需要實現(xiàn)三樣東西:

1>定義窗口類(通過DECLARE_WND_CLASS或DECLARE_WND_CLASS_EX宏實現(xiàn));

2>生成消息映射鏈(通過BEGIN_MSG_MAP和END_MSG_MAP宏實現(xiàn));

3>窗口使用的默認窗口類型(稱為windowtraits)。

窗口類的定義通過DECLARE_WND_CLASS宏或DECLARE_WND_CLASS_EX宏來實現(xiàn)。

這兩個宏定義了一個CWndClassInfo結構,這個結構封裝了WNDCLASSEX結構。DECLARE_WND_

CLASS宏讓你指定窗口類的類名,其他參數(shù)使用默認設置,而DECLARE_WND_CLASS_EX宏還犬

許你指定窗口類的類型和窗口的背景顏色。你也可以用NULL作為類名,ATL會自動為你生成一個

類名。

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

classCMyWindow:publicCWindowlmpl<CMyWindow>

DECLARE_WND_CLASS(_T("MyWindowClass"))

接下來是生成消息映射鏈,ATL的消息映射鏈比MFC簡單得多,ATL的消息映射鏈被展開為

switch語句,switch語句正確的消息處理者并調用相應的函數(shù)。使用消息映射鏈的宏是BEGIN_MSG

_MAP和END_MSG_MAP,讓我們?yōu)槲覀兊拇翱谔砑右粋€空的消息映射鏈。

classCMyWindow:publicCWindowlmpl<CMyWindow>

public:

DECLARE_WND_CLASS<_T("MyWindowClass"))

BEGIN_MSG_MAP(CMyWindow)

END_MSG_MAP()

我將在下一節(jié)展開講如何如何添加消息處理到消息映射鏈。最后,我們需要為我們的窗口類定

義窗口的特征,窗口的特征就是窗口類型和擴展窗口類型的聯(lián)合體,用于創(chuàng)建窗口時指定窗口的類

型。窗口類型被指定為參數(shù)模板,所以窗口的調用者不需要為指定窗口的正確類型而煩心,下面是

是同ATL類CWinTraits定義窗口類型的例子:

TypedefCWinTraits<WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN,WS_EX_APPWINDOW>CMyWindowTraits;

classCMyWindow:publicCWindowlmpl<CMyWindowzCWindow,CMyWindowTraits>

public:

DECLARE_WND_CLASS(_T("MyWindowClass"))

BEGIN_MSG_MAP(CMyWindow)

END_MSG_MAP()

調用者可以重載CMyWindowTraits的類型定義,但是一般情況下這是沒有必要的,ATL提供了

兒個預先定義的特殊的類型,其中之一就是CFrameWinTraits,一個非常棒的框架窗口:

typedefCWinTraits<WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN|WS_CLIPSIBLINGS,

WS_EX_APPWINDOW|WS_EX_WINDOWEDGE>CFrameWinTraits;_________

1.3.2、填寫消息映射鏈

ATL的消息映射鏈是對開發(fā)者不太友好的部分,也是WTL對其改進最大的部分。類向導至少

可以讓你添加消息響應,然而ATL沒有消息相關的宏和象MFC那樣的參數(shù)自動展開功能,在ATL

中只有三種類型的消息處理,一個是WM_NOTIFY,一個是WM_COMMAND,第三類是其他窗口

消息宏MESSAGE_HANDLER。讓我們開詔為我們的窗口添加WM.CLOSE和WM_DESTROY的消

息處理函數(shù)。

classCMyWindow:publicCWindowlmpl<CMyWindow,CWindow,CFrameWinTraits>

(

public:

DECLARE_WND_CLASS(_T("MyWindowClass"))

BEGIN_MSG_MAP(CMyWindow)

MESSAGE_HANDLER(WM_CLOSE,OnClose)

MESSAGE_HANDLER(WM_DESTROY,OnDestroy)

END_MSG_MAP()

LRESULTOnClose(UINTuMsg,WPARAMwParam,LPARAMIParam,BOOL&bHandled)

(

DestroyWindow();

return0;

)

LRESULTOnDestroy(UINTuMsg,WPARAMwParam,LPARAMIParam,BOOL&bHandled)

(

PostQuitMessage(O);

return0;

}

};

你可能注意到消息響應函數(shù)得到的是原始的WPARAM和LPARAM值,你需要自己將其展開為

相應的消息所需要的參數(shù)。還有第四個參數(shù)bHandled,這個參數(shù)在消息相應函數(shù)調用前被ATL設置

為TRUE,如果在你的消息響應處理完之后需要ATL調用默認的WindowProc。處理該消息,你可以

將bHandled設置為FALSE0這與MFC不同,MFC是顯式地調用基類的響應函數(shù)來實現(xiàn)的默認的消

息處理的。

讓我們再添加一個對WM_COMMAND消息的處理,假設我們的窗口有一個ID為IDC_ABOUT

的About菜單:

classCMyWindow:publicCWindowlmpl<CMyWindow,CWindow,CFrameWinTraits>

(

public:

DECLARE_WND_CLASS(_T("MyWindowClass"))

BEGIN_MSG_MAP(CMyWindow)

MESSAGE_HANDLER(WM_CLOSE,OnClose)

MESSAGE_HANDLER(WM_DESTROY,OnDestroy)

COMMAND_ID_HANDLER(IDC_ABOUT,OnAbout)

END_MSG_MAP()

LRESULTOnClose(UINTuMsg,WPARAMwParam,LPARAMIParam,BOOL&bHandled)

(

DestroyWindow();

return0;

}

LRESULTOnDestroy(UINTuMsg,WPARAMwParam,LPARAMIParam,BOOL&bHandled)

{

PostQuitMessage(O);

return0;

)

LRESULTOnAbout(WORDwNotifyCode,WORDwID,HWNDhWndCtl,BOOL&bHandled)

(

MessageBox(_T(nSampleATLwindow1*),_T("AboutMyWindow'*));

return0;

)

};

需要注意的是COMMAND_HANDLER宏已經(jīng)將WM_COMMAND消息的參數(shù)展開了,同樣,

NOTIFY—HANDLER宏也將WM_NOTIFY消息的參數(shù)展開了。

1.3.3、高級消息映射鏈和嵌入類

ATL的另一個顯著不同之處就是任何C++類都可以響應消息,而MFC只是將消息響應任務分給

TCWnd類和CCmdTarget類,夕卜力口幾個有PreTranslateMessage()方法的類。ATL的這種特性

允許我們編寫所謂的“嵌入類”,為我們的窗口添加特性只需將該類添加到繼承列表中就行了,就這么

簡單!

一個基本的帶有消息映射鏈的類通常是模板類,將派生類的類名作為模板的參數(shù),這樣它就可

以訪問派生類中的成員,比如m_hWnd(CWindow類中的HWND成員)。讓我們來看■?個嵌入類的

例子,這個嵌入類通過響應WM_ERASEBKGND消息來畫窗口的背景。

template<classT,COLORREFt_crBrushColor>

classCPaintBkgnd:publicCMessageMap

(

public:

CPaintBkgnd(){m_hbrBkgnd=CreateSolidBrush(t_crBrushColor);}

~CPaintBkgnd(){DeleteObject(m_hbrBkgnd);}

BEGIN_MSG_MAP(CPaintBkgnd)

MESSAGE_HANDLER(WM_ERASEBKGND,OnEraseBkgnd)

END_MSG_MAP()

LRESULTOnEraseBkgnd(UINTuMsg,WPARAMwParam,LPARAMIParam,BOOL&bHandled)

(

T*pT=static_cast<T*>(this);

HDCde=(HDC)wParam;

RECTrcClient;

pT->GetClientRect(&rcClient);

FillRect(de,&rcClient,m_hbrBkgnd);

return1;//wepaintedthebackground

)

protected:

HBRUSHm_hbrBkgnd;

);

讓我們來研究,一下這個新類。首先,CPaintBkgnd有兩個模板參數(shù):使用CPaintBkgnd的派生類

的名字和用來畫窗口背景的顏色(t_前綴通常用來作為模板類的模板參數(shù)的前綴)。CPaintBkgnd也是

從CMessageMap派生的,這并不是必須的,因為所有需要響應消息的類只需使用BEGIN_MSG_MAP

宏就足夠了,所以你可能看到其他的一些嵌入類的例子代碼,它們并不是從該基類派生的。

構造函數(shù)和析構函數(shù)都相當簡單,只是創(chuàng)建和銷毀Windows畫刷,這個畫刷由參數(shù)t_crBrushColor

決定顏色。接著是消息映射鏈,它響應WM_ERASEBKGND消息,最后由響應函數(shù)OnEraseBkgnd。

使用構造函數(shù)創(chuàng)建的畫刷填充窗口的背景。在OnEraseBkgnd。中有兩件事需要注意:-是它使用了

一個派生的窗口類的方法(即GetClientRectO),我們如何知道派生類中有GetClientRect()方法呢?如

果派生類中沒有這個方法我們的代碼也不會有任何問題,由編譯器確認派生類T是從CWindow派生

的。另一-個是OnEraseBkgnd。沒有將消息參數(shù)wParam展開為設備上下文(DC)。

想要使用這個嵌入類有兩件事需要做:

首先,將它加入到繼承列表:

classCMyWindow:publicCWindowlmpl<CMyWindowzCWindow,CFrameWinTraits>,

_________________publicCPaintBkgndvCMyWindow,RGB(0,0,255)>_______________

其次,需要CMyWindow將消息鏈入CPaintBkgnd類,在CMyWindow的消息映射鏈中加入CHAIN

_MSG_MAP宏:

classCMyWindow:publicCWindowlmpl<CMyWindow,CWindow,CFrameWinTraits>,

publicCPaintBkgnd<CMyWindow,RGB(0/0/255)>

typedefCPaintBkgnd<CMyWindow,RGB(O,O,255)>CPaintBkgndBase;

BEGIN_MSG_MAP(CMyWindow)

MESSAGE_HANDLER(WM_CLOSEZOnClose)

MESSAGE_HANDLER(WM_DESTROY,OnDestroy)

COMMAND_HANDLER(IDC_ABOUT,OnAbout)

CHAIN_MSG_MAP(CPaintBkgndBase)

END_MSG_MAP()

};

任何CMyWindow沒有處理的消息都被傳遞給CPaintBkgndo應該注意的是WM_CLOSE,WM_

DESTROY和IDC_ABOUT消息將不會傳遞,因為這些消息一旦被處理消息映射鏈的查找就會中止。

使用typedef是必要地,因為宏是預處理宏,只能有?個參數(shù),如果我們將CPaintBkgnd<CMyWindow,

RGB(0,0,255)>作為參數(shù)傳遞,那個“,”會使預處理器認為我們使用了多個參數(shù)。

你可以在繼承列表中使用多個嵌入類,每一個嵌入類使用一個CHAIN_MSG_MAP宏,這樣消

息映射鏈就會將消息傳遞給它。這與MFC不同,MFC的CWnd派生類只能有一個基類,MFC自動

將消息傳遞給基類。

1.3.4、ATL程序的結構

到目前為止我們已經(jīng)有了一個完整的主窗口類(即使不完全有用),讓我們看看如何在程序中使

用它。一個ATL程序包含一個CComModule類型的全局變量.Module,這和MFC的程序都有一個

CWinApp類型的全局變量theApp有些類似,唯一,不同的是在ATL中這個變量必須命名為.Module。

下面是stdafx.h文件的開始部分:

//stdafx.h:

#defineSTRICT

#defineVC_EXTRALEAN

#include<atlbase.h>//基本的ATL類

externCComModule_Module;//全局_Module

^include<atlwin.h>//ATL窗口類

allbase.h是包含最基本的Window編程的頭文件,所以我們不需要再包含windows.h,tchar.h之

類的頭文件。在CPP文件中聲明了.Module變量:

//main.cpp:

CComModule_Module;

CComModule含有程序的初始化和關閉函數(shù),需要在WinMain。中顯示的調用。讓我們從這里開

始:

//main.cpp:

CComModule_Module;

intWINAPIWinMain(HINSTANCEhlnst,HINSTANCEhlnstPrev,LPSTRszCmdLine,intnCmdShow)

(

_Module.lnit(NULL,hlnst);

_Module.Term();

}一

Init()的第一個參數(shù)只有COM的服務程序才有用,由于我們的EXE不含有COM對象,我們只

需將NULL傳遞給Init()就行了。ATL不提供自己的WinMain。和類似MFC的消息隊列,所以我們需

要創(chuàng)建CMyWindow對象并添加消息隊列才能使我們的程序運行。

//main.cpp:

#include"MyWindow.h',

CComModule_Module;

intWINAPIWinMain(HINSTANCEhlnst,HINSTANCEhlnstPrev,LPSTRszCmdLine,intnCmdShow)

.Module.lnit(NULL,hlnst);

CMyWindowwndMain;

MSGmsg;

//創(chuàng)建和顯示主窗口

if(NULL==wndMain.Create(NULL,CWindow::rcDefault,_T(nMyFirstATLWindow")))

(

//窗口失敗

return1;

)

wndMain.ShowWindow(nCmdShow);

wndMain.UpdateWindow();

//運行消息循環(huán)

while(GetMessage(&msg7NULL,0,0)>0)

(

TranslateMessage(&msg);

DispatchMessage(&msg);

}

_Module.Term();

returnmsg.wParam;

)

上面的代碼唯一需要說明的是:CWindow::rcDefault,它是CWindow中的成員(靜態(tài)數(shù)據(jù)成員),

數(shù)據(jù)類型是RECT。與調用CreateWindowO時使用CW_USEDEFAULT指定窗口的寬度和高度一樣,

ATL使用rcDefault作為窗口的最初大小。

在ATL代碼內部,ATL使用了一些類似匯編語言的方法將窗口的句柄與相應的窗口對象聯(lián)系起

來。從外部來看,就可以毫無問題的在線程之間傳遞CWindow對象,而MFC的CWnd卻不能這樣

做。圖1就是我們的窗口:

圖1ATL的應用程序

我們得承認這確實沒有什么激動人心的地方。我們將添加一個About菜單并顯示一個對話框,

主要是為它增加??些情趣。

1.3.5、ATL中的對話框

我們在前面提到過,ATL有兩個對話框類,我們的About對話框使用CDialoglmpl。生成一個新

對話框和生成一個主窗口幾乎一樣,只有兩點不同:

1>窗口的基類是CDialoglmpl,而不是CWindowImpl。

2>你需要定義名稱為IDD的公有成員,用來保存對話框的資源ID。

現(xiàn)在開始為About對話框定義一個新類:

classCAboutDIg:publicCDialoglmpl<CAboutDlg>

(

public:

enum{IDD=IDD_ABOUT};

BEGIN_MSG_MAP(CAboutDlg)

END_MSG_MAP()

};

ATL沒有在內部實現(xiàn)對“OK”和“Cancel”兩個按鈕的響應處理,所以我們需要自己添加這些代碼,

如果用戶用鼠標點擊標題欄的關閉按鈕,WM_CLOSE的響應函數(shù)就會被調用。我們還需要處理

WM」NITDIALOG消息,這樣我們就能夠在對話框出現(xiàn)時正確的設置鍵盤焦點,下面是完整的類定

義和消息響應函數(shù)。

classCAboutDIg:publicCDialoglmpl<CAboutDlg>

(

public:

enum{IDD=IDDABOUT};

BEGIN_MSG_MAP(CAboutDlg)

MESSAGE_HANDLER(WM_INITDIALOG,OnlnitDialog)

MESSAGE_HANDLER(WM_CLOSE,OnClose)

COMMAND_ID_HANDLER(IDOK,OnOKCancel)

COMMAND_ID_HANDLER(IDCANCEL,OnOKCancel)

END_MSG_MAP(j

LRESULTOnlnitDialog(UINTuMsg,WPARAMwParam,LPARAMIParam,BOOL&bHandled)

(

CenterWindow();

returnTRUE;//letthesystemsetthefocus

)

LRESULTOnClose(UINTuMsg,WPARAMwParam,LPARAMIParam,BOOL&bHandled)

(

EndDialog(IDCANCEL);

return0;

}

LRESULTOnOKCancel(WORDwNotifyCode,WORDwID,HWNDhWndCtl,BOOL&bHandled)

(

EndDialog(wlD);

return0;

}

};

我使用一個消息響應函數(shù)同時處理“OK”和“Cancel”兩個按鈕的WM_COMMAND消息,因為命

令響應函數(shù)的wID參數(shù)就已經(jīng)指明了消息是來自“OK”按鈕還是來自“Cancel”按鈕。

顯示對話框的方法與MFC相似,創(chuàng)建一個新對話框類的實例,然后調用DoModaK)方法?,F(xiàn)在

我們回到主窗口,添加一個帶有About菜單項的菜單用來顯示我們的對話框,這需要再添加兩個消

息響應函數(shù),一個是響應WM_CREATE,另一個是響應菜單的IDC_ABOUT命令。

classCMyWindow:publicCWindowlmpl<CMyWindowzCWindow,CFrameWinTraits^

publicCPaintBkgnd<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()

LRESULTOnCreate(UINTuMsg,WPARAMwParam,LPARAMIParam,BOOL&bHandled)

(

HMENUhmenu=LoadMenu(_Module.GetResourcelnstance(),MAKEINTRESOURCE(IDR_MENU1));

SetMenu(hmenu);

return0;

}

LRESULTOnAbout(WORDwNotifyCode,WORDwID,HWNDhWndCtl,BOOL&bHandled)

(

CAboutDIgdig;

dlg.DoModal();

return0;

)

II...

};

在指定對話框的父窗口的方式上有些不同,MFC是通過構造函數(shù)將父窗口的指針傳遞給對話框,

而在ATL中是將父窗口的指針作為DoModal()方法的第一個參數(shù)傳遞給對話框的,如果象上面的代

碼一樣沒有指定父窗口,ATL會使用GetActiveWindow。得到的窗口(也就是我們的主框架窗口)作

為對話框的父窗口。

對LoadMenu。方法的調用展示了CComModule的另一個方法一GetResourcelnstance。,它返回

你的EXE的HINSTANCE實例,和MFC的AfxGetResourceHandle。方法相似。(當然還有CCom

Module::GetModuleInstance(),它相當于MFC的AfxGetInstanceHandle()o)

圖2就是主窗口和對話框的顯示效果:

圖2帶對話框的應用程序主窗口

第二章WTL界面基類

在這一部分我們講的內容包括生成一個基本的主窗口和WTL提供的一些友好改進,比如UI界

面的更新(如菜單上的選擇標記)和更好的消息映射機制。為了更好地掌握本章的內容,你應該安

裝WTL并將WTL庫的頭文件目錄添加到VC的搜索目錄中,還要將WTL的應用程序生成向導復制

到正確的位置。WTL的發(fā)布版本中有文檔具體介紹如何做這些設置,如果遇到困難可以查看這些文

檔。

2.1、WTL總體印象

WTL的類大致可以分為幾種類型:

1>主框架窗口的實現(xiàn)-CFrameWindowImpl,CMDIFrameWindowImpl

2>控件的封裝-CButton,CListViewCtrl

3>GDI對象的封裝-CDC,CMenu

4>一些特殊的界面特性-CSplitterWindow,CUpdateUI,CDialogResize,CCustomDraw

5>實用的工具類和宏-CString,CRect,BEGIN_MSG_MAP_EX

本篇文章將深入地介紹框架窗口類,還將簡要地講一下有關的界面特性類和工具類,界面特性

類和工具類中絕大多數(shù)都是獨立的類,盡管有一些是嵌入類,例如:CDialogResizeo

2.2、開始寫WTL程序

如果你沒有用WTL的應用程序生成向導也沒關系(我將在后面介紹這個向導的用法),WTL的程

序的代碼結構很像ATL的程序,本章使用的例子代碼有別于第一章的,主要是為了顯示W(wǎng)TL的特性,

沒有什么實用價值。

這一節(jié)我們將在WTL生成的代碼基礎上添加代碼,生成一個新的程序,程序主窗口的客戶區(qū)顯

示當前的時間。stdafx.h的代碼如下:

^defineSTRICT

#defineWIN32_LEAN_AND_MEAN

ttdefineWTLUSECSTRING

^include<atlbase.h>//基本的ATL類

#include<atlapp.h>//基本的WTL類

externCAppModule_Module;//WTL派生的CComModule版本

#include<atlwin.h>//ATL窗口類

^include<atlframe.h>//WTL主框架窗口類

include<atlmisc.h>//WTL實用工具類,例如:CString

#include<atlcrack.h>//WTL增強的消息宏

atlapp.h是你的工程中第一個包含的頭文件,這個文件內定義了有關消息處理的類和CApp

Module,CAppModule是從CComModuIe派生的類。如果你打算使用CString類,你需要手工定義_WTL

_USE_CSTRING標號,因為CString類是在atlmisc.h中定義的,而許多包含在atlmisc.h之前的頭

受件.會用至UCString,定義_WTL_USE_CSTRING之后,atlapp.h就會向前聲明CString類,其他的

頭文件就知道CString類的存在,從而避免編譯器為此報錯。

接下來定義框架窗口。我們的SDI窗口是從CFrameWindowImpl派生的,在定義窗口類時使用

DECLARE_FRAME_WND_CLASS代替前面使用的DECLARE_WND_CLASS。下面時MyWindow.h

中窗口定義M開始部務:

classCMyWindow:publicCFrameWindowlmpl<CMyWindow>

(

public:

DECLARE_FRAME_WND_CLASS(_T("FirstWTLwindow"),IDR_MAINFRAME);

BEGIN_MSG_MAP(CMyWindow)

CHAIN_MSG_MAP(CFrameWindowlmpl<CMyWindow>)

END_MSG_MAP()

};

DECLARE_FRAME_WND_CLASS有兩個參數(shù),窗口類名(類名可以是NULL,ATL會替你生

成一個類名)和資源ID,初建窗口時WTL用這個ID裝載圖標,菜單和快捷鍵表。我們還要象CFrame

Windowlmpl中的消息處理(例如WM_SIZE和WM_DESTROY消息)那樣將消息鏈入窗口的消息中。

現(xiàn)在來看看WinMain。函數(shù),它和第一部分中的例子代碼中的WinMain。函數(shù)兒乎一樣,只是創(chuàng)

建窗口部分的代碼略微不同。

//main.cpp:

include"stdafx.h"

#include"MyWindow.h"

CAppModule_Module;

intAPIENTRYWinMain(HINSTANCEhlnst,HINSTANCEhPrevlnstzLPSTRIpCmdLine,intnCmdShow)

(

_Module.lnit(NULL,hlnstance);

CMyWindowwndMain;

MSGmsg;

//創(chuàng)建主窗口

if(NULL==wndMain.CreateEx())

return1;//Windowcreationfailed

//顯示窗口

wndMain.ShowWindow(nCmdShow);

wndMain.UpdateWindow();

//標準Win32消息循環(huán)

while(GetMessage(&msg,NULL,0,0)>0)

TranslateMessage(&msg);

DispatchMessage(&msg);

)

_Module.Term();

returnmsg.wParam;

}

CFrameWindowImpl中的CreateEx。函數(shù)的參數(shù)使用了常用的默認值,所以我們不需要特別指定

任何參數(shù)。正如前面介紹的,CFrameWindowImpl會處理資源的裝載,你只需要使用IDR_MAIN

FRAME作為ID定義你的資源就行了(譯者注:主要是圖標,菜單和快捷鍵表),你也可以直接使用

本章的例子代碼。

如果你現(xiàn)在就運行程序,你會看到主框架窗口,事實上它沒有做任何事情。我們需要手工添加

一些消息處理,所以現(xiàn)在是介紹WTL的消息映射宏的最佳時間。

2.2.1、WTL對消息映射的增強

將Win32Api通過消息傳遞過來的WPARAM和LPARAM數(shù)據(jù)分解出來是一件麻煩的事情,并

且很容易出錯,不幸得是ATL并沒有為我們提供更多的幫助,我們仍然需要從消息中分解這些數(shù)據(jù),

當然WM_COMMAND和WM_NOTIFY消息除外。但是WTL的出現(xiàn)拯救了這一切!

WTL的增強消息映射宏定義在atlcrack.h(這個名字來源于“消息解密者”,是一個與windowsx.h

的宏所使用的相同術語)中。首先將BEGIN_MSG_MAP改為BEGIN_MSG_MAP_EX,帶_EX的版

本產(chǎn)生“解密”消息的代碼。

classCMyWindow:publicCFrameWindowlmpl<CMyWindow>

(

public:

BEGIN_MSG_MAP_EX(CMyWindow)

CHAIN_MSG_MAP(CFrameWindowlmpl<CMyWindow>)

END_MSG_MAP()

};

對于我們的時鐘程序,我們需要處理WM_CREATE消息來設置定時器,WTL的消息處理使用

MSG_作為前綴,后面是消息名稱,例如MSG_WM_CREATE?這些宏只是代表消息響應處理的名稱,

現(xiàn)在我們來添加對WM_CREATE消息的響應函數(shù):

classCMyWindow:publicCFrameWindowlmpl<CMyWindow>

(

public:

BEGIN_MSG_MAP_EX(CMyWindow)

MSG_WM_CREA

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論