Delphi的对象机制浅探 (100分)

  • 主题发起人 savetime
  • 开始时间
高手啊,高手,想法啊,想法,学习啊,学习
 
我认为 Borland 应该对 AComp := AComp.Create 调用提示为语法错误。我实在是想不到什么时候会需要这种形式的调用。
--------------------------------------------------------------------------------
看看TApplication的CreateForm过程,有一个典型的
AComp := AComp.Create 例子,有些时候生成的汇编是不知所云,
那是因为我们对汇编根本就不了解,汇编的语句单个拿出来都很好懂,
但是要一段拿出来要明白要实现什么功能就困难了。

procedure TApplication.CreateForm(InstanceClass: TComponentClass
var Reference);
var
Instance: TComponent;
begin
Instance := TComponent(InstanceClass.NewInstance)
//调用NewInstance方法分配内存,创建框架等等。然后根据TComponent类圈定框架赋给Instance
TComponent(Reference) := Instance;//给Reference变量赋值
try
Instance.Create(Self);//执行constructor Create(Owner) 的代码
except
TComponent(Reference) := nil;
raise;
end;
if (FMainForm = nil) and (Instance is TForm) then
begin
TForm(Instance).HandleNeeded
//调用HandleNeeded过程,创建窗体。
FMainForm := TForm(Instance);//把第一个创建的窗体作为主窗体。
end;
end;
 
to book523,
我又错了,你真的找出了这样的代码,厉害!我要考虑一下再回复你。
 
我现在正在分析 TWinControl 如何如何封装 Windows 的消息系统,二天过去,进展不大。相关的函数太多了,还有一些汇编夹在其中。我认为消息系统才是 VCL 的关键地方。整个程序的执行过程全在里面。
---------------------------------------
我现在也在做这个工作,昨天看了李维的那本新书《inside vcl》开始有点头绪了,
你不要从TWinControl开始,那样会不知所云,你可以从项目工程文件的那三句话开始
分析,估计很快就能理出个大概来。
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
 
我又错了,你真的找出了这样的代码,厉害!我要考虑一下再回复你。
---------------------------------------------------------------
正好昨天分析这个过程看到的。
 
到此一游,
 
to book523,

我找到错误的的原因了。我把汇编代码 jz 和 jl 搞混了,所以上面注释 System._ClassCreate 有错误。

其实 dl 寄存器有 3 种状态(我原来以为是 2 种):

dl = 1 是在 TClass.Create 之前被设置
dl = 0 是在 Inherited 之前被设置
dl = -1 是在 Object.Create 之前被设置

dl = 1 System._ClassCreate 被调用
dl = 0 System._ClassCreate 不被调用
dl = -1 System._ClassCreate 被调用, 但不执行 NewInstance 工作,只是设置异常断点


“⊙测试目标:以 object reference 和 class reference 调用构造函数的编译器实现” —— 的注释也有错误。

重读 Obj.Create 的汇编代码,终于明白了应该是有 Object Reference consturction 这一事实。只是在 Obj.Create 之前必须用 NewInstance 分配内存和设置 Obj VMT 的指针,否则 Obj.Create 就会失败。Obj.Create 不会调用 NewInstance,但会设置异常处理,保证出错时析构函数被调用。

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

对于 VCL 的消息系统,我原本也想从上向下读,不过我对 TWinControl 没什么了解,只好先看它都包装了哪些函数。我现在可以用你推荐的方法试试。
我猜想 TApplication 只是调用 GetMessage 再 DispatchMessage,所以关键的是 VCL 如何注册 WndProc,并把注册的这个 WndProc 关联到对象上。

MakeObjectInstance 很有意思,在内存中建立一块一块的 ObjectInstance 代码,ObjectInstance 的地址又被注册为标准的 Windows Procedure。

我是从 TWinControl.Create 入手的,第一句不太明白,
FObjectInstance := Classes.MakeObjectInstance(MainWndProc);
{ function MakeObjectInstance(Method: TWndMethod): Pointer }

你能解释一下 MakeObjectInstance 的 MainWndProc 传递的实际内容是什么(是不是传递 MainWndProc 的指针和 Self 指针?我是不太明白为什么编译器知道要把 Self 传过去。)

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

你已经拿到 Inside VCL 了,幸福啊。我们这里的书店太差了,到今天还没通知我到货。
 
你能解释一下 MakeObjectInstance 的 MainWndProc 传递的实际内容是什么(是不是传递 MainWndProc 的指针
和 Self 指针?我是不太明白为什么编译器知道要把 Self 传过去。)
============================================================================
传递的是MainWndProc 的指针和 Self 指针,看delphi的消息结构:
TMessage = packed record
Msg: Cardinal;
case Integer of
0: (
WParam: Longint;
LParam: Longint;
Result: Longint);
1: (
WParamLo: Word;
WParamHi: Word;
LParamLo: Word;
LParamHi: Word;
ResultLo: Word;
ResultHi: Word);
end;
而windows中的消息结构:
typedef struct tagMSG
{
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG;
显然少了一个最重要的field即窗口句柄hwnd。
因此在delphi中处理消息的方法都会把self作为隐含参数,
把self压入到EAX中,再把TMessage 结构的指针作为第二个参数,放入EDX中,
这样才符合windows的回调函数的格式,
实际上MainWndProc 正是delphi窗体类的回调函数。

 
你上面对Create过程的跟踪分析真是精辟啊,令人佩服啊。

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

你已经拿到 Inside VCL 了,幸福啊。我们这里的书店太差了,到今天还没通知我到货。

===============================
我是在dearbook上订了,12。31号订的,2。2号就拿到了。
 
to savetime:
看到了帖子吗:
http://www.delphibbs.com/delphibbs/dispq.asp?lid=584889
我从收藏夹中提上来的。
 
看到,正在研究。
 
留下记号,到此一游
 
TO:savetime 、积步 我也正在学习汇编。其实我也有savetime的想法。可是自己能力
和时间的问题。只能向你们学习了。关于:积步兄的问题确实有趣!而且 savetime兄的回答就精妙了! 现在我调试一下结果是下面的:
1.这是(savetime)做的:MOV ECX, TA(EBX).FA
// 通知编译器 EBX 指向的是 TA class
其实这句话可以这样翻译过来(这是我的个人看法,有步不的地方请指正):
asm
MOV EAX, [A];//这句是把这个实例的引用传进寄存器EAX,也就是堆中的首地址
//MOV EAX,TA(EAX).FA;
MOV tmpInt,EAX ;//这里是把寄存器中的值(也就是实例的首地址,而不是变量的地址)传给tempInt;
end;
//在这里也就是相当于(savetime)的那句话了
ShowMessage(IntToStr(TA(tmpInt).FA));
2.这是(积步)提问的:
procedure TForm1.Button1Click(Sender: TObject);
var
A: TA;
tmpInt: Integer;
begin
A := TA.Create;
tmpInt := 0;
A.SetA(100);
asm
MOV ECX, A
//為什麼A.FA => 100 立即數就可以顯示為 100, 如果不改就顯示為其它值。(这里修改一下)
MOV tmpInt, ECX;
end;
ShowMessage(IntToStr(TA(tmpInt).FA));//主意这里如果改为这样就对了,这里可能是编译器做的
A.Free;
end

其实(积步)的做法就是得到这个类的段地址的FA的偏移地址。而不是该地址
里面的内容,至于怎么样取出我们要找到该变量地址的值,就象(savetime)那样
做!但是那样那做为什么能够取出他的值?后来(积步)用寄存器加上立即数也可以
取出来,我还是模糊???



 
to kk2000:
你的问题很简单
MOV EAX, A
// A 是指向对象的指针,这句把对象在内存中的地址存入 EAX
这时 EAX + 4 就是对象的第一个成员变量
TA(EAX).FA 就是 EAX 加上 FA 的偏移处的内容
这是 Delphi 语法支持的,能大概看得懂就行了。

你可是解决了一个大问题,不用汇编也能访问私有成员,我原以为是汇编的特权呢。
procedure TForm1.Button1Click(Sender: TObject);
var
A: TA;
begin
A := TA.Create;
A.SetA(100);
ShowMessage(IntToStr(TA(A).FA));
A.Free;
end;
 
我错了,我忘记同一个单元的类可以互相访问私有成员,真是该死。
 
to savetime:
你做什么工作啊,好像挺有时间的啊。
有时间到www.01cn.net上看看,是个好地方啊。
 
to book523,
www.01cn.net 真是高手云集啊,收藏!
我在公司做杂务,主要是收发货、检查客户退回来的产品。我总是尽快把杂务处理掉,其他的时间就自己写点小程序,读读书。我也想去软件公司,可是没有人要我,学历太低,专业知识也不够。只好先混着。
 
看了看.感觉这才是真正的高手之路 .
可偶对汇编太没感觉了..我开始看看vcl吧 :)
 

Similar threads

A
回复
0
查看
980
Andreas Hausladen
A
I
回复
0
查看
726
import
I
I
回复
0
查看
681
import
I
I
回复
0
查看
494
import
I
顶部