Delphi是Borland公司的一個物件導向的視覺化軟體開發工具。 Delphi集中了Visual C++和Visual Basic兩者的優點:容易上手、功能強大,特別是在介面設計、資料庫程式設計、網路程式設計方面更有其獨特的優勢。
Delphi中的消息
訊息是Windows發出的一個通知,它告訴應用程式某個事件發生了。在Delphi中,大多數情況下Windows的訊息被封裝在VCL的事件中,我們只需處理相應的VCL事件就可以了,但如果我們需要編寫自己的控制項、截獲或過濾訊息就必須深入研究Win32的訊息處理機制。
在Delphi中訊息以TMessage記錄的方式定義。打開Message.pas文件,我們可以看到Tmessage是這樣定義的:
type
TMessage = packed record
Msg: Cardinal;
case Integer of
0: ( WParam: Longint;
LParam: Longint;
Result: Longint);
1: ( WParamLo: Word;
WParamHi: Word;
LParamLo: Word;
LParamHi: Word;
ResultLo: Word;
ResultHi: Word);
end;
其中,Msg是區別於其他訊息的常數值,這些常數值可以是Windows單元中預先定義的常數,也可以是使用者自己定義的常數。 Wparam通常是一個與訊息有關的常數值,也可以是視窗或控制項的句柄。 LParam通常是一個指向記憶體中資料的指標。
Result是訊息處理的回傳值。 Wparam、Lparam和Result都是32位的,如果想存取其中的低16位元或高16位元可以分別使用WparamLo、WparamHi、 LParamLo、LparamHi、ResultLo和ResultHi。
在Delphi中除了通用的Tmessage外,還為每個Windows定義了一個特殊的訊息記錄。我們可以瀏覽Message.pas文件,以下是鍵盤的訊息記錄:
TWMKey = packed record
Msg: Cardinal;
CharCode: Word;
Unused: Word;
KeyData: Longint;
Result: Longint;
與鍵盤相關的訊息如:WM_KEYDOWN、 WM_KEYUP、 WM_CHAR、 WM_SYSKEYDOWN WM_SYSKEYUP、WM_SYSCHAR的記錄也定義為TWMkey。在Message.pas檔案中有以下聲明:
TWMChar=TWMkey; TWMKeyDown=
TWMkey;TWMKeyUp=TWMkey; TWMSys
-KeyDown=TWMkey; TWMSysKeyUp=
TWMkey;TWMSysChar=TWMkey;
訊息的發送
訊息處理就是定義應用程式如何回應Windows的訊息。在Delphi中每一個訊息都有自己的處理過程,它必須是一個物件中的方法,且只能傳遞一個Tmessage或其他特殊的訊息記錄,方法聲明後要有一個message指令,後面接著一個在0到32767之間的常量。
前面我們提到的訊息都是標準的Windows訊息(WM_X),除此之外還有VCL內部訊息、通知訊息和使用者自訂訊息。
VCL內部訊息通常以「CM_」開頭,用於管理VCL內部的事物。如果改變了某個屬性值或元件的其他一些特性後,需要透過內部訊息將該變更通知其他元件。例如,啟動輸入焦點訊息是向已啟動的或被停用的元件發送的,用於接受或放棄輸入焦點。
另外還有通知訊息,一個窗口內的子控制項發生了一些事情,需要通知父窗口,這是透過通知訊息實現的。它只適用於標準的視窗控件,如按鈕、列錶框、編輯框等等。打開Message.pas文件,在標準的Windows後面就是通知訊息的聲明:
const
{$EXTERNALSYM BN_CLICKED}
BN_CLICKED = 0;
{$EXTERNALSYM BN_PAINT}
BN_PAINT = 1;
{$EXTERNALSYM BN_HILITE}
BN_HILITE = 2;
以上是按鈕的通知訊息,分別表示使用者點選了按鈕、按鈕應當重畫、使用者加亮了按鈕。
使用者也可以自行定義訊息、給自己發送訊息和編寫訊息處理過程。訊息的常數值為WM_USER+100到$7FFF, 這個範圍是Windows為使用者自訂訊息保留的。
Delphi訊息的發送有三種方法:
1. Tcontrol類別的Perform物件方法。可以向任何一個窗體或控制項傳送訊息,只需要知道窗體或控制項的實例。其聲明如下:
function Tcontrol.Perform(Msg:Cardinal;Wparam,Lparam:Longint):Longint
2. Windows的API函數SendMessage()和Postmessage()。其聲明如下:
function SendMessage(hWnd: HWND; Msg: UINT;wParam:WPARAM; lParam: LPARAM):LRESULT;stdcall;
function SendMessage(hWnd: HWND; Msg: UINT;wParam: WPARAM; lParam:LPARAM):LRESULT;stdcall
PostMessage函數將訊息加入應用程式的訊息佇列中去。應用程式的訊息循環會從訊息佇列中提取登記的該訊息,然後再發送到對應的視窗。
SendMessage函數可以越過訊息佇列直接向視窗過程發送。所以當Windows需要立刻回傳值時使用SendMessage,當需要不同的應用程式依序處理訊息時使用PostMessage。而Perform本質上和SendMessage相似,它們直接向視窗過程發送。 SendMessage、Postmessage函數只需要知道視窗的句柄就可以發送訊息,所以它們可以向非Delphi窗體發送一條訊息,但而Perform必須知道窗體或控制項的實例。
VCL訊息處理機制
在Delphi應用程式的原始程式碼中有語句application.Run,它的作用是啟動訊息循環,然後呼叫Application.PRocessMessage,該函數會在應用程式的訊息佇列中尋找一則訊息。當在訊息佇列中檢索到一則訊息後,觸發Application.OnMessage事件。這樣在Windows本身對訊息處理之前,就會回應OnMessage事件的處理過程,它優於任何訊息處理,而且只接收登記的訊息,即前面所述的由PostMessage發送的訊息。回應Application.OnMessage事件的處理過程必須是TmessageEvent類型,其聲明如下:
type TMessageEvent = procedure (var Msg: TMsg; var Handled: Boolean) of object;
其中TMsg是Windows中定義的訊息記錄,我們可以這樣宣告:
Procedure OnMyMessage(var Msg:TMsg;var Handled:Boolean);
然後把這個方法賦給Application.OnMessage事件:
Application.OnMessage :=OnMyMessage;
OnMessage事件將捕獲發送給應用程式的所有訊息,這是一個非常繁忙的事件,因此在處理OnMessage事件的處理過程中設定斷點進行訊息處理是不明智的。
VCL物件用來接收訊息的方法叫MainWndProc。它是定義在Twincontrol類別中的靜態方法,不能被重載。它不直接處理訊息,當訊息離開MainWndProc後,訊息被傳遞給物件的WndProc方法,WndProc方法是在Tcontrol類別中定義的一個虛擬方法,由它呼叫Dispatch方法。 Dispatch根據傳入的Message來尋找對應的處理方法,如果最後找不到,就繼續向上到父類別中尋找訊息處理方法,一直到找到為止,如果找不到則呼叫Defaulthandler。 Defaulthandler方法對訊息進行最後的處理,然後把訊息傳遞給Windows的DefWindowProc函數或其他預設的視窗程序。