⓪編著 :蕭沖
VCL component msg handling:
Event -> ProcessMessage (TApplication)
-> 訊息被傳到指定的VCL,如Form、Button、Label............
-> 該物件的MainWndProc (最初定義在 TWinControl,不可被override)
-> 該物件的WndProc (virtual定義在 TControl)
-> 該物件的Dispatch (virtual定義在 TObject) 此函式會進一步的往父類別去找(透過VMT)可處理該訊息的函式)
-> 該物件的TWinControl.WMCommand (若此訊息是由子視窗所送來的WM_COMMAND)
-> 子物件的Handler (但方法是定義在父類上的),是經DoControlMsg這個函式找出子物件,並使用子物件的perform方法
-> 該物件或子物件的DefaultHandler
A. Hook MSG at WinMain:
1/用Application的OnMessage處理POST的訊息,但對於某些由系統直接send的msg或是user用SendMessage方法傳的msg是無法被trigger的。
1.1/ Application->Run裡的Loop中包含ProcessMessage這個method,而OnMessage則含在它的判別中。
function TApplication.ProcessMessage(var Msg: TMsg): Boolean;
var
Handled: Boolean;
begin
Result := False;
if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
begin
Result := True;
if Msg.Message <> WM_QUIT then
begin
Handled := False; //稍後可能經由OnMessage而改變,進而影響DispatchMessage是否被叫
if Assigned(FOnMessage) then FOnMessage(Msg, Handled); //是否有指定OnMessage
if not IsHintMsg(Msg) and not Handled and not IsMDIMsg(Msg) and
not IsKeyMsg(Msg) and not IsDlgMsg(Msg) then
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end
else
FTerminate := True;
end;
end;
2/用HookMainWindow/UnhookMainWindow 改寫TApplication.WndProc中的訊息處理部份。
B. SubClass WndProc:
1/用SubClassing的技術SetWindowLongPtr(),指向另一個新的WndProc,這是最全面性的更改視窗類的處理函式。然後再用CallWindowProc 呼叫原WndProc,最後app結束前再用SetWindowLongPtr原來的WndProc的註冊。 (事實上vcl自己就有用,它先指向InitWindow,然後再轉向MainWndProc)
C. Override default WndProc:
1/ override WndProc 這是全面性的。在TControl裡有個property叫WindowProc,就是指向原始的WndProc,可簡單的透過這個屬性指向自己的method。
D. Override TObject.Dispatch method :
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_XXXX, TWMXXXX, WMXXXX)
END_MESSAGE_MAP(TForm)
這個macro經preprocessing後長得像
virtual void __fastcall Dispatch(void *Message)
{
switch (((PMessage)Message)->Msg)
{
case WM_XXXX:
WMXXXX(*((TMessage *)Message));
break;
default:
TForm::Dispatch(Message); //最後會叫到TObject.Dispatch : 用途是找該類別由子至父的VMT裡的虛擬方法,以處理該訊息
break;
}
}
E. 指定VCL預設的On事件屬性,如OnButtonClick等至自己的handler
1/在每個元件上的on event上指定自己的handler,即一般的操作方法。(定義在dfm檔裡)
F. override TWinControl.DefaultHandler (或是透過屬性FDefWndProc指向自己的handler)。這裡可以處理user define msg的最後一個地方。
整個訊息的流程可能是由子窗收到訊息處理後,往父視窗送,父視窗處理後,再依子的handle找到子元件,改變一下原message的id,如:把WM_COMMAND變成CN_COMMAND(=CN_BASE+WM_COMMAND),往子視窗送(但不是用一般的queue的方式,而是用perform的方法,即直接叫用子元件的wndproc函式),在子的windproc裡去進一步處理CN_COMMAND
註: VC++裡的MFC的做法不一樣,在MFC4以後,有個叫reflect的機制。以WM_CTLCOLOREDIT這個訊息來說,原本只有是父視窗會收到這樣的訊息(來自子視窗),但這樣只能在父視窗處理該息訊,這對開發子元件不是很方便。所以在送WM_CTLCOLOREDIT給父視窗前,會先用OnChildNotify的法方(類似VCL裡的perform的樣式)來自己"模擬"送自己一個WM_CTLCOLOREDIT的訊息(事實上如perform的情形一樣,是沒有經過queue的方式),然後WM_CTLCOLOREDIT裡若有自己處理好,可再送WWM_CTLCOLOREDIT給父類別(若OnChildNotify沒return true時)。到這裡,即MFC4以後,WM_CTLCOLOREDIT的訊息才可能出現在父視窗與子視窗,否則都是只有父會收到。比對一下VCL的架構,borland把二個訊息給變不同,即原來的WM_CTLCOLOREDIT進一步改成CN_CTLCOLOREDIT "送"給子視窗。此外,VCL是子窗到父窗再到子窗,而MFC4是子窗到子窗到父窗。
可參考:
http://developeriq.in/articles/2009/jun/24/subclassing-in-vc/
http://msdn.microsoft.com/en-us/library/eeah46xd%28v=vs.80%29.aspx
留言列表