delphi程序设计之底层原理

虽然用delphi也有7,8年了,但大部分时间还是用在系统的架构上,对delphi底层还是一知半解,今天在网上看到一篇文章写得很好,虽然是07年的,但仍有借鉴的价值。

现摘录如下:

Delphi程序设计之--经验技巧

这些日子太忙了,今天把剩下的部分贴完,希望对大家有用。看过前一篇的都知道此文的作者和出处,我就不详细说了。
 
{ No. 16 }

//对于记录类型Record的分析。

实例:

type
 TBaseRec = record
   rStr: Integer;
   rStr2: String;
   rStr3: String;
 end;

TStrRec = record
   rStr: Integer;
   rStr2: String;
   rStr3: String;
   rStr4: String;
 end;

procedure TForm1.Button3Click(Sender: TObject);
var
 vRec1: TStrRec;
 vBaseRec: TBaseRec;
begin
 vRec1.rStr := 1;
 vRec1.rStr2 := ‘123123‘;
 vRec1.rStr3 := ‘1‘;
 vRec1.rStr4 := ‘1‘;

vBaseRec := TBaseRec(Pointer(@vRec1)^);

ShowMessage(IntToStr(vBaseRec.rStr) + ‘_‘ + vBaseRec.rStr2 + ‘_‘ + vBaseRec.rStr3);
//
end;

{说明:
1、记录类型互相转换时,必须保证基础Record类型,数据大小Sizeof应小于或等于扩展类型。保证转换后的记录类型对象的数据访问合法正确。
2、在Delphi中,使用记录类型互相转换最为平凡的就是在消息Record的实现上了。在Delphi中定义了若干于TMessage可同时描述消息接受信息的Record,如:
 TWMKey = packed record
   Msg: Cardinal;
   CharCode: Word;
   Unused: Word;
   KeyData: Longint;
   Result: Longint;
 end;
 当需要接受KeyDown和KeyUp的消息时,我们即可以使用TMessage也可以使用TWMKey作为消息接收的参数类型。因为Delphi为我们提供了若干便利的消息类型,所以我们在使用消息处理问世时就不会象VC中那样繁琐和易错了。
3、记录类型的使用还提供了一个不同语言间数据信息封装访问的途径。在不同语言间使用记录类型和记录类型指针时,应注意内部定义的变量的类型匹配问题。

记录类型的本质测试研究:
更改上面例子的实现部分,测试:
procedure TForm1.Button3Click(Sender: TObject);
var
 vRec1: TStrRec;
 rStr: Integer;
 rStr2: String;
 rStr3: String;
 vpt: Integer;
begin
 vRec1.rStr := 1;
 vRec1.rStr2 := ‘123123‘;
 vRec1.rStr3 := ‘1‘;
 vRec1.rStr4 := ‘1‘;

vpt := Integer(@vRec1);
 rStr := Integer(Pointer(vpt)^);
 vPt := vPt + Sizeof(rStr);
 rStr2 := String(Pointer(vpt)^);
 vPt := vPt + Sizeof(rStr2);
 rStr3 := String(Pointer(vpt)^);

ShowMessage(IntToStr(rStr) + ‘_‘ + rStr2 + ‘_‘ + rStr3);
end;
提示信息于开始例子相同,则推测:
1、Record类型中定义的数据是在一个连续空间中保存的
2、当定义函数时,如果考虑到函数处理的信息可能在后续版本中,需要扩充则可以使用记录变量的方式传递参数。当扩充函数时只需将记录变量根据此记录的版本号转换为对应的记录类型变量进行访问即可。具体实例可以参考Windows API函数的版本升级及扩展情况。
}

2003-6-17 19:52:00    
 2003-6-17 22:22:02    事件类型属性,通过属性赋值函数操作 
{ No. 17 }

{在TypInfo单元中,有若干函数可以让我们操作Dephi管理的类的VMT。通过,属性名称和对象VTM直接访问或改变属性值。
公共的属性访问函数:GetPropValue;公共的属性设置函数:SetPropValue。其中,对事件属性信息的读取可以使用GetPropValue,但是却不能通过SetPropValue给事件属性赋值。
解决方案:使用SetMethodProp给控件属性赋值。procedure SetMethodProp(Instance: TObject; const PropName: string; const Value: TMethod); overload;其中,TMethod 用以描述操作函数。
 TMethod = record
   Code,  //函数地址;可以通过类函数MethodAddress,取得函数地址。其中,只有声明在Published段的函数才能通过MethodAddress访问。
   Data: Pointer; //对象地址
 end;
}

//**************** 正常使用
type
 TMyForm = class(TForm)
  ...
 private
   FMyText: String;
 published
   procedure MyClick(Sender: TObject); 
 end;
 
//窗体中,按钮事件;实现动态分配另一个按钮的事件的方法
procedure TMyForm.Button1OnClick(Sender: TObject);
var
 vMethod: TMethod;
begin
 FMyText := ‘Hello Joy!‘;
 vMethod.Code := Self.MethodAddress(‘MyClick‘);      //************ Code 1
 vMethod.Data := Self;                               //************ Code 2
 SetMethodProp(Button2, ‘OnClick‘, vMethod);
end;

procedure TMyForm.MyClick(Sender: TObject);
begin
  ShowMessage(‘Ok!‘);                                //************ Show 1     
  ShowMessage(Self.FMyText);            //************ Show 2      
end;
 
//**************** 修改 一
//将[Code 1]和[Code 2]处的Self变更为TMyForm。则[Show 1]显示正常,[Show 2]显示不正常。
//说明:当类的函数被执行时,寄存器eax保存的是当前类的地址。所以,TMethod.Data中保存的应该是将来执行TMethod.Code函数时,赋给eax的值,即类对象指针。
//又因为[Show 1]中,不需要eax中类对象指针,所以可以正常执行。

//**************** 修改 二
//函数地址读取部分
procedure TMyForm.Button1OnClick(Sender: TObject);
var
 vMethod: TMethod;
 vEvent: TNotifyEvent;
begin
 FMyText := ‘Hello Joy!‘;
 vEvent := MyClick;
 vMethod.Code := @vEvent;                            //************ Code 1
 vMethod.Data := Self;                               //************ Code 2
 SetMethodProp(Button2, ‘OnClick‘, vMethod);
end;
//其中,MyClick可以定义为私有函数。
//说明:vMethod只是要记录一个类的函数地址和类对象的地址。MethodAddress函数也只是通过函数名称进行函数地址的读取而已。

2003-6-18 12:48:38    不通过汇编访问 [VMT] 
VMT:Virtual Method Table

//访问VMT信息
e.g.
procedure TForm1.Button1Click(Sender: TObject);
var
 vpt: Pointer;
 vMethod: TMethod;
begin
 vMethod.Code := TForm.MethodAddress(‘MyClick‘);
 vMethod.Data := Self;
 if vMethod.Code = nil then ShowMessage(‘Error!‘);
 vPt := Pointer(TListBox);  
 Integer(vPt) := Integer(vPt) + vmtTypeInfo; //在Delphi帮助中说明,根据偏移量可以取得ClassInfo。或者可以参考,TObject.ClassInfo的定义。
 SetMethodProp(ListBox2, GetPropInfo(pTypeInfo(vPt^), ‘OnClick‘), vMethod);
end;

//其中,类的类到底如何提取?
 function TObject.ClassType: TClass;
 begin
     mov eax, [eax]
 end;

//上面的语句可以翻译成
 function TObject.ClassType: TClass;
 begin  
    Result := TClass(Pointer(Self)^);
 end;

//所以上面的例子也可以改为
procedure TForm1.Button1Click(Sender: TObject);
var
 vpt: Pointer;
 vMethod: TMethod;
begin
 vMethod.Code := TForm.MethodAddress(‘MyClick‘);
 vMethod.Data := Self;
 if vMethod.Code = nil then ShowMessage(‘Error!‘);
 vPt := Pointer(Pointer(ListBox2)^);  //取得ClassType的指针 
 Integer(vPt) := Integer(vPt) + vmtTypeInfo; 
 SetMethodProp(ListBox2, GetPropInfo(pTypeInfo(vPt^), ‘OnClick‘), vMethod);
end;
{仿照上面的例子,我们可以访问所有VMT入口地址,并取得相应的信息}

2003-6-21 11:24:32    使用汇编实现远程函数调用 
{ No. 19 }

//如何通过指针,调用类函数中定义的函数(如下面的:MyFar)?
//如果我们只传递函数指针,然后调用函数的话,我们会发现,在MyFar中不能访问当前类对象的变量FMyText。如果嵌入一段汇编,将当前位置压入栈,然后再调用此函数,则就可以象类函数一样,在其中访问类的变量了。
e.g.
type 
 pMyRec = ^TMyRec;
 TMyRec = record
   rStr: String;
   rInt: Integer;
 end;

procedure MyProc(APro: Pointer);
var
 CallerBp: Cardinal;
 MyRec: TMyRec;
 vPt: Pointer;
begin
 MyRec.rStr := ‘MyRec.rStr‘;
 MyRec.rInt := ‘MyRec.rInt‘;
 vPt := Pointer(@MyRec);
 asm
   mov eax, [ebp]
   mov CallerBp, eax
   mov eax, vpt
   Push CallerBp
 end;
end;

procedure TMyTemp.SetText(aText: String);
 procedure MyFar(aRec: TMyRec); 
 begin
    ShowMessage(Format(‘%s_%sd_%s‘, [Self.FmyText, aRec.rStr, aRec.rInt]);
 end;
begin
 Self.FMyText := ‘JoyYuan‘;
 MyProc(Addr(MyFar));
end;

{说明:具体例子可以参考:Grids单元中,TSparsePointerArray.ForAll的实现。
}

2003-6-21 11:47:21    类函数地址 
{ No. 20 }

测试结果列举:也是测试方法和测试思维经历的过程。

结果一:Button1.CanFocus与Button2.CanFocus的地址相同
结果二:Button1.CanFocus与Form1.CanFocus的地址相同,也等同于ListBox1.CanFocus
结果三:当在TForm1窗体类中,重载Form1.CanFocus后,Button1.CanFocus与Form1.CanFocus的地址不相同
 结论:如果没有重载父类的虚函数,则访问时,直接得到并访问父类的函数。所以,TButton, TListBox, TForm默认都访问的是TWinControl的CanFocus。所以函数地址相同。

结果四:定义事件类型
Type
 TMyEvent1 = Function(): Boolean of Object;
 TMyEvent2 = function(): Boolean;
 得结果:Sizeof(TMyEvent1) = 8;  Sizeof(TMyEven2) = 4;
 结论:类函数类型,保存的室两个指针的内容,见TMethod中,Code 和 Data;既一个函数指针,一个对象指针。

验证测试例子一:
var
 vTestEvent: TNotifyEvent;
begin
 Pointer((@@vTestEvent)^) := @TForm1.MyClick; //或者通过:TForm1.MethodAddress(‘MyClick‘) 方式取函数地址
 Pointer(Pointer(Integer(@@vTestEvent) + 4)^) := Pointer(Self);
 vTestEvent(nil); //效果和执行 Self.MyClick一样。
end;
验证测试例子二:
var
 vMethod: TMethod;
begin
 vMethod.Code := TForm1.MethodAddress(‘MyClick‘);
 vMethod.Data := Self;
 TNotifyEvent(vMethod)(nil);//效果和执行 Self.MyClick一样。
end;

文章作者:大富翁的joyyuan97 
 
出处:http://www.delphibbs.com/keylife/iblog_show.asp?xid=1134

http://blog.csdn.net/diligentcatrich/article/details/8112808

时间: 2024-10-10 16:14:25

delphi程序设计之底层原理的相关文章

Atitit.变量的定义 获取 储存 物理结构 基本类型简化 隐式转换 类型推导 与底层原理 attilaxDSL

Atitit.变量的定义 获取 储存 物理结构 基本类型简化 隐式转换 类型推导 与底层原理 attilaxDSL 1.1. $ 美元字符, php 黑头1 1.2. 默认变量的范围和声明:1 1.3. 变量的存储,储存在变量池里Map(varName,varVal)1 1.3.1. 复合变量1 1.4. 变量类型简化: 字符串 数字,  bool1 1.5. 变量自动隐式转换2 1.6. 类型推导2 2. 参考 复合变量2 1.1. $ 美元字符, php 黑头 1.2. 默认变量的范围和声明

Spring IoC底层原理

-------------------siwuxie095 Spring IoC 底层原理 1.IoC 即 Inversion of Control,控制反转,把对象的创建 交给 Spring 进行管理 2.IoC 容器管理 Bean 的方式: (1)基于配置文件的方式 (2)基于注解的方式 3.IoC 底层原理所使用的技术: (1)XML 配置文件 (2)dom4j 解析 XML (3)工厂模式 (5)反射 4.原始方案,耦合度太高 public class UserService{ publ

【T-SQL进阶】02.理解SQL查询的底层原理

本系列[T-SQL]主要是针对T-SQL的总结. T-SQL基础 [T-SQL基础]01.单表查询-几道sql查询题 [T-SQL基础]02.联接查询 [T-SQL基础]03.子查询 [T-SQL基础]04.表表达式-上篇 [T-SQL基础]04.表表达式-下篇 [T-SQL基础]05.集合运算 [T-SQL基础]06.透视.逆透视.分组集 [T-SQL基础]07.数据修改 [T-SQL基础]08.事务和并发 [T-SQL基础]09.可编程对象 T-SQL进阶 [T-SQL进阶]01.好用的SQ

MVC底层原理

窥探ASP.Net MVC底层原理 实现跨越Session的分布式TempData 1.问题的引出 我相信大家在项目中都使用过TempData,TempData是一个字典集合,一般用于两个请求之间临时缓存数据或者页面之间传递消息.也都知道TempData是用Session来实现的,既然是用Session来实现的,那么模式就是线程模式,这样的Session是没法用到分布式系统中的,那么在多台机器上部署,怎么做到Session在多台机器中共存,这就涉及到分布式存储.那该如何实现TempData的分布

Lucene底层原理和优化经验分享(1)-Lucene简介和索引原理

基于Lucene检索引擎我们开发了自己的全文检索系统,承担起后台PB级.万亿条数据记录的检索工作,这里向大家分享下Lucene底层原理研究和一些优化经验. 从两个方面介绍: 1. Lucene简介和索引原理 2. Lucene优化经验总结 1. Lucene简介和索引原理 该部分从三方面展开:Lucene简介.索引原理.Lucene索引实现. 1.1 Lucene简介 Lucene最初由鼎鼎大名Doug Cutting开发,2000年开源,现在也是开源全文检索方案的不二选择,它的特点概述起来就是

ASP.NET底层原理

上图基本上演示了IIS 6整个处理过程.在User Mode下,http.sys接收到一个基于aspx的http request,然后它会根据IIS中的Metabase查看该基于该Request的Application属于哪个Application Pool,如果该Application Pool不存在,则创建之.否则直接将request发到对应Application Pool的Queue中.我上面已经说了,每个Application Pool对应着一个Worker Process:w3wp.e

NGUI所见即所得之深入剖析UIPanel,UIWidget,UIDrawCall底层原理

NGUI所见即所得之深入剖析UIPanel,UIWidget,UIDrawCall底层原理 By D.S.Qiu 尊重他人的劳动,支持原创,转载请注明出处:http.dsqiu.iteye.com 之前项目中用的NGUI的版本是3.0.7 f3,开始的时候感觉没有什么问题,直达最近项目UI的完成度比较高时,就突然出现掉帧很严重的现象,即使只有一个UI打开(其他都是active = false的情况下),打开profier,发现UIPanel LateUpdate 竟然占了CPU使用率的50%左右

HashMap的底层原理

简单说: 底层原理就是采用数组加链表: 两张图片很清晰地表明存储结构: 既然是线性数组,为什么能随机存取?这里HashMap用了一个小算法,大致是这样实现: // 存储时: int hash = key.hashCode(); // 这个hashCode方法这里不详述,只要理解每个key的hash是一个固定的int值 int index = hash % Entry[].length; Entry[index] = value; // 取值时: int hash = key.hashCode()

Spring AOP底层原理

------------------siwuxie095 Spring AOP 底层原理 AOP 即 Aspect Oriented Programming,面向切面编程, 即 不通过修改源代码的方式扩展功能 「在不修改源代码的情况下,对程序进行增强」 2.AOP 采取横向抽取机制,取代了传统纵向继承体系重复性 代码 3.AOP 底层原理所使用的技术 (1)JDK 的动态代理:针对实现了接口的类产生代理 即 有接口,使用动态代理创建接口实现类的代理对象 (2)CGLIB 的动态代理:针对没有实现