有過去一個周末了,昨天去看跳水比賽,現場的氣氛還是不錯的。可惜田亮沒有來,否則,光看看觀眾席的fans也是一種享受啊。
廢話結束,進入正題,今天講點以前沒說清楚的內容。上次提到了訊息反射,但沒有深入,這個概念是這樣的,許多視窗控制項會向父視窗發送一些訊息,比如WM_COMMAND訊息和WM_NOTIFY訊息,通知父視窗一些事件。因為是發給父視窗的,所以控制項視窗的過程函數不能捕捉到這些訊息。但是,經常我們希望在控制項視窗對象中處理這些訊息,這樣使控制項類更加獨立。為了實現這個目的,MFC和ATL都提供了訊息反射的機制,就是讓父視窗在收到這類訊息的時候,把它們再發還給控制項視窗,這就是訊息反射。我們要實現Skin外掛程式,也需要在控制項視窗類別中收到這些訊息,但是,我們不能依賴ATL或者MFC的反射,因為我們希望Skin外掛程式可以被不同的宿主程式使用,而不是局限於ATL和MFC。其他程式可能沒有訊息反射機制,或者使用了不同的訊息反射機制。所以,我們實現了自己的訊息反射機制。
CReflectHook類就是用來完成訊息反射的,其建構函式接受父視窗的控制代碼作為參數,然後調用SubclassWindow把對象執行個體連結到視窗上去,這和控制項的實作類別似。ProcessWindowMessage是個虛函數,它的定義可以追述到ATL視窗類別的最底層,CReflectHook::ProcessWindowMessage實現反射功能,把收到的需要反射的訊息發送回控制項視窗。但也不是簡單的原樣返回,而是封裝成另一個WM_REFLECT訊息,以免和其他訊息衝突。當反射訊息發會給控制項視窗時,控制項視窗利用下面三個宏解開WM_REFLECT,並得到原來的訊息:
LF_REFLECTED_NOTIFY_CODE_HANDLER
LF_REFLECTED_COMMAND_CODE_HANDLER
LF_REFLECTED_MESSAGE_HANDLER
這幾個宏的含義不多說了,熟悉WTL的同學很容易找到答案。另外值得一提的時WM_GETREFLECTOR訊息,這個自訂訊息可以用來向父視窗查詢於其相關聯的CReflectHook執行個體,以避免重複安裝反射鉤子,因為CReflectHook::ProcessWindowMessage為這個訊息返回了this指標。CWidgetHook::Install使用了WM_GETREFLECTOR訊息。
值得講的就這麼多了吧,最後說一下怎麼寫CWidgetHook繼承類吧(即控制項類),這也是直接影響最終效果的。WTL定義了許多通用控制項封裝類,把這些類作為CWidgetHook的第二個模板參數可以是後續工作大大簡化,當然,如果沒有對應的封裝類,也可以接收預設參數。
因為藉助了ATL/WTL的基礎架構,控制項類的編寫和寫一個ATL視窗類別非常相似,可以使用ATL/WTL訊息映射宏,當然,這些宏需要手工輸入,而不象MFC一樣提供了Wizard。另外,控制項類還可以選擇的重載CWidgetHook定義的4個初始化和清理函數,參考Skin技術實現架構(五)。附帶的例子中提供了一個Mac按鈕類的實現,可以參考,這個按鈕例子算是比較複雜的,因為Windows視窗類別名為“Button”的視窗實際上包括普通按鈕、單選鈕和複選框,其他的許多控制項實現起來比button容易,當然也有一些比較麻煩的。
關於這個skin架構,基本上應該都講清楚了,不過肯定比較亂,也許以後有時間整理吧。這裡使用了很多WTL的東西,可能熟悉的人並不多,而且最終沒有得到微軟官方支援,所以要說有多少價值,也說不上,只是喜歡的朋友可以玩一下。我現在也不怎麼關注C++的東西了,沒辦法,新技術發展太快,不得不跟上啊。隨便寫一點以前的積累,和同學們共勉。