C++ Builder中消息處理過程及應(yīng)用
發(fā)布時間:2008-03-22 閱讀數(shù): 次 來源:網(wǎng)樂原科技
C++ Builder作為一種RAD方式的程序開發(fā)工具,其全新的可視化編程環(huán)境、面向組件的開發(fā)模式無疑會大大地提高編程效率。它對繁雜的Windows 消息及API作了較全面的封裝,編程者在大多數(shù)情況下不需理會Windows消息的細(xì)節(jié),只要將心思放在組件的事件處理函數(shù)上即可。然而,畢竟Windows操作系統(tǒng)是一個以消息驅(qū)動的系統(tǒng),運行其上的應(yīng)用程序,自然無法脫離系統(tǒng)之外,因此掌握并運用消息處理,對一些問題的處理會有事半功倍的效果。
盡管C++ Builder的VCL控件封裝了大多數(shù)常用的消息,C++ Builder所提供的事件處理能力也具備了相當(dāng)程度的完備性,但當(dāng)處理C++ Builder 未定義的Windows消息或自定義消息時,掌握C++ Builder 的內(nèi)部消息處理機制還是十分必要的。下面,從Windows 操作系統(tǒng)消息驅(qū)動機制開始,進(jìn)而探討C++ Builder的VCL控件中消息的封裝、傳遞和處理機制,最后以新增消息處理過程的應(yīng)用實例作為對所講內(nèi)容的驗證和實踐。
一、Windows 消息驅(qū)動機制
Windows是以消息驅(qū)動的操作系統(tǒng),Windows 消息提供了應(yīng)用程序與應(yīng)用程序以及應(yīng)用程序與Windows系統(tǒng)之間進(jìn)行通訊的手段。
Windows 中有一個系統(tǒng)消息隊列,對于每一個正在執(zhí)行的Windows應(yīng)用程序,系統(tǒng)為其建立一個“消息隊列”,即應(yīng)用程序隊列,用來存放該程序可能創(chuàng)建的各種窗口的消息。應(yīng)用程序中含有一段稱作“消息循環(huán)”的代碼,用來從消息隊列中檢索這些消息并把它們分發(fā)到相應(yīng)的窗口函數(shù)中。
消息循環(huán)代碼是應(yīng)用程序中主函數(shù)winmain ( )中類似如下的程序段:
while(GetMessage(&&msg,NULL,NULL,NULL))
{ //從消息隊列中取得消息
TranslateMessage(&&msg);
//檢索并生成字符消息WM_CHAR
DispatchMessage(&&msg);
//將消息發(fā)送給相應(yīng)的窗口函數(shù)
}
由此可見,所謂“消息循環(huán)”,實際是程序循環(huán)。
Windows 應(yīng)用程序創(chuàng)建的每個窗口都在系統(tǒng)核心注冊一個相應(yīng)的窗口函數(shù),窗口函數(shù)程序代碼形式上是一個巨大的switch 語句,用以處理由消息循環(huán)發(fā)送到該窗口的消息,窗口函數(shù)由Windows 采用消息驅(qū)動的形式直接調(diào)用,而不是由應(yīng)用程序顯示調(diào)用的,窗口函數(shù)處理完消息后又將控制權(quán)返回給Windows。
系統(tǒng)消息隊列、應(yīng)用程序隊列、消息循環(huán)和窗口函數(shù)之間的關(guān)系如圖1所示。
二、C++ Builder 中的消息處理
有了以上Windows 系統(tǒng)消息驅(qū)動模式程序設(shè)計的認(rèn)識,下面分析一下C++ Builder中消息處理是如何封裝、實現(xiàn)的。
Windows 程序框架,包括一些初始化、消息循環(huán)代碼等,在類 Application中封裝、實現(xiàn)。每一個用C++ Builder 編寫的Windows GUI 應(yīng)用程序,大部分缺省生成如下代碼:
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
//Windows 應(yīng)用程序主函數(shù)
{
try
{
Application-〉Initialize();//作初始化
Application-〉CreateForm(__classid(TForm1), &&Form1);
Application-〉Run();
//其中包含消息循環(huán)
}
catch (Exception &&exception) //例外處理
{
Application-〉ShowException(&&exception);
}
return 0;
}
對于消息處理,C++ Builder采用基于控件(component)的程序設(shè)計模式,每種控件都繼承一套完整的消息派送體系。其實現(xiàn)方法如下: 它為每一種類型的控件都注冊一個名為 MainWndProc 的方法函數(shù)作為窗口函數(shù),接受“消息循環(huán)”派送來的消息,它是一個非虛擬方法,不對任何特定消息作特別處理,它僅僅調(diào)用 WndProc 方法函數(shù),并作一些例外處理。不同控件對消息處理的定制發(fā)生在WndProc 方法中,因為它是一個虛擬方法,每一種控件可以通過覆蓋它來適應(yīng)特別的情況。WndProc 方法檢查不同的條件,作不同的處理,從而能夠濾掉不希望處理的各種消息。例如:當(dāng)控件正被拖動時,應(yīng)忽略鍵盤事件,所以在Twincontrol 類的WndProc 方法中,有判斷當(dāng)控件不是被拖放狀態(tài)、才繼續(xù)傳遞鍵盤消息這樣功能的代碼。最終,WndProc 調(diào)用 Dispatch 方法,它是一個從所用控件的起始祖先Tobject 繼承而來的虛擬方法,它確定調(diào)用哪個方法處理傳來的消息。Dispatch 使用消息結(jié)構(gòu)(Tmessage)中的 msg 成員變量確定如何處理一個特定的消息,如果控件定義了處理這一消息的函數(shù),則調(diào)用它,否則,就逐級向上追溯,看祖先類是否定義此類的處理方法,直到起始祖先類(Tobject)。如果都沒有定義處理方法,則調(diào)用缺省的處理方法(DefaultHandler)。
以上是消息在控件中的傳遞過程,INPRISE公司為方便用戶,對消息處理作了進(jìn)一步的封裝,把常用的消息封裝成相應(yīng)的事件屬性,這樣編程者完全不用考慮消息細(xì)節(jié),只要編寫事件處理方法,并給事件屬性賦值即可。
三、應(yīng)用實例
下面以增加新的自定義消息處理過程為例,對以上所述內(nèi)容做進(jìn)一步的說明。
通過以上分析我們知道,每一條消息的具體處理過程,是在 Dispatch 中派發(fā)完成的,因此增加新的消息, 只要覆蓋虛擬函數(shù) Dispatch 即可。
C++ Builder為了方便地處理消息,定義了以下三個處理消息的宏:
BEGIN_MESSAGE_MAP
VCL_MESSAGE_HANDLER(msg,type,meth)
END_MESSAGE_MAP(base)
定義如下:
#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;
#define END_MESSAGE_MAP(base) default:
base::Dispatch(Message);
break;
}
}
我們只需在控件類或自定義控件類的public節(jié),依次寫入三個宏即可,其中宏VCL_MESSAGE_HANDLER可以根據(jù)處理消息的條數(shù)而出現(xiàn)多次。宏展開后,即生成一個新的Dispatch 函數(shù),它先判斷處理用戶定義消息,若是其他消息,則傳遞至父類的Dispatch 函數(shù)處理,從而完成自定義消息的處理并保證原來消息處理體系的完整性。