VCL消息机制概述

消息本身是作为一个记录传递给应用程序的,记录中包含消息的类型以及其它的信息!这
个记录类型叫做TMsg,它在WINDOWS单元中声明,这里就不一一列举(偶打字很辛苦的:P)

在WIN32中预定义的一些消息常量往往是以WM开头,以代表某一特定的消息。DELPHI的ME
SSAGE单元中定义了所有WINDOWS消息,如果有兴趣可以自己打开MESSAGE单元研究一下!

WINDOWS的消息系统由3部分组成:(1)消息队列,WINDOWS能够为所有的应用程序维护
一个消息队列,应用程序必须从消息队列中获取消息,然后分派给某个窗口。(2)消息循环
,通过这个循环机制,应用程序从消息队列中检索消息,再把它分派给适当的窗口,依次进
行。(3)窗口过程,每个窗口都有一个窗口过程,以接收WINDOWS传递给窗口的消息,窗口
过程的任务就是要获取消息并响应它(窗口过程是一个回调函数,处理完一个消息后,通常
要给WINDOWS一个返回值)。

从消息的产生到消息被一个窗口响应,这其中要经历以下几个步骤:(1)系统中发生了
某个事件。(2)WINDOWS把这个事件翻译成消息,然后把它放在消息队列中。(3)应用程序
从消息队列中接收这个消息,并把它存放在TMsg记录中。(4)应用程序把消息传递给一个适
当的窗口过程。(5)窗口过程响应这个消息并进行处理。

下面说说DELPHI的VCL消息系统处理原理:

DELPHI中的每一个VCL组件都有内在的消息处理机制,在建立一个窗体或加入一个组件
时,VCL就已经注册了一个消息接收例程MainWndProc,这个例程是每个窗体和组件固有的。
MainWndProc是定义在TWinControl类中的一个静态方法,不能被重载。它不直接处理消息,
而是交给WndProc处理并提供异常保护。如有异常发生,则调用Application.HandleExcepti
on方法处理异常。具体可以自己查看TWinControl类MainWndProc的实现代码,偶这里就不贴
了!

procedure TWinControl.MainWndProc(var Message: TMessage);
begin
try
try
WindowProc(Message);
finally
FreeDeviceContexts;
FreeMemoryContexts;
end;
except
Application.HandleException(Self);
end;
end;

WndProc是TControl类中的一个虚拟方法,这意味着它可以被覆盖,提供自定义的消息处
理例程(事实上,TControl构件中就是利用它来过滤并处理所有的鼠标消息(从WM_MOUSEFI
RST到WM_MOUSELAST)的)。WndProc调用DisPatch方法进行消息分配,具体代码可以查看TC
ontrol类WndProc的代码实现。

DisPatch方法是在TObject根类中定义的,传递给它的参数必须是一个记录类型,这个记
录中的第一个点必须是个Cardinal类型的字段。DisPatch方法根据消息号码调用组件的最后
继承类中处理此消息的句柄方法。如果此组件和它的祖先类中都没有对应此消息的处理句柄
,DisPatch方法便会调用DefaultHandler方法,DisPatch方法是定义于TObject类中的虚拟方
法,它只是简单的返回而不对消息做任何处理。我们可以对此虚拟方法的重载,在子类中实
现对消息的缺省处理(OH……MY GOD,怎么掉线了~~)。

这就是DELPHI对WINDOWS消息的处理流程!

发送消息:

DELPHI主要通过三种方式发送消息。Perform(),使用于所有的TControl派生对象。S
endMessage()和PostMessage()。这些想必都比较熟悉,不再多说了!如果需要跨进程发
送消息,就要用到RegisterWindowMessage(),它能够确保每个应用程序使用一致的消息序
号。具体使用方法可以参考帮助,很简单的!TWinControl派生的对象可以调用Broadcast(
)向它的子组件广播一个消息。当需要向一组组件发送相同的消息时,便可以使用这种技术
!如果要用SendMessage()或PostMessage()实现广播消息,只需要把第一个参数——目
标对象句柄——设置为HWND_BROADCAST就可以,它代表向所有应用的主窗口发送消息!

消息过滤:DELPHI消息过滤一般有3种方法,重载构件继承的虚方法WndProc;针对某消
息编写消息处理句柄;重载构件继承的虚方法DefaultHandler。其中第二中方法比较常用!

procedure TDockTree.WindowProc(var Message: TMessage);

procedure CalcSplitterPos;
var
MinWidth,
TestLimit: Integer;
begin
MinWidth := FGrabberSize;
if (FSizingZone.FParentZone.FOrientation = doHorizontal) then
begin
TestLimit := FSizingZone.Top + MinWidth;
if FSizePos.y <= TestLimit then FSizePos.y := TestLimit;
TestLimit := GetNextLimit(FSizingZone) - MinWidth;
if FSizePos.y >= TestLimit then FSizePos.y := TestLimit;
end
else begin
TestLimit := FSizingZone.Left + MinWidth;
if FSizePos.x <= TestLimit then FSizePos.x := TestLimit;
TestLimit := GetNextLimit(FSizingZone) - MinWidth;
if FSizePos.x >= TestLimit then FSizePos.x := TestLimit;
end;
end;

const
SizeCursors: array[TDockOrientation] of TCursor = (crDefault, crVSplit, crHSplit);
var
TempZone: TDockZone;
Control: TControl;
P: TPoint;
R: TRect;
HitTestValue: Integer;
Msg: TMsg;
begin
case Message.Msg of
CM_DOCKNOTIFICATION:
with TCMDockNotification(Message) do
if (NotifyRec.ClientMsg = CM_VISIBLECHANGED) then
ControlVisibilityChanged(Client, Boolean(NotifyRec.MsgWParam));
WM_MOUSEMOVE:
if FSizingZone <> nil then
begin
DrawSizeSplitter;
FSizePos := SmallPointToPoint(TWMMouse(Message).Pos);
CalcSplitterPos;
DrawSizeSplitter;
end;
WM_LBUTTONDBLCLK:
begin
TempZone := InternalHitTest(SmallPointToPoint(TWMMouse(Message).Pos),
HitTestValue);
if TempZone <> nil then
with TempZone do
if (FChildControl <> nil) and (HitTestValue = HTCAPTION) then
begin
CancelDrag;
FChildControl.ManualDock(nil, nil, alTop);
end;
end;
WM_LBUTTONDOWN:
begin
P := SmallPointToPoint(TWMMouse(Message).Pos);
TempZone := InternalHitTest(P, HitTestValue);
if (TempZone <> nil) then
begin
if HitTestValue = HTBORDER then
SplitterMouseDown(TempZone, P)
else if HitTestValue = HTCAPTION then
begin
if (not PeekMessage(Msg, FDockSite.Handle, WM_LBUTTONDBLCLK,
WM_LBUTTONDBLCLK, PM_NOREMOVE)) and
(TempZone.FChildControl is TWinControl) then
TWinControl(TempZone.FChildControl).SetFocus;
if (TempZone.FChildControl.DragKind = dkDock) and
(TempZone.FChildControl.DragMode = dmAutomatic)then
TempZone.FChildControl.BeginDrag(False);
Exit;
end;
end;
end;
WM_LBUTTONUP:
if FSizingZone = nil then
begin
P := SmallPointToPoint(TWMMouse(Message).Pos);
TempZone := InternalHitTest(P, HitTestValue);
if (TempZone <> nil) and (HitTestValue = HTCLOSE) then
begin
if TempZone.FChildControl is TCustomForm then
TCustomForm(TempZone.FChildControl).Close
else
TempZone.FChildControl.Visible := False;
end;
end
else
SplitterMouseUp;
WM_SETCURSOR:
begin
GetCursorPos(P);
P := FDockSite.ScreenToClient(P);
with TWMSetCursor(Message) do
if (Smallint(HitTest) = HTCLIENT) and (CursorWnd = FDockSite.Handle)
and (FDockSite.VisibleDockClientCount > 0) then
begin
TempZone := InternalHitTest(P, HitTestValue);
if (TempZone <> nil) and (HitTestValue = HTBORDER) then
begin
Windows.SetCursor(Screen.Cursors[SizeCursors[TempZone.FParentZone.
FOrientation]]);
Result := 1;
Exit;
end;
end;
end;
CM_HINTSHOW:
with TCMHintShow(Message) do
begin
FOldWndProc(Message);
if Result = 0 then
begin
Control := HitTest(HintInfo^.CursorPos, HitTestValue);
if HitTestValue = HTBORDER then
HintInfo^.HintStr := ‘‘
else if (Control <> nil) and (HitTestValue in [HTCAPTION, HTCLOSE]) then
begin
R := Control.BoundsRect;
AdjustDockRect(Control, R);
Dec(R.Left, 2 * (R.Left - Control.Left));
Dec(R.Top, 2 * (R.Top - Control.Top));
Dec(R.Right, 2 * (Control.Width - (R.Right - R.Left)));
Dec(R.Bottom, 2 * (Control.Height - (R.Bottom - R.Top)));
HintInfo^.HintStr := Control.Caption;
HintInfo^.CursorRect := R;
end;
end;
Exit;
end;
end;
if Assigned(FOldWndProc) then
FOldWndProc(Message);
end;

时间: 2024-10-10 00:19:49

VCL消息机制概述的相关文章

【温故Delphi】之VCL消息机制小结

TObject消息分派 procedure Dispatch(var Message); virtual; #负责分派消息到特定VCL组件的事件处理函数 procedure DefaultHandler(var Message); virtual; #消息分类:通过子类覆盖此方法,处理窗口命令消息,窗口标准消息,VCL自行触发消息,VCL通知消息,自定义 VCL封装类的方法种类 procedure WMButtonUp(var Message: TWMButtonUp); message WM_

【原创】源码角度分析Android的消息机制系列(一)——Android消息机制概述

ι 版权声明:本文为博主原创文章,未经博主允许不得转载. 1.为什么需要Android的消息机制 因为Android系统不允许在子线程中去访问UI,即Android系统不允许在子线程中更新UI. 为什么不允许在子线程中更新UI呢?因为Android的控件不是线程安全的.既然是非线程安全的,那么若在多个子线程中并发访问,UI控制可能会处于一种不可预期的状态.有的读者可能会说,为什么不对UI控件加锁呢?加锁会降低UI访问的效率,因为加锁之后,若想要运行这段synchronized的代码,线程要先拿到

Windows消息机制概述

消息是指什么?     消息系统对于一个win32程序来说十分重要,它是一个程序运行的动力源泉.一个消息,是系统定义的一个32位的值,他唯一的定义了一个事件,向 Windows发出一个通知,告诉应用程序某个事情发生了.例如,单击鼠标.改变窗口尺寸.按下键盘上的一个键都会使Windows发送一个消息给应用程序.    消息本身是作为一个记录传递给应用程序的,这个记录中包含了消息的类型以及其他信息.例如,对于单击鼠标所产生的消息来说,这个记录中包含了单击鼠标时的坐标.这个记录类型叫做MSG,MSG含

Android的消息机制

1.背景                                                                Handler是Android消息机制的上层接口,通过handler可以轻松地将一个任务切换到Handler所在的线程中去执行. Handler的作用之一是更新UI,有时候需要在子线程中进行耗时的I/O操作,可能是读取文件或者访问网络等,当耗时操作完成以后可能需要在UI上做一些改变,这时用Handler. Android的消息机制主要是指Handler的运行机制

【原创】源码角度分析Android的消息机制系列(五)——Looper的工作原理

ι 版权声明:本文为博主原创文章,未经博主允许不得转载. Looper在Android的消息机制中就是用来进行消息循环的.它会不停地循环,去MessageQueue中查看是否有新消息,如果有消息就立刻处理该消息,否则就一直等待. Looper中有一个属性: static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); 这也就解释了,前面我们所说的我们可以通过ThreadLocal实现Looper

Android的消息机制Handler详解

Android的消息机制详解 Android的消息机制主要指 Handler 的运行机制,Handler的运行需要底层的MessageQueue 和 Looper 的支撑. MessageQueue:消息队列,它的内部存储了一组消息,以队列的形式对外提供插入和删除的工作,其内部存储结构采用单链表的数据结构来存储消息列表. Looper:可理解为消息循环. 由于MessageQueue只是一个消息存储单元,不能去处理消息,而Looper会以无限循环的形式去查找是否有新的消息,如果有的话就处理,否则

Android消息机制(基于源码解析)

1. 消息机制概述 Android中的消息机制主要指的是Handler的运行机制,Handler的运行需要底层的MessageQueue和Looper.Message的支撑,下文会逐一分析. 2. 为什么需要消息机制 Android中的消息机制主要是为了满足线程间通信而设计的,最重要的应用场景应该在于更新UI Android规定访问UI只能在主线程中进行,如果在子线程中访问UI,那么程序就会抛出异常 系统为什么不允许在自线程中访问UI呢?这是因为Android的UI控件不是线程安全的,如果在多线

Android 开发艺术探索——第十章 Android的消息机制

Android 开发艺术探索--第十章 Android的消息机制读书笔记 Handler并不是专门用于更新UI的,只是常被用来更新UI 概述 Android的消息机制主要值得就是Handler的运行机制,Handler的运行需要底层的MessageQueue和Looper的支撑. MessageQueue即为消息队列,顾名思义,它的内部存储了一组消息,以队列的的形式对外提供插入和删除的工作.虽然叫队列,但内部存储结构并不是真正的队列,而是采用单链表的数据结构来存储消息列表. Looper意思为循

[Android]简略的Android消息机制源码分析

相关源码 framework/base/core/java/andorid/os/Handler.java framework/base/core/java/andorid/os/Looper.java framework/base/core/java/andorid/os/Message.java framework/base/core/java/andorid/os/MessageQueue.java libcore/luni/src/main/java/java/lang/ThreadLo