C++ BUILDER 消息處理的深入探索
發(fā)表時(shí)間:2023-07-30 來(lái)源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]在本文中我將告訴你如何以C++Builder來(lái)處理Windows訊息,並透過(guò)此一能力,來(lái)達(dá)成在一般VCL元件所無(wú)法做到的功能。 何謂Window訊息(Message) 大家都知道 Windows是一...
在本文中我將告訴你如何以C++Builder來(lái)處理Windows訊息,並透過(guò)此一能力,來(lái)達(dá)成在一般VCL元件所無(wú)法做到的功能。
何謂Window訊息(Message)
大家都知道 Windows是一套以訊息驅(qū)動(dòng)(Message Driven)的作業(yè)系統(tǒng)。然而對(duì)於訊息本身卻諱莫如深,只知其然而不知其所以然,雖然C++Builder將某些Windows訊息封裝於事件(Event)系統(tǒng)中,但身為一個(gè)Windows程式設(shè)計(jì)師,實(shí)有必要瞭解Windows的訊息系統(tǒng)。
所謂訊息是由Windows作業(yè)系統(tǒng)送往程式的事件。它是系統(tǒng)中各個(gè)物件溝通的方式,舉例來(lái)說(shuō),當(dāng)移動(dòng)滑鼠、按下滑鼠鍵、改變視窗大小時(shí),Windows都會(huì)送出訊息以通知程式。當(dāng)然,為了要辨別事件的內(nèi)容,Windows系統(tǒng)中定義了許多的訊息,如WM_PAINT,WM_CHAR等等。
當(dāng)事件發(fā)生時(shí),Windows會(huì)判斷該事件必須由那個(gè)程式接收,然後將事件以訊息的方式送往程式的視窗中。雖然在Windows系統(tǒng)中包含了數(shù)以百計(jì)的事件,但是作業(yè)系統(tǒng)並沒有為各個(gè)事件設(shè)計(jì)不同的訊息結(jié)構(gòu),而是以一個(gè)一般性的結(jié)構(gòu)來(lái)描述訊息,這個(gè)結(jié)構(gòu)在C++Builder就稱是TMessage。
當(dāng)然,隨著事件的不同,對(duì)於訊息的解釋也有所不同,在C++Builder中也為各種常用的訊息定義了專屬的結(jié)構(gòu),你可以直接使用它們來(lái)解釋訊息。這些訊息定義在C++Builder目錄下的Include\vcl\messages.hpp中,你可以決定要自行解釋TMessage參數(shù)或是直接將其轉(zhuǎn)換成專屬的結(jié)構(gòu)。很抽象嗎?我舉個(gè)例子吧,以WM_NCHITTEST訊息來(lái)說(shuō),C++Builder為它定義了TWMNCHitTest的專屬結(jié)構(gòu),所以你可以直接經(jīng)由它來(lái)得到XPos、YPos等值;蛘吣阋部梢灾苯佑蒚Message的LParam取得其值,端看你使用的方便。仔細(xì)觀察TMessage及TWMNCHitTest兩個(gè)結(jié)構(gòu),你會(huì)發(fā)現(xiàn)它們是等價(jià)的,也就是說(shuō)它們的大小是一致的,因此你可以直接用強(qiáng)制轉(zhuǎn)型互相轉(zhuǎn)換(這有點(diǎn)類似union的方法)。
struct TMessage
{
Cardinal Msg;
union
{
struct
{
Word WParamLo;
Word WParamHi;
Word LParamLo;
Word LParamHi;
Word ResultLo;
Word ResultHi;
};
struct
{
long WParam;
long LParam;
long Result;
};
};
};
struct TWMNCHitTest
{
Cardinal Msg;
long Unused;
union
{
struct
{
Windows::TSmallPoint Pos;
long Result;
};
struct
{
short XPos;
short YPos;
};
};
} ;
在收到訊息後,程式必須處理該訊息,若是不處理,則可直接將它交給Windows的內(nèi)定處理程序來(lái)處理之,若是程式需要傳回值,也可以在此時(shí)傳回,Windows會(huì)將該值傳回給呼叫方。如此就完成了訊息傳遞的程序。
WM_NCHITTEST訊息的奧秘
WM_NCHITTEST訊息是一個(gè)很特殊的訊息。它是用來(lái)決定目前滑鼠所在位置屬性的訊息,因此我們可以利用此特性,當(dāng)滑鼠移至指定的位置時(shí),傳回 HTCAPTION,使得系統(tǒng)以為滑鼠目前位於標(biāo)題棒,如此你就可以移動(dòng)視窗了。如何?是不是很神奇呢?
由上可知,只要我們適時(shí)地?cái)r截WM_NCHITTEST訊息,然後傳回HTCAPTION,就可以順利地欺騙系統(tǒng),達(dá)成在任何位置模擬出標(biāo)題棒的效果。
C++ Builder的處理訊息的巨集
在C++Builder為了處理訊息的方便,因此定義了三個(gè)處理訊息的巨集(Macro)。
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_NCHITTEST,TMessage,OnNcHitTest)
END_MESSAGE_MAP(TForm)
以上的三個(gè)巨集BEGIN_MESSAGE_MAP、MESSAGE_HANDLER及END_MESSAGE就是C++ Builder定義的巨集,其中比較重要的是MESSAGE_HANDLER;它共需要三個(gè)參數(shù),第一個(gè)參數(shù)代表訊息的ID,第二個(gè)代表參數(shù)型態(tài),最後一個(gè)則是訊息事件處理函數(shù)。
乍看之下,這個(gè)巨集似乎和MFC及OWL所使用的巨集有幾分神似,沒錯(cuò),不過(guò)其機(jī)制卻更為簡(jiǎn)單及簡(jiǎn)潔,我們可以看看C++Builder對(duì)於這三個(gè)巨集的原始定義:
#define BEGIN_MESSAGE_MAP virtual void __fastcall Dispatch(void *Message) \
{ \
switch (((PMessage)Message)->Msg) \
{
#define MESSAGE_HANDLER(msg,type,meth) \
case msg: \
meth(*((type *)Message)); \
break;
#define END_MESSAGE_MAP(base) default: \
base::Dispatch(Message); \
break; \
} \
}
相較於MFC或 OWL的可怕巨集,它實(shí)在是簡(jiǎn)單多了,這是因?yàn)镃++Builder已替你完成了大部份的工作。其實(shí)若我們把以上的巨集展開後,可以得到以下的結(jié)果:
virtual void __fastcall Dispatch(void *Message)
{
switch (((PMessage)Message)->Msg)
{
case WM_NCHITTEST:
OnNcHitTest(*((TMessage *)Message));
break;
default:
TForm::Dispatch(Message);
break;
}
}
怎麼樣?展開之後是不是有恍然大悟的感覺,要弄清楚這個(gè)巨集在賣啥膏藥是很容易的,如果你玩過(guò)MFC的訊息處理機(jī)制,再看到以上的巨集,相較之下,實(shí)在是小兒科,不過(guò)也就因其簡(jiǎn)單,所以C++Builder的優(yōu)勢(shì)益加彰顯。
我簡(jiǎn)單地說(shuō)明以上的程式:在每個(gè)TForm中都定義一個(gè)名為Dispatch的虛擬函式,它就是用來(lái)處理Windows的訊息的,在大部份情況下,訊息都是呼叫C++Builder所提供的處理函式,因此你不需要修改它。
換句話說(shuō),我們只要改寫Dispatch函式,就可以藉以處理指定的訊息了。前面提到的三個(gè)巨集只是將這個(gè)程序簡(jiǎn)化而已,沒什麼大不了。