日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > c/c++ >内容正文

c/c++

深入BCB理解VCL的消息机制

發(fā)布時(shí)間:2025/3/15 c/c++ 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深入BCB理解VCL的消息机制 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
深入BCB理解VCL的消息機(jī)制

引子:本文所談及的技術(shù)內(nèi)容都來(lái)自于Internet的公開(kāi)信息。由筆者在閑暇之際整理
后,貼出來(lái)以飴網(wǎng)友,姑且妄稱(chēng)原創(chuàng)。每次在國(guó)外網(wǎng)站上找到精彩文章的時(shí)候,心中都
會(huì)暗自嘆息,為什么在中文網(wǎng)站難以覓得這類(lèi)文章呢?其實(shí)原因大家都明白。

時(shí)至今日,學(xué)習(xí)Windows編程的兄弟們都知道消息機(jī)制的重要性。所以理解消息機(jī)制也
成了不可或缺的功課。大家都知道,Borland的C++ Builder以及Delphi的核心是VCL。
作為Win32平臺(tái)上的開(kāi)發(fā)工具,封裝Windows的消息機(jī)制當(dāng)然也是必不可少的。那么,在
C++ Builder中處理消息的方法有哪些呢?它們之間的區(qū)別又在哪里?如果您很清楚這
些,呵呵,對(duì)不起啦,請(qǐng)關(guān)掉這個(gè)窗口。如果不清楚那就和我一起深入VCL的源碼看個(gè)
究竟吧。

方法1:使用消息映射(Message Map)重載TObject的Dispatch虛成員函數(shù)
這個(gè)方法大家用的很多。形式如下


BEGIN_MESSAGE_MAP
VCL_MESSAGE_HANDLER( … … )
END_MESSAGE_MAP( … )

但這幾句話(huà)實(shí)在太突兀,C++標(biāo)準(zhǔn)中沒(méi)有這樣的定義。不用講,這顯然又是宏定義。它
們到底怎么來(lái)的呢?CKER第一次見(jiàn)到它們的時(shí)候,百思不得其解。嘿嘿,不深入VCL,
怎么可能理解?

在/Borland/CBuilder5/Include/Vcl找到sysmac.h,其中有如下的預(yù)編譯宏定義:


#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;

// NOTE: ATL defines a MESSAGE_HANDLER macro which conflicts with VCL's
macro. The
// VCL macro has been renamed to VCL_MESSAGE_HANDLER. If you are not using
ATL,
// MESSAGE_HANDLER is defined as in previous versions of BCB.

#if !defined(USING_ATL) && !defined(USING_ATLVCL) &&
!defined(INC_ATL_HEADERS)
#define MESSAGE_HANDLER VCL_MESSAGE_HANDLER
#endif // ATL_COMPAT

#define END_MESSAGE_MAP(base)
default: /
base: ispatch(Message); /
break; /
} /
}

這樣對(duì)如下的例子:
BEGIN_MESSAGE_MAP
VCL_MESSAGE_HANDLER(WM_PAINT,TMessage,OnPaint)
END_MESSAGE_MAP(TForm1)

在預(yù)編譯時(shí),就被展開(kāi)成如下的代碼
virtual void __fastcall Dispatch(void *Message)
{
switch (((PMessage)Message)->Msg)
{
case WM_PAINT:
OnPaint(*((TMessage *)Message));
//消息響應(yīng)句柄,也就是響應(yīng)消息的成員函數(shù),在Form1中定義
break;
default:
TForm1: ispatch(Message);
break;
}

}

這樣就很順眼了,對(duì)吧。對(duì)這種方法有兩點(diǎn)要解釋一下:
1.virtual void __fastcall Dispatch(void *Message)
這個(gè)虛方法的定義最早可以在TObject的定義中找到。打開(kāi)BCB的幫助,查找TForm的方
法(Method),你會(huì)發(fā)現(xiàn)這里很清楚的寫(xiě)著Dispatch方法繼承自TObject。如果您關(guān)心
VCL的繼承機(jī)制的話(huà),您會(huì)發(fā)現(xiàn)TObject是所有VCL對(duì)象的基類(lèi)。TObject的抽象凝聚了
Borland的工程師們的心血。如果有興趣。您應(yīng)該好好查看一下TObject的定義。
很顯然,所有TObject的子類(lèi)都可以重載基類(lèi)的Dispatch方法,來(lái)實(shí)現(xiàn)自己的消息調(diào)
用。如果Dispatch方法找不到此消息的定義,會(huì)將此消息交由TObject:DefaultHandler
方法來(lái)處理。抽象基類(lèi)TObject的DefaultHandler方法實(shí)際上是空的。同樣要由繼承子
類(lèi)重載實(shí)現(xiàn)它們自己的消息處理過(guò)程。


2.很多時(shí)候,我見(jiàn)到的第二行是這樣寫(xiě)的:
MESSAGE_HANDLER(WM_PAINT,TMessage,OnPaint)
在這里,您可以很清楚地看到幾行注解,意思是ATL中同樣包含了一個(gè)MESSAGE_HANDLER
的宏定義,這與VCL發(fā)生了沖突。為了解決這個(gè)問(wèn)題,Borland改用
VCL_MESSAGE_HANDLER。當(dāng)您沒(méi)有使用ATL的時(shí)候,MESSAGE_HANDLER將轉(zhuǎn)換成
VCL_MESSAGE_HANDLER。但如果用了ATL就會(huì)有問(wèn)題。所以我建議您始終使用
VCL_MESSAGE_HANDLER的寫(xiě)法,以免出現(xiàn)問(wèn)題。

?

方法2:重載TControl的WndProc方法
還是先談?wù)刅CL的繼承策略。VCL中的繼承鏈的頂部是TObject基類(lèi)。一切的VCL組件和對(duì)
象都繼承自TObject。

打開(kāi)BCB幫助查看TControl的繼承關(guān)系:
TObject->TPersistent->TComponent->TControl

原來(lái)TControl是從TPersistent類(lèi)的子類(lèi)TComponent類(lèi)繼承而來(lái)的。TPersistent抽象基
類(lèi)具有使用流stream來(lái)存取類(lèi)的屬性的能力。

TComponent類(lèi)則是所有VCL組件的父類(lèi)。
這就是所有的VCL組件包括您的自定義組件可以使用dfm文件存取屬性的原因(當(dāng)然要是
TPersistent的子類(lèi),我想您很少需要直接從TObject類(lèi)來(lái)派生您的自定義組件吧)。

TControl類(lèi)的重要性并不亞于它的父類(lèi)們。在BCB的繼承關(guān)系中,TControl類(lèi)的是所有
VCL可視化組件的父類(lèi)。實(shí)際上就是控件的意思吧。所謂可視化是指您可以在運(yùn)行期間
看到和操縱的控件。這類(lèi)控件所具有的一些基本屬性和方法都在TControl類(lèi)中進(jìn)行定
義。

TControl的實(shí)現(xiàn)在/Borland/CBuilder5/Source/Vcl/control.pas中可以找到。(可能
會(huì)有朋友問(wèn)你怎么知道在那里?使用BCB提供的Search -> Find in files很容易找到。
或者使用第三方插件的grep功能。)

好了,進(jìn)入VCL的源碼吧。說(shuō)到這里免不了要抱怨一下Borland。哎,為什么要用pascal
實(shí)現(xiàn)這一切……:-(

TControl繼承但并沒(méi)有重寫(xiě)TObject的Dispatch方法。反而提供了一個(gè)新的方法
WndProc。一起來(lái)看看Borland的工程師們是怎么寫(xiě)的吧。

procedure TControl.WndProc(var Message: TMessage);
var
Form: TCustomForm;
begin
//由擁有control的窗體來(lái)處理設(shè)計(jì)期間的消息
if (csDesigning in ComponentState) then
begin
Form := GetParentForm(Self);
if (Form <> nil) and (Form.Designer <> nil) and
Form.Designer.IsDesignMsg(Self, Message) then Exit;
end
//如果需要,鍵盤(pán)消息交由擁有control的窗體來(lái)處理
else if (Message.Msg >= WM_KEYFIRST) and (Message.Msg <= WM_KEYLAST) then
begin
Form := GetParentForm(Self);
if (Form <> nil) and Form.WantChildKey(Self, Message) then Exit;
end
//處理鼠標(biāo)消息
else if (Message.Msg >= WM_MOUSEFIRST) and (Message.Msg <= WM_MOUSELAST)
then
begin
if not (csDoubleClicks in ControlStyle) then
case Message.Msg of
WM_LBUTTONDBLCLK, WM_RBUTTONDBLCLK, WM_MBUTTONDBLCLK:
Dec(Message.Msg, WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
end;
case Message.Msg of
WM_MOUSEMOVE: Application.HintMouseMessage(Self, Message);
WM_LBUTTONDOWN, WM_LBUTTONDBLCLK:
begin
if FDragMode = dmAutomatic then
begin
BeginAutoDrag;
Exit;
end;
Include(FControlState, csLButtonDown);
end;
WM_LBUTTONUP:
Exclude(FControlState, csLButtonDown);
end;
end
// 下面一行有點(diǎn)特別。如果您仔細(xì)的話(huà)會(huì)看到這個(gè)消息是CM_VISIBLECHANGED.
// 而不是我們熟悉的WM_開(kāi)頭的標(biāo)準(zhǔn)Windows消息.
// 盡管Borland沒(méi)有在它的幫助中提到有這一類(lèi)的CM消息存在。但很顯然這是BCB的
// 自定義消息。呵呵,如果您對(duì)此有興趣可以在VCL源碼中查找相關(guān)的內(nèi)容。一定會(huì)有
不小的收獲。
else if Message.Msg = CM_VISIBLECHANGED then
with Message do
SendDockNotification(Msg, WParam, LParam);
// 最后調(diào)用dispatch方法。
Dispatch(Message);
end;

看完這段代碼,你會(huì)發(fā)現(xiàn)TControl類(lèi)實(shí)際上只處理了鼠標(biāo)消息,沒(méi)有處理的消息最后都
轉(zhuǎn)入Dispatch()來(lái)處理。
但這里需要強(qiáng)調(diào)指出的是TControl自己并沒(méi)有獲得焦點(diǎn)Focus的能力。TControl的子類(lèi)
TWinControl才具有這樣的能力。我憑什么這樣講?呵呵,還是打開(kāi)BCB的幫助。很多朋
友抱怨BCB的幫助實(shí)在不如VC的MSDN。毋庸諱言,的確差遠(yuǎn)了。而且這個(gè)幫助還經(jīng)常有
問(wèn)題。但有總比沒(méi)有好啊。

言歸正傳,在幫助的The TWinControl Branch 分支下,您可以看到關(guān)于TWinControl類(lèi)
的簡(jiǎn)介。指出TWinControl類(lèi)是所有窗體類(lèi)控件的基類(lèi)。所謂窗體類(lèi)控件指的是這樣一
類(lèi)控件:

1. 可以在程序運(yùn)行時(shí)取得焦點(diǎn)的控件。

2. 其他的控件可以顯示數(shù)據(jù),但只有窗體類(lèi)控件才能和用戶(hù)發(fā)生鍵盤(pán)交互。

3. 窗體類(lèi)控件能夠包含其他控件(容器)。

4. 包含其他控件的控件又稱(chēng)做父控件。只有窗體類(lèi)控件才能夠作為其他控件的父控
件。

5. 窗體類(lèi)控件擁有句柄。

除了能夠接受焦點(diǎn)之外,TWinControl的一切都跟TControl沒(méi)什么分別。這一點(diǎn)意味著
TWinControl可以對(duì)許多的標(biāo)準(zhǔn)事件作出響應(yīng),Windows也必須為它分配一個(gè)句柄。并且
與這個(gè)主題相關(guān)的最重要的是,這里提到是由BCB負(fù)責(zé)來(lái)對(duì)控件進(jìn)行重畫(huà)以及消息處
理。這就是說(shuō),TWinControl封裝了這一切。

似乎扯的太遠(yuǎn)了。但我要提出來(lái)的問(wèn)題是TControl類(lèi)的WndProc方法中處理了鼠標(biāo)消
息。但這個(gè)消息只有它的子類(lèi)TWinControl才能夠得到啊!?

這怎么可以呢……Borland是如何實(shí)現(xiàn)這一切的呢?這個(gè)問(wèn)題實(shí)在很奧妙。為了看個(gè)究
竟,再次深入VCL吧。

還是在control.pas中,TWinControl繼承了TControl的WndProc方法。源碼如下:

procedure TWinControl.WndProc(var Message: TMessage);
var
Form: TCustomForm;
KeyState: TKeyboardState;
WheelMsg: TCMMouseWheel;
begin
case Message.Msg of
WM_SETFOCUS:
begin
Form := GetParentForm(Self);
if (Form <> nil) and not Form.SetFocusedControl(Self) then Exit;
end;
WM_KILLFOCUS:
if csFocusing in ControlState then Exit;
WM_NCHITTEST:
begin
inherited WndProc(Message);
if (Message.Result = HTTRANSPARENT) and (ControlAtPos(ScreenToClient(
SmallPointToPoint(TWMNCHitTest(Message).Pos)), False) <> nil) then
Message.Result := HTCLIENT;
Exit;
end;
WM_MOUSEFIRST..WM_MOUSELAST:
//下面這一句話(huà)指出,鼠標(biāo)消息實(shí)際上轉(zhuǎn)入IsControlMouseMsg方法來(lái)處理了。
if IsControlMouseMsg(TWMMouse(Message)) then
begin
if Message.Result = 0 then
DefWindowProc(Handle, Message.Msg, Message.wParam, Message.lParam);
Exit;
end;
WM_KEYFIRST..WM_KEYLAST:
if Dragging then Exit;
WM_CANCELMODE:
if (GetCapture = Handle) and (CaptureControl <> nil) and
(CaptureControl.Parent = Self) then
CaptureControl.Perform(WM_CANCELMODE, 0, 0);
else
with Mouse do
if WheelPresent and (RegWheelMessage <> 0) and
(Message.Msg = RegWheelMessage) then
begin
GetKeyboardState(KeyState);
with WheelMsg do
begin
Msg := Message.Msg;
ShiftState := KeyboardStateToShiftState(KeyState);
WheelDelta := Message.WParam;
Pos := TSmallPoint(Message.LParam);
end;
MouseWheelHandler(TMessage(WheelMsg));
Exit;
end;
end;
inherited WndProc(Message);
end;

鼠標(biāo)消息是由IsControlMouseMsg方法來(lái)處理的。只有再跟到IsControlMouseMsg去看看
啦。源碼如下:
function TWinControl.IsControlMouseMsg(var Message: TWMMouse): Boolean;
var
//TControl出現(xiàn)啦
Control: TControl;
P: TPoint;
begin
if GetCapture = Handle then
begin
Control := nil;
if (CaptureControl <> nil) and (CaptureControl.Parent = Self) then
Control := CaptureControl;
end else
Control := ControlAtPos(SmallPointToPoint(Message.Pos), False);
Result := False;
if Control <> nil then
begin
P.X := Message.XPos - Control.Left;
P.Y := Message.YPos - Control.Top;
file://TControl的Perform方法將消息交由WndProc處理。
Message.Result := Control.Perform(Message.Msg, Message.Keys,
Longint(PointToSmallPoint(P)));
Result := True;
end;
end;

原來(lái)如此,TWinControl最后還是將鼠標(biāo)消息交給TControl的WndProc來(lái)處理了。這里出
現(xiàn)的Perform方法在BCB的幫助里可以查到,是TControl類(lèi)中開(kāi)始出現(xiàn)的方法。它的作用
就是將指定的消息傳遞給TControl的WndProc過(guò)程。
結(jié)論就是TControl類(lèi)的WndProc方法的消息是由TWinControl類(lèi)在其重載的WndProc方法
中調(diào)用IsControlMouseMsg方法后使用Peform方法傳遞得到的。

由于這個(gè)原因,BCB和Delphi中的TControl類(lèi)及其所有的派生類(lèi)都有一個(gè)先天的而且是
必須的限制。那就是所有的TControl類(lèi)及其派生類(lèi)的Owner必須是TWinControl類(lèi)或者
TWinControl的派生類(lèi)。Owner屬性最早可以在TComponent中找到,一個(gè)組件或者控件是
由它的Owner擁有并負(fù)責(zé)釋放其內(nèi)存的。這就是說(shuō),當(dāng)Owner從內(nèi)存中釋放的時(shí)候,它所
擁有的所有控件占用的內(nèi)存也都被釋放了。Owner最好的例子就是Form。Owner同時(shí)也負(fù)
責(zé)消息的分派,當(dāng)Owner接收到消息的時(shí)候,它負(fù)責(zé)將應(yīng)該傳遞給其所擁有的控件的消
息傳遞給它們。這樣這些控件就能夠取得處理消息的能力。TImage就是個(gè)例子:你可以
發(fā)現(xiàn)Borland并沒(méi)有讓TImage重載TControl的WndProc方法,所以TImage也只有處理鼠標(biāo)
消息的能力,而這種能力正是來(lái)自TControl的。

唧唧崴崴的說(shuō)了一大堆。終于可以說(shuō)處理消息的第二種方法就是重載TControl的
WndProc方法了。例程如下:

void __fastcall TForm1::WndProc(TMessage &Message)
{
switch (Message.Msg)
{
case WM_CLOSE:
OnCLOSE(Message); // 處理WM_CLOSE消息的方法
break;
}
TForm::WndProc(Message);
}

乍看起來(lái),這和上次講的重載Dispatch方法好象差不多。但實(shí)際上還是有差別的。差別
就在先后次序上,從前面TControl的WndProc可以看到,消息是先交給WndProc來(lái)處理,
最后才調(diào)用Dispatch方法的啦。
這樣,重載WndProc方法可以比重載Dispatch方法更早一點(diǎn)點(diǎn)得到消息并處理消息。

好了,這次就說(shuō)到這里。在您的應(yīng)用程序里還有沒(méi)有比這更早得到消息的辦法呢?有,
下次再說(shuō)。

?


方法3 自TApplication的方法
不用我多廢話(huà),大家都知道TApplication在BCB中的重要性。在BCB的幫助中指出:
TApplication、TScreen和TForm構(gòu)成了所有BCB風(fēng)格的Win32 GUI程序的脊梁,他們控制
著您程序的行為。TApplication類(lèi)提供的屬性和方法封裝了標(biāo)準(zhǔn)Windows程序的行為。
TApplication表現(xiàn)了在Windows操作系統(tǒng)中創(chuàng)建、運(yùn)行、支持和銷(xiāo)毀應(yīng)用程序的基本原
理。因此,TApplication大大簡(jiǎn)化了開(kāi)發(fā)者和Windows環(huán)境之間的接口。這正是BCB的
RAD特性。

TApplication封裝的標(biāo)準(zhǔn)Windows行為大致包括如下幾部分:
1> Windows 消息處理
2> 上下文關(guān)聯(lián)的在線幫助
3> 菜單的快捷鍵和鍵盤(pán)事件處理
4> 異常處理
5> 管理由操作系統(tǒng)定義的程序基礎(chǔ)部分,如:MainWindow 主窗口、 WindowClass 窗
口類(lèi) 等。

一般情況下,BCB會(huì)為每個(gè)程序自動(dòng)生成一個(gè)TApplication類(lèi)的實(shí)例。這部分源碼可以
在yourproject.cpp文件中見(jiàn)到(這里假定您的工程名稱(chēng)就叫yourproject.bpr)。

當(dāng)然TApplication是不可見(jiàn)的,他總是在您的Form背后默默的控制著您的程序的行為。
但也不是找不到蛛絲馬跡。如果您新建一個(gè)程序(New Application),然后不作任何改
動(dòng),編譯運(yùn)行的話(huà),你會(huì)發(fā)現(xiàn)程序窗體的Caption是Form1,但在Windows的狀態(tài)條上的
Caption確寫(xiě)著project1的字樣。這就是TApplication存在的證據(jù)。當(dāng)然,這只是一種
臆測(cè),實(shí)戰(zhàn)的方法應(yīng)該打開(kāi)BCB附帶的WinSight來(lái)查看系統(tǒng)的進(jìn)程。您可以清楚的看到
TApplication類(lèi)的存在,他的大小是0(隱藏的嘛),然后才是TForm1類(lèi)。

好了,既然TApplication封裝了消息處理的內(nèi)容。我們就研究一下TApplication的實(shí)際
動(dòng)作吧。實(shí)際上消息到達(dá)BCB程序時(shí),最先得到它們的就是TApplication對(duì)象。經(jīng)由
TApplication之后,才傳遞給Form的。以前的方法都是重載TForm的方法,顯然要比本
文所提到的方法要晚一些收到消息。對(duì)您來(lái)說(shuō),是不是希望在第一時(shí)間收到消息并處理
它們呢?

要清楚的知道TApplication的處理機(jī)制還是深入VCL源碼。首先看一看最最普通的一段
代碼吧。

#include
#pragma hdrstop
USERES("Project1.res");
USEFORM("Unit1.cpp", Form1);
//--------------------------------------------------------------
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
try
{
// 初始化Application
Application->Initialize();
// 創(chuàng)建主窗口,并顯示
Application->CreateForm(__classid(TForm1), &Form1);
// 進(jìn)入消息循環(huán),直到程序退出
Application->Run();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
return 0;
}

短短的幾行代碼就可以讓您的BCB程序自如運(yùn)行。因?yàn)橐磺卸家呀?jīng)被VCL在后臺(tái)封裝好
了。Application->Run()方法進(jìn)入程序的消息循環(huán),直到程序退出。一起跟進(jìn)VCL源碼
看個(gè)究竟吧。
TApplication的定義在forms.pas中。

procedure TApplication.Run;
begin
FRunning := True;
try
AddExitProc(DoneApplication);
if FMainForm <> nil then
begin
// 設(shè)置主窗口的顯示屬性
case CmdShow of
SW_SHOWMINNOACTIVE: FMainForm.FWindowState := wsMinimized;
SW_SHOWMAXIMIZED: MainForm.WindowState := wsMaximized;
end;
if FShowMainForm then
if FMainForm.FWindowState = wsMinimized then
Minimize else
FMainForm.Visible := True;
// 看見(jiàn)了吧,這里有個(gè)循環(huán),直到Terminated屬性為真退出。Terminated什么意思,
就是取消,結(jié)束
repeat
HandleMessage
until Terminated;
end;
finally
FRunning := False;
end;
end;

消息處理的具體實(shí)現(xiàn)不在Run方法中,很顯然關(guān)鍵在HandleMessage方法,看看這函數(shù)名
字-消息處理。只有跟進(jìn)HandleMessage瞧瞧嘍。
procedure TApplication.HandleMessage;
var
Msg: TMsg;
begin
if not ProcessMessage(Msg) then Idle(Msg);
end;

咳,這里也不是案發(fā)現(xiàn)場(chǎng)。程序先將消息交給ProcessMessage方法處理。如果沒(méi)什么要
處理的,就轉(zhuǎn)入Application.Idle方法“程序在空閑時(shí)調(diào)用的方法”。
呼呼,再跟進(jìn)ProcessMessage方法吧。

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;
if Assigned(FOnMessage) then FOnMessage(Msg, Handled);
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;

哎呀呀,終于有眉目了。ProcessMessage采用了一套標(biāo)準(zhǔn)的Windows API 函數(shù)
PeekMessage .... TranslateMessage;DispatchMessage。
有人說(shuō):Application->OnMessage = MyOnMessage; //不能響應(yīng)SendMessage的消息,
但是可以響應(yīng)PostMessage發(fā)送的消息,也就是消息隊(duì)列里的消息

SendMessage和PostMessage最主要的區(qū)別在于發(fā)送的消息有沒(méi)有通過(guò)消息隊(duì)列。

原因就在這里。ProcessMessage使用了PeekMessage(Msg, 0, 0, 0, PM_REMOVE) 從消
息隊(duì)列中提取消息。然后先檢查是不是退出消息。不是的話(huà),檢查是否存在OnMessage
方法。如果存在就轉(zhuǎn)入OnMessage處理消息。最后才將消息分發(fā)出去。

這樣重載Application的OnMessage方法要比前兩種方法更早得到消息,可以說(shuō)是最快速
的方法了吧。舉個(gè)例子:

void __fastcall TForm1::MyOnMessage(tagMSG &Msg, bool &Handled)
{
TMessage Message;
switch (Msg.message)
{
case WM_KEYDOWN:
Message.Msg = Msg.message;
Message.WParam = Msg.wParam;
Message.LParam = Msg.lParam;
MessageDlg("You Pressed Key!", mtWarning, TMsgDlgButtons() << mbOK, 0);
Handled = true;
break;
}
}

void __fastcall TForm1::FormCreate(TObject *Sender)
{
Application->OnMessage = MyOnMessage;
}

現(xiàn)在可以簡(jiǎn)短的總結(jié)一下VCL的消息機(jī)制了。
標(biāo)準(zhǔn)的BCB程序使用Application->Run()進(jìn)入消息循環(huán),在Application的
ProcessMessage方法中,使用PeekMessage方法從消息隊(duì)列中提取消息,并將此消息從
消息隊(duì)列中移除。然后ProcessMessage 方法檢查是否存在Application->OnMessage方
法。存在則轉(zhuǎn)入此方法處理消息。之后再將處理過(guò)的消息分發(fā)給程序中的各個(gè)對(duì)象。至
此,WndProc方法收到消息,并進(jìn)行處理。如果有無(wú)法處理的交給重載的Dispatch方法
來(lái)處理。要是還不能處理的話(huà),再交給父類(lèi)的Dispatch方法處理。最后Dispatch方法實(shí)
際上將消息轉(zhuǎn)入DefaultHandler方法來(lái)處理。

嘿嘿,實(shí)際上,你一樣可以重載DefaultHandler方法來(lái)處理消息。但是太晚了一點(diǎn)。我
想沒(méi)有人愿意最后一個(gè)處理消息吧...:-)

寫(xiě)到這里似乎可以結(jié)束了。但如果您看過(guò)上一篇的話(huà),一定會(huì)注意到
Application->HookMainWindow方法。這又是怎么一回事呢?

如果您打算使用Application->OnMessage來(lái)捕獲所有發(fā)送至您的應(yīng)用程序的消息的話(huà),
您大概要失望了。原因已經(jīng)講過(guò),它無(wú)法捕獲使用SendMessage直接發(fā)送給窗口的消
息,因?yàn)檫@不通過(guò)消息隊(duì)列。您也許會(huì)說(shuō)我可以直接重載TApplication的WndProc方
法。呵呵,不可以。因?yàn)門(mén)Application的WndProc方法被Borland申明為靜態(tài)的,從而無(wú)
法重載。顯而易見(jiàn),這么做的原因很可能是Borland擔(dān)心其所帶來(lái)的副作用。那該如何
是好呢?

查看TApplication的WndProc的pascal源碼可以看到:

procedure TApplication.WndProc(var Message: TMessage);
... // 節(jié)約篇幅,此處與主題無(wú)關(guān)代碼略去
begin
try
Message.Result := 0;
for I := 0 to FWindowHooks.Count - 1 do
if TWindowHook(FWindowHooks[I]^)(Message) then Exit;
... // 節(jié)約篇幅,此處與主題無(wú)關(guān)代碼略去

WndProc方法一開(kāi)始先調(diào)用HookMainWindow掛鉤的自定義消息處理方法,然后再調(diào)用缺
省過(guò)程處理消息。這樣使用HookMainWindow就可以在WndProc中間接加入自己的消息處
理方法。使用這個(gè)方法響應(yīng)SendMessage發(fā)送來(lái)的消息很管用。最后提醒一下,使用
HookMainWindow掛鉤之后一定要對(duì)應(yīng)的調(diào)用UnhookMainWindow卸載鉤子程序。給個(gè)例子

void __fastcall TForm1::FormCreate(TObject *Sender)
{
Application->HookMainWindow(AppHookFunc);
}

bool __fastcall TForm1::AppHookFunc(TMessage &Message)
{
bool Handled ;
switch (Message.Msg)
{
case WM_CLOSE:
mrYes == MessageDlg("Really Close??",
mtWarning,
TMsgDlgButtons() << mbYes << mbNo,
0) ? Handled = false : Handled = true ;
break;
}
return Handled;
}

void __fastcall TForm1::FormDestroy(TObject *Sender)
{
Application->UnhookMainWindow(AppHookFunc);
}

void __fastcall TForm1::Button1Click(TObject *Sender)
{
SendMessage(Application->Handle,WM_CLOSE,0,0);
}

這樣,將本文中的兩種方法相結(jié)合,您就可以自如的處理到達(dá)您的應(yīng)用程序的各種消息
了。

與50位技術(shù)專(zhuān)家面對(duì)面20年技術(shù)見(jiàn)證,附贈(zèng)技術(shù)全景圖

總結(jié)

以上是生活随笔為你收集整理的深入BCB理解VCL的消息机制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 少妇精品视频 | 亚洲综人网 | 久久久二区 | 香蕉视频97 | 久草视频资源 | av资源在线免费观看 | 国产三级高清 | 91网站在线播放 | 国产91久久婷婷一区二区 | 欧美亚洲综合久久 | 久久久久久久性 | 日韩欧美国产片 | 清纯唯美亚洲综合 | www.射.com| va在线播放 | 91中文字幕在线播放 | 黄色大全免费观看 | 在线观看v片 | 欧美影院一区二区 | 久久久久成人精品无码中文字幕 | 国产喷水吹潮视频www | 日干夜干天天干 | 免费无遮挡在线观看视频网站 | 欧美色图亚洲色 | 一级免费黄色大片 | 丁香网五月天 | 黄色专区 | 青青草成人av | 伊人久久五月天 | 欧美一级做性受免费大片免费 | 国产做受高潮 | 欧美成人图区 | 欧美精品国产精品 | 91视频免费在线观看 | 国产精品一区二区久久 | 日日干日日摸 | 日韩夜夜高潮夜夜爽无码 | 国产1区2区在线观看 | 久久只有精品 | 精品久久精品 | 少妇无码一区二区三区免费 | 国产又黄又猛又粗 | 欧美日韩综合在线 | 国产又粗又黄又爽又硬 | 日本一级理论片在线大全 | 国产区福利 | 国产精品一区二区无码对白 | 69毛片| 主播一区二区 | 亚洲开心网 | 中文字幕免费高 | 日免费视频 | 户外少妇对白啪啪野战 | 男男全肉变态重口高h | 久久久99精品国产一区二区三区 | 亚洲 小说区 图片区 都市 | 午夜亚洲一区 | 欧美日韩激情在线一区二区三区 | 99精品久久毛片a片 成人网一区 | 高清在线一区二区 | 国产成人精品久久二区二区 | 潮喷失禁大喷水无码 | 高清不卡毛片 | 最近最新最好看的2019 | 欧美性猛交99久久久久99按摩 | 国产精华7777777| 美国爱爱视频 | 日韩一区二区高清 | 黄色欧美网站 | 人妻久久久一区二区三区 | 亚洲永久免费 | 天堂伊人网 | 豆花免费跳转入口官网 | 国产视频在线一区二区 | 你懂的在线免费观看 | 欧美草草 | 中文字幕有码视频 | 亚洲自拍另类 | 女人高潮娇喘声mp3 乱色视频 | 91免费精品视频 | 欧美人体视频 | 亚洲综合福利 | 久久久一二三四 | 欧美hdxxxx | 偷偷操av | 国产成人免费观看视频 | 国产精品无码一区二区三区 | 亚洲一区电影 | 国产精品99久久久久久一二区 | 成年人性生活免费视频 | 少妇激情视频 | 在线观看天堂av | 91亚洲精选 | 国产良妇出轨视频在线观看 | 爱情岛论语亚洲入口 | 申鹤乳液狂飙 | 中文字幕在线播出 | 久操综合| 欧美日韩中字 |