好奇一下。看来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. 【Luogu】P3567Kur-Couriers(主席树)

    题目链接 数组大小开到一千二百万才过- - 可以把数先离散化再全都加到主席树中. 对于一个区间[from,to] 取中间点mid 看看小于mid的数有多少个,如果个数的两倍<=to-from+1 ...

  2. 16.1113 模拟考试T3

    城堡[问题描述]给定一张N个点M条边的无向连通图,每条边有边权.我们需要从M条边中选出N − 1条, 构成一棵树. 记原图中从 1 号点到每个节点的最短路径长度为?Di ,树中从 1 号点到每个节点的 ...

  3. DFS与BFS对比

    前面已经说过了深搜和广搜了,是不是有点还不是很好的分清他们?(其实分不分的请都没大有关系) 下面我们来看一看广搜与深搜的区别吧. 算法步骤上的区别 深度优先遍历图算法步骤: 1.访问顶点v 2,.依次 ...

  4. TOT 傅立叶变换 FFT 入门

    HDU 1402,计算很大的两个数相乘. FFT 只要78ms,这里: 一些FFT 入门资料:http://wenku.baidu.com/view/8bfb0bd476a20029bd642d85. ...

  5. [Bzoj3677][Apio2014]连珠线(树形dp)

    3677: [Apio2014]连珠线 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 434  Solved: 270[Submit][Status] ...

  6. Codechef Yet another cute girl

    题意大概就是让你求一下[L,R]中的约数个数是素数的数的个数. 其中1<=L<=R<=1e12,R-L<=1e6. 然后我写了两种做法,第一种是可以直接搞出来L-R的约数个数, ...

  7. Idea Failed to read artifact descriptor for xx:jar:unknown

    网上的解决方案: 根据网上说明添加了maven命令clean compile install -Dmaven.test.skip=true,与我遇到的问题不同 有的方法猜测可以通过,但是没时间测试了 ...

  8. Liunx 下Redis 的安装

    一.Redis 的简介 Redis是一款开源的.高性能的键-值存储.它常被称作是一款数据结构服务器,它是一个key-value存储系统.和Memcache类似,Memecache只支持字符窜的数据类型 ...

  9. Hadoop安装和基本单机部署

    下载安装  # 下载 $ cd /usr/local $ wget http://mirrors.hust.edu.cn/apache/hadoop/common/hadoop-2.9.2/hadoo ...

  10. SQL2000数据库密码被替换,重置密码提示未能找到存储过程sp_password解决方案

    利用windows身份验证进入查询分析器后在master数据库下运行如下脚本: create procedure sp_password @old sysname = NULL, -- the old ...