本文所談及的技術內容都來自於Internet的公開信息。由CKER在閒暇之際整理後,貼出來以飴網友,姑且妄稱原創。
『每次在國外網站上找到精彩文章的時候,心中都會暗自歎息為什麼在中文網站難以覓得這類文章呢?其實原因大家都明白。』
時至今日,學習Windows的兄弟們都知道消息機制的重要性。所以理解消息機制也成了不可或缺的功課。
大家都知道,Borland的C++ Builder以及Delphi的核心是VCL。作為Win32平台上的開發工具,封裝Windows的消息機制當然也是必不可少的。
那麼,在C++ Builder中處理消息的方法有哪些呢?它們之間的區別又在哪裡?如果您很清楚這些,呵呵,對不起啦,請關掉這個窗口。
如果不清楚那就和我一起深入VCL的源碼看個究竟吧。『注:BCB只有Professional和Enterprise版本才帶有VCL源碼。當然,大伙的版本都有源碼的。我沒猜錯吧 :-)<CKER用的是BCB5>』
方法1。使用消息映射(Message Map)重載TObject的Dispatch虛成員函數
這個方法大家用的很多。形式如下
BEGIN_MESSAGE_MAP
VCL_MESSAGE_HANDLER( … …)
END_MESSAGE_MAP( …)
但這幾句話實在太突兀,C++標准中沒有這樣的定義。不用講,這顯然又是宏定義。它們到底怎麼來的呢?CKER第一次見到它們的時候,百思不得其解。嘿嘿,不深入VCL,怎麼可能理解?
在\Borland\CBuilder5\Include\Vcl找到sysmac.h,其中有如下的預編譯宏定義:
#define BEGIN_MESSAGE_MAP virtual void __fastcall Dispatch(void *Message) \
{ \
switch (((PMessage)Message)->Msg) \
{
#define VCL_MESSAGE_HANDLER(msg,type,meth) \
case msg: \
meth(*((type *)Message)); \
break;
// NOTE: ATL defines a MESSAGE_HANDLER macro which conflicts with VCL's macro. The
// VCL macro has been renamed to VCL_MESSAGE_HANDLER. If you are not using ATL,
// MESSAGE_HANDLER is defined as in previous versions of BCB.
file://
#if !defined(USING_ATL) && !defined(USING_ATLVCL) && !defined(INC_ATL_HEADERS)
#define MESSAGE_HANDLER VCL_MESSAGE_HANDLER
#endif // ATL_COMPAT
#define END_MESSAGE_MAP(base)
default: \
base::Dispatch(Message); \
break; \
} \
}
這樣對如下的例子:
BEGIN_MESSAGE_MAP
VCL_MESSAGE_HANDLER(WM_PAINT,TMessage,OnPaint)
END_MESSAGE_MAP(TForm1)
在預編譯時,就被展開成如下的代碼
virtual void __fastcall Dispatch(void *Message)
{
switch (((PMessage)Message)->Msg)
{
case WM_PAINT:
OnPaint(*((TMessage *)Message)); //消息響應句柄,也就是響應消息的成員函數,在Form1中定義
break;
default:
Form1::Dispatch(Message);
break;
}
}
這樣就很順眼了,對吧。對這種方法有兩點要解釋一下:
1。virtual void __fastcall Dispatch(void *Message) 這個虛方法的定義最早可以在TObject的定義中找到。打開
BCB的幫助,查找TForm的Method(方法),你會發現這裡很清楚的寫著Dispatch方法繼承自TObject。如果您關心VCL的繼承機制的話,您會發現TObject是所有VCL對象的基類。TObject的抽象凝聚了Borland的工程師們的心血。如果有興趣。您應該好好查看一下TObject的定義。
很顯然,所有Tobject的子類都可以重載基類的Dispatch方法,來實現自己的消息調用。如果Dispatch方法找不到此消息的定義,會將此消息交由TObject::DefaultHandler方法來處理。抽象基類TObject的DefaultHandler方法實際上是空的。同樣要由繼承子類重載實現它們自己的消息處理過程。
2。很多時候,我見到的第二行是這樣寫的:
MESSAGE_HANDLER(WM_PAINT,TMessage,OnPaint)
在這裡,您可以很清楚的看到幾行注解,意思是ATL中同樣包含了一個MESSAGE_HANDLER的宏定義,這與VCL發生了沖突。為了解決這個問題,Borland改用VCL_MESSAGE_HANDLER這樣的寫法。
當您沒有使用ATL的時候,MESSAGE_HANDLER將轉換成VCL_MESSAGE_HANDLER。但如果您使用了ATL的話,就會有問題。所以我建議您始終使用VCL_MESSAGE_HANDLER的寫法,以免出現問題。