好奇一下。看来Object Pascal确实与Windows深入结合了。

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls; const
UM_Test = WM_USER + ; type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure MyMessage(var Msg: TMessage); message UM_Test;
end; var
Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject);
begin
Perform(UM_Test, , );
end; procedure TForm1.MyMessage(var Msg: TMessage);
begin
inherited;
ShowMessage('Hello');
end; end.

在message处理中和其他不一样的是inherited不会因为没有在祖先中找到一样的函数或者方法而将inherited失效,他会传入缺省的消息处理.
这里调用TFORM1的祖先的消息处理,由于tform和tcustomform没有这个实现UM_Test函数,所以直接调用的是tcustomform的defaulthandle.(注意这个方法是对twincontrol的override)。

但是,如果本类重载了DefaultHandler函数,就会直接调用本类的DefaultHandler函数:

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls; const
UM_Test = WM_USER + ; type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure MyMessage(var Msg: TMessage); message UM_Test;
procedure DefaultHandler(var Message); override;
end; var
Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject);
begin
Perform(UM_Test, , );
end; procedure TForm1.DefaultHandler(var Message);
begin
with TMessage(Message) do
begin
if Msg = UM_Test then ShowMessage('DefaultHandler');
end;
inherited;
end; procedure TForm1.MyMessage(var Msg: TMessage);
begin
inherited;
ShowMessage('Hello');
end; end.

顺便再看看这样改写的效果:

procedure TForm1.DefaultHandler(var Message);
begin
with TMessage(Message) do
begin
if Msg = UM_Test then ShowMessage('DefaultHandler');
if Msg = WM_SETTEXT then ShowMessage('WM_SETTEXT');
end;
inherited;
end;

理论解释:

因为WndProc里面调用了Dispatch,而Dispatch实际上是尝试调用动态方法,在动态方法表中查找Index是指定的方法,如果一直找到TObject都找不到Index是这个的方法,就会调用DefaultHandler。
这里面有几个概念,动态方法表和虚方法表,不要混了。
动态方法实际上在Class中是个最大65536的函数数组。调用的时候会根据Index到这个数组中来调用。而消息方法实际就是动态方法,方法声明后面的Message实际上就是Index。
VCL之所以对消息处理效率比VC等高,就是因为这个动态方法表,实际调用的就是用消息做下标直接调用方法。最多就是找几层继承的父类。而比VC的遍历要快。

Inherited动态方法的时候就是到父类的动态方法表找Index等于本方法Index的方法。如果父类没有这个方法就再到父类的父类找。都找不到就调用DefaultHandler。

动态方法和虚方法相比优点是省内存,另外能对Message的处理比较方便。虚方法不能做到对消息的方便处理。
虚方法则比较费内存,每个派生类都要有一套虚方法表。但是被调用的时候比动态方法要快,不需要到父类去找。

理论解释2:

你恰恰理解反了,不是Inherited会调用Dispatch,而是Dispatch会调用动态方法.
消息的调用次序实际上是
WndProc->Dispatch->动态方法或者DefaultHandler方法.

TControl.WndProc的最后一句就是Dispatch.
然后Dispatch就找Index的动态方法调用.
如果找不到就调用到DefaultHandler.
如果找到,就调用动态方法.
如果动态方法里面调用了Inherited,那么实际上inherited是编译器处理的.如果在祖宗里面有对应index的方法,那么就直接编译成调用该方法.如果祖宗里面没有该index的方法编译成调用DefaultHandler(存疑).
实际上这样处理效率比运行时处理要快.

Inherited必须由编译器处理的,因为Inherited有两种含义.动态方法和虚方法的实现机制不同必然要根据方法编译成不同的代码.

====================================================================

procedure TObject.Dispatch(var Message);
asm
PUSH ESI
MOV SI,[EDX] //获取index
OR SI,SI
JE @@default //如果是0,到default,也就是调用DefaultHandler
CMP SI,0C000H
JAE @@default //如果不在合理范围内,到default,也就是调用DefaultHandler
PUSH EAX
MOV EAX,[EAX]
CALL GetDynaMethod //获取动态方法
POP EAX
JE @@default //如果GetDynaMethod返回的是nil,到default,也就是调用DefaultHandler
MOV ECX,ESI
POP ESI
JMP ECX //跳转到动态方法 @@default:
POP ESI
MOV ECX,[EAX]
JMP DWORD PTR [ECX] + VMTOFFSET TObject.DefaultHandler
end; procedure GetDynaMethod;
{ function GetDynaMethod(vmt: TClass; selector: Smallint) : Pointer; }
asm
{ -> EAX vmt of class }
{ SI dynamic method index }
{ <- ESI pointer to routine }
{ ZF = 0 if found }
{ trashes: EAX, ECX } PUSH EDI
XCHG EAX,ESI
JMP @@haveVMT
@@outerLoop:
MOV ESI,[ESI]
@@haveVMT:
MOV EDI,[ESI].vmtDynamicTable //根据vmt获取该类的动态方法表
TEST EDI,EDI
JE @@parent
MOVZX ECX,word ptr [EDI]
PUSH ECX
ADD EDI,
REPNE SCASW
JE @@found //跳到found
POP ECX
@@parent:
MOV ESI,[ESI].vmtParent //为空,那么找父类的
TEST ESI,ESI //测试父类是否为空
JNE @@outerLoop //跳出循环
JMP @@exit //退出 @@found:
POP EAX
ADD EAX,EAX
SUB EAX,ECX { this will always clear the Z-flag ! }
MOV ESI,[EDI+EAX*-] @@exit:
POP EDI
end;

对应的pascal代码如下

procedure TObject.Dispatch(var Message);
type
//THandlerProc = procedure(Self: Pointer; var Message) { of object };
THandlerProc = procedure(var Message) of object;
var
MsgID: Word;
Addr: Pointer;
M: THandlerProc;
begin
MsgID := TDispatchMessage(Message).MsgID;//消息id,也就是动态方法表的索引
if (MsgID <> ) and (MsgID < $C000) then
begin
Addr := GetDynaMethod(PPointer(Self)^, MsgID);//获取class的动态方法表
if Addr <> nil then//如果拿到了动态方法就调用
begin
//THandlerProc(Addr)(Self, Message)
TMethod(M).Data := Self;
TMethod(M).Code := Addr;
M(Message);
end
else
Self.DefaultHandler(Message);//如果找不到动态方法则调用defaultHandler
end
else
Self.DefaultHandler(Message);//如果index范围不在应该在的范围内也调用DefaultHandler
end; function GetDynaMethod(vmt: TClass; selector: SmallInt): Pointer;
type
TDynaMethodTable = record
Count: Word;
Selectors: array[..] of SmallInt;
{Addrs: array[0..0] of Pointer;}
end;
PDynaMethodTable = ^TDynaMethodTable;
var
dynaTab: PDynaMethodTable;
Parent: Pointer;
Addrs: PPointer;
I: Cardinal;
begin
while True do
begin
dynaTab := PPointer(PByte(vmt) + vmtDynamicTable)^;//根据vmt获取该类的动态方法表
if dynaTab <> nil then
begin
for I := to dynaTab.Count - do
if dynaTab.Selectors[I] = selector then
begin
Addrs := PPointer(PByte(@dynaTab.Selectors) + dynaTab.Count * SizeOf(dynaTab.Selectors[]));
Result := PPointer(PByte(Addrs) + I * SizeOf(Pointer))^;
Exit;//能找到则退出
end;
end;
Parent := PPointer(PByte(vmt) + vmtParent)^;//找不到则找父类的
if Parent = nil then Break;//如果父类是nil,也就是tobject了,则退出
vmt := PPointer(Parent)^;
end;
Result := nil;
end;

inherited在消息中的作用(编译器根据inherited所在的函数,直接转换成对祖先类同名动态函数的调用,或者转换成对DefaultHandler的调用)的更多相关文章

  1. python装饰器中@wraps作用--修复被装饰后的函数名等属性的改变

    Python装饰器(decorator)在实现的时候,被装饰后的函数其实已经是另外一个函数了(函数名等函数属性会发生改变),为了不影响,Python的functools包中提供了一个叫wraps的de ...

  2. SET STATISTICS IO和SET STATISTICS TIME 在SQL Server查询性能优化中的作用

    近段时间以来,一直在探究SQL Server查询性能的问题,当然也漫无目的的查找了很多资料,也从网上的大神们的文章中学到了很多,在这里,向各位大神致敬.正是受大神们无私奉献精神的影响,所以小弟也作为回 ...

  3. SLAM+语音机器人DIY系列:(二)ROS入门——8.理解roslaunch在大型项目中的作用

    摘要 ROS机器人操作系统在机器人应用领域很流行,依托代码开源和模块间协作等特性,给机器人开发者带来了很大的方便.我们的机器人“miiboo”中的大部分程序也采用ROS进行开发,所以本文就重点对ROS ...

  4. __setup 在内核中的作用【转】

    本文转载自:http://blog.csdn.net/lanmanck/article/details/7613305 本文来自: http://blog.chinaunix.net/uid-1379 ...

  5. [转载]java中import作用详解

    [转载]java中import作用详解 来源: https://blog.csdn.net/qq_25665807/article/details/74747868 这篇博客讲的真的很清楚,这个作者很 ...

  6. static在c\c++中的作用(翁恺c++公开课[28-29]学习笔记)

    static相对来说是一个较复杂的修饰符,c++中的static在c的基础之上又包含了static在类中的应用(也就是说多了static的成员变量和static的成员函数):c\c++中静态变量.对象 ...

  7. static在C/C++中的作用-(转自华山大师兄)

    1.先来介绍它的第一条也是最重要的一条:隐藏.(static函数,static变量均可) 当同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性.举例来说明.同时编译两个源文件 ...

  8. ZeroMQ接口函数之 :zmq_msg_copy - 把一个消息的内容复制到另一个消息中

    ZeroMQ 官方地址 :http://api.zeromq.org/4-1:zmq_msg_copy zmq_msg_copy(3)   ØMQ Manual - ØMQ/3.2.5 Name zm ...

  9. 微软承诺将在今年的 Visual C++ 更新中加入 Clang 编译器

    微软最近发布将在2015年11月 Visual C++ 更新中加入 Clang 编译器 ,Clang 开源编译器以相比GCC更快的编译速度和更优的错误提示著称. Clang关于C,C++,及Objec ...

随机推荐

  1. iOS学习笔记03-UITableView

    一.UITableView基本介绍 默认的UITableView有2种风格: UITableViewStylePlain(不分组) UITableViewStyleGrouped(分组) UITabl ...

  2. BZOJ3572 [Hnoi2014]世界树 【虚树 + 树形dp】

    题目 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息.持续运转的根本基石. ...

  3. 【2018.10.10】[HNOI2008] GT考试(bzoj1009)

    10pts: 暴力枚举字符串,Hash判是否出现.(真会有人写么) 时间复杂度$O(10^n*n)$. 40pts: 学过OI的人都会写的dp 如果这道题的40pts($n\le 250000$)设成 ...

  4. 洛谷 [P4035] 球形空间生成器

    高斯消元 注意浮点误差,判断一个浮点数是否为 0 的时候,看他的绝对值与 \(10^{-8}\)的关系 #include <iostream> #include <algorithm ...

  5. 如何让Gridview在没有数据的时候显示表头[没有使用SqlDataSource控件时]

    原文发布时间为:2008-08-03 -- 来源于本人的百度文章 [由搬家工具导入] 要看全文请点击http://blog.csdn.net/windok2004/archive/2007/10/28 ...

  6. javascript事件委托和jQuery事件绑定on、off 和one以及on绑定多个事件(重要)

    一. 事件委托什么是事件委托?用现实中的理解就是:有100 个学生同时在某天中午收到快递,但这100 个学生不可能同时站在学校门口等,那么都会委托门卫去收取,然后再逐个交给学生.而在jQuery 中, ...

  7. android 布局之LinearLayout(学习一)

    一,View localView = mRadioGroup_content.getChildAt(i);指定自定义菜单栏的点击格,如child3 其中:mRadioGroup_content = ( ...

  8. nginx配置 location root alias

    语法规则: location [=|~|~*|^~] /uri/ { … } = 开头表示精确匹配 ^~ 开头表示uri以某个常规字符串开头,理解为匹配 url路径即可.nginx不对url做编码,因 ...

  9. python--输出自己需要的字符串连接的的方式

    python中有很多字符串连接方式,今天在写代码,顺便总结一下,从最原始的字符串连接方式到字符串列表连接,大家感受下: 最原始的字符串连接方式:str1 + str2 python 新字符串连接语法: ...

  10. Fast I/O 模板

    [来源:2017 Multi-University Training Contest - Team 1] //面包有毒:P #define BUF_SIZE 100000 //fread -> ...