Delphi是Borland公司提供的全新的WINDOWS程式開發工具.由於它採用了具有彈性的和可重用的物件導向Pascal(object-oriented pascal)語言,並有強大的資料庫引擎(BDE), 快速的程式碼編譯器, 同時又提供了眾多出色的構件.受到廣大程式設計人員的青睞. 在眾多的程式語言( 如VB,PowerBuilder,Powerpoint 等) 中脫穎而出.
其中一個DELPHI強於其他程式語言(如VB4.0)的地方就是在DELPHI中可自訂訊息, 並可直接處理訊息. 這對於那些希望編寫自己的構件(Component), 或者希望截獲. 過濾訊息的使用者來說是必不可少的. 因為編寫構件一般要對相應的消息進行處理.下面就對Delphi 中訊息處理機制進行一下介紹。
一.DELPHI VCL中訊息的傳遞
Delphi中每一個VCL(Visual Component Library)構件( 如Tbutton,Tedit等)都有一內在的訊息處理機制,其基本點就是構件類別接收到某些訊息並把它們發送給適當的處理方法,如果沒有特定的處理方法,則呼叫預設的消息處理句柄。其中mainwndPRoc是定義在Twincontrol類別中的一個靜態方法,不能被重載(Override)。它不直接處理訊息, 而是交由wndproc 方法處理, 並為wndproc方法提供一個異常處理模組。 Mainwndproc方法宣告如下:
procedure MainWndProc(var Message: TMessage);
Wndproc是在Tcontrol類別中定義的一個虛擬方法,由它呼叫dispatch方法來進行訊息的分配, wndproc 方法宣告如下:
procedure WndProc(var Message: TMessage); virtual;
dispatch 方法是在Tobject 根類別中定義的, 其聲明如下:
procedure Tobject.dispatch(var Message); 傳遞給dispatch 的訊息參數必須是一個記錄類型, 且這個記錄中第一個入點必須是一個cardinal 類型的域(field), 它包含了要指派的訊息的訊息號碼. 例如:
type
Tmessage=record
Msg:cardinal;
wparam:Word;
lparam:longint; .
result:longint;
end;
而Dispatch 方法會根據訊息號碼呼叫構件的最後一個代類中處理此訊息的句柄方法. 如果此構件和它的祖先類別中都沒有對應此訊息的處理句柄,Dispatch 方法便會呼叫Defaulthandler 方法.Defaulthandler 方法是定義於Tobject 中的虛擬方法, 其宣告如下:
procedure Defaulthandler(var Message);virtual;
Tobject 類別中的Defaulthandler 方法只是實現簡單的返回而不對訊息進行任何的處理. 我們可以透過對此虛擬方法的重載, 在子類別中實現對訊息的預設處理. 對於VCL 中的構件而言,其Defaulthandler 方法會啟動windows API 函數Defwindowproc 對訊息進行處理.
二.DELPHI 中的消息處理句柄
在DELPHI 中使用者可以自訂訊息及訊息處理句柄. 訊息處理句柄的定義有以下幾個原則:
訊息處理句柄方法必須是一個過程, 且只能傳遞一個Tmessage 型變數參數.
方法宣告後要有message 指令, 後接一個在0 到32767 之間的訊息標號( 整數常數).
訊息處理句柄方法不需要用override 指令來明確指明重載祖先的一個訊息處理句柄, 另外它一般聲明在構件的protected 或private 區.
在訊息處理句柄中一般先是使用者自己對訊息的處理, 最後用inherited 指令呼叫祖先類別中對應此訊息的處理句柄( 有些情況下可能正相反). 由於可能對祖先類別中對此訊息的處理句柄的名字和參數類型不清楚, 而調用命令inherited 可以避免此麻煩, 同樣如果祖先類中沒有對應此消息的處理句柄,inherited 就會自動調用Defaulthandler 方法.( 當然如果要屏蔽掉此消息,就不用inherited 指令了)。
訊息處理句柄方法聲明為:
procedure Mymsgmethod(var message:Tmessage); message Msgtype;
同樣使用者也可以定義自己的訊息, 使用者自訂訊息應從WM_USER 開始.
自訂訊息及訊息處理句柄舉例如下:
const my_paint=Wm_user+1;
type
Tmypaint=record
msgid:cardinal;
msize:word;
mcolor:longint;
msgresult:longint;
end;
type
Tmycontrol=class(TCustomControl)
protected
procedure change(var message:Tmypaint); message my_paint;
.....
end;
.....
procedure Tmycontrol.change(var message:Tmypaint);
begin
size:=message.msize; { 設定Tmybutton 尺寸屬性}
color:=message.mcolor; { 設定Tmybutton 顏色屬性}
{do something else}
inherited; { 交由Tcustomcontrol 處理}
end;
三. 過濾訊息
過濾訊息又稱訊息陷阱。 在一定情況下, 使用者可能需要封鎖某些訊息. 或截獲某些訊息進行處理。 由上述介紹可以看出過濾訊息一般有三種途徑:(1). 重載構件繼承的虛擬方法wndproc. (2). 針對某訊息編寫訊息處理句柄. (3). 重載構件繼承的虛擬方法Defhandler , 在其中對訊息進行處理。 其中常用的方法是方法(2), 在上節中已介紹過了, 方法(1) 與方法(3) 相似,這裡只簡單介紹一下方法(1)。
重載虛擬方法wndproc 的一般流程如下:
procedure Tmyobject.wndproc(var message:Tmessage);
begin
{... 判斷此訊息是否該處理..}
inherited wndproc(message);
{ 未處理的訊息交由父輩wndproc 方法處理}
end;
由此可以看出在wndproc 方法中處理訊息的優勢是可以過濾整個範圍內的訊息,而不必為每個訊息指定一個處理句柄, 事實上Tcontrol 構件中就是利用它來過濾並處理所有的滑鼠訊息的( 從WM_mousefirst 到WM_mouselast, 如下程式碼示). 同樣利用它也可以阻止某些訊息被傳送給處理句柄。
procedure TControl.WndProc(var Message: TMessage);
begin
if (Message.Msg>=WM_MOUSEFIRST) and
(Message.Msg <= WM_MOUSELAST)
then
if Dragging then { 處理拖曳事件}
DragMouseMsg(TWMMouse(Message))
else
... { 處理其他滑鼠訊息}
end;
Dispatch(Message);
{ 否則正常發送訊息}
end;
下例為一簡單的自訂構件範例:
Tmyedit 類別是從Tedit 類別衍生出的新類, 它的特點是在運行中不能獲得焦點, 不能由鍵盤輸入( 有點類似Tlabel 構件). 我們可在其wndproc 方法中過濾出WM_setfocus,WM_mousemove 訊息並進行處理來達到上述要求, 原始程式如下:
unit myedit;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs,
StdCtrls;
type
Tmyedit = class(TEdit)
private
{ Private declarations }
protected
{ Protected declarations }
{ other fields and methods}
procedure wndproc(var message:Tmessage);override;
public
{ Public declarations }
published
{ Published declarations }
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [Tmyedit]);
end;
procedure Tmyedit.wndproc(var message:tmessage);
begin
if message.msg=wm_mousemove then
begin
cursor:=crarrow;
{ 設定遊標為crarrow, 而非缺省的crBeam 遊標}
exit;
end;
if message.msg=wm_SetFocus then exit;
{屏蔽掉WM_setfocus訊息,不讓Tmyedit控制項取得輸入焦點}
inherited wndproc(message);
{其他訊息交父輩wndproc處理}
end;
end.
您可以將Tmyedit 加到Component Palette中檢驗其效能。
由上述介紹可以看出,只有清楚了Delphi VCL中的訊息處理機制, 掌握好處理各種訊息的方法和時機(必要時要藉助各種工具, 如winsight32,spy 等),並結合OOP語言的特點, 我們才可能編出高品質的構件。 這當然要靠讀者在實踐中不斷摸索,累積經驗.