技术交流,DH讲解. 在前面2篇文章中,我们发现在TObject.InitInstance都没有IntfTable,所以有些地方的代码都没有执行. 所以下面我们把代码改一下,看看新的效果,然后把vmt系列的都来试一下:
IHuangJacky = interface
['{B7D099CE-BAD5-4589-86EA-71AE78B37483}']
procedure SayMyName;
end;
THuangJacky = class(TInterfacedObject,IHuangJacky)
private
FName:string;
procedure SetName(const Value: string);
public
procedure SayMyName;
constructor Create;
property Name:string read FName write SetName;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
H:THuangJacky;
begin
H:=THuangJacky.Create();
ShowMessage(H.Name);
H.SayMyName;
H.Free;
end;
一样走进入,Create里面,一样的我都不说了. 列出来不一样的地方: InstanceSize返回是$18 = 24.上一次没有实现接口,只有12,这一次多一半. 那么这一半的内存都用来干什么了呢? 我们在进入InitInstance之前先看几个相关的结构体:
PInterfaceEntry = ^TInterfaceEntry;
TInterfaceEntry = packed record
IID: TGUID;
VTable: Pointer;
IOffset: Integer;
ImplGetter: Integer;
end;
PInterfaceTable = ^TInterfaceTable;
TInterfaceTable = packed record
EntryCount: Integer;
Entries: array[0..9999] of TInterfaceEntry;
end;
TGUID = packed record
D1: LongWord;//4
D2: Word;//2
D3: Word;//2
D4: array[0..7] of Byte;//8
end;
然后InitInstance后半部分代码,因为前面都是一样的.
00404DCE 89D0 mov eax,edx
00404DD0 89E2 mov edx,esp
00404DD2 8B4BAC mov ecx,[ebx-$54] //vmtIntfTable
00404DD5 85C9 test ecx,ecx
00404DD7 7401 jz $00404dda
00404DD9 51 push ecx //压入,说明有IntfTable.
00404DDA 8B5BD0 mov ebx,[ebx-$30] //vmtParent
00404DDD 85DB test ebx,ebx
00404DDF 7404 jz $00404de5
00404DE1 8B1B mov ebx,[ebx] //遍历父对象
00404DE3 EBED jmp $00404dd2
00404DE5 39D4 cmp esp,edx
00404DE7 741D jz $00404e06
00404DE9 5B pop ebx //pop,第一次是父类vmtIntfTable
00404DEA 8B0B mov ecx,[ebx] //EntryCount 有1个
00404DEC 83C304 add ebx,$04 //偏移+4,正好跳过第一个变量.
00404DEF 8B7310 mov esi,[ebx+$10] //跳到VTable处
00404DF2 85F6 test esi,esi //$00401c78
00404DF4 7406 jz $00404dfc
00404DF6 8B7B14 mov edi,[ebx+$14] //IOffset
00404DF9 893407 mov [edi+eax],esi //将eax + 8 处写上VTable,第二次偏移是$10
00404DFC 83C31C add ebx,$1c
00404DFF 49 dec ecx //
00404E00 75ED jnz $00404def //看EntryCount是否为0
00404E02 39D4 cmp esp,edx
00404E04 75E3 jnz $00404de9
00404E06 5F pop edi
00404E07 5E pop esi
00404E08 5B pop ebx
00404E09 C3 ret
00404E0A 8BC0 mov eax,eax
//首先自己类有vmtIntfTable,所以push一次ecx,$004b330c //然后判断父类,也有vmtIntfTable,所以再push一次ecx //$00401c84
我们发现2个类的IntfTable中EntryCount只有一个,那么我们看看VTable里面都是些什么内容: 第一个TInterfacedObject的VTable: 我们可以看到是3个函数地址,这3个函数都是什么呢? 我们看见里面记录了是TInterfacedObject里面实现了IInterface接口里面的3个函数. 第二个THuangJacky的VTable: 这里有了4个函数指针.具体看看都是什么函数: 这4个函数就是IHuangJacky接口要实现的函数. 根据李维大哥的书里面:要在VTable里面来,接口必须要有GUID,额.是么?我们去掉IHuangJacky的GUID看一下: 没有变化,看来和GUID没有关系呀.难道维哥这话还有编译器版本有关. 最后看一下实例所分内存的情况: 0-3:类 4-7:空 8-B:父类VTable C-F:FName 10-13:自己的VTable 14-17:空
我们来看看System.pas中定义的几个常量的意义:
{ Virtual method table entries }
vmtSelfPtr = -88;
vmtIntfTable = -84;
vmtAutoTable = -80;
vmtInitTable = -76;
vmtTypeInfo = -72;
vmtFieldTable = -68;
vmtMethodTable = -64;
vmtDynamicTable = -60;
vmtClassName = -56;
vmtInstanceSize = -52;
vmtParent = -48;
vmtEquals = -44 deprecated 'Use VMTOFFSET in asm code';
vmtGetHashCode = -40 deprecated 'Use VMTOFFSET in asm code';
vmtToString = -36 deprecated 'Use VMTOFFSET in asm code';
vmtSafeCallException = -32 deprecated 'Use VMTOFFSET in asm code';
vmtAfterConstruction = -28 deprecated 'Use VMTOFFSET in asm code';
vmtBeforeDestruction = -24 deprecated 'Use VMTOFFSET in asm code';
vmtDispatch = -20 deprecated 'Use VMTOFFSET in asm code';
vmtDefaultHandler = -16 deprecated 'Use VMTOFFSET in asm code';
vmtNewInstance = -12 deprecated 'Use VMTOFFSET in asm code';
vmtFreeInstance = -8 deprecated 'Use VMTOFFSET in asm code';
vmtDestroy = -4 deprecated 'Use VMTOFFSET in asm code';
vmtQueryInterface = 0 deprecated 'Use VMTOFFSET in asm code';
vmtAddRef = 4 deprecated 'Use VMTOFFSET in asm code';
vmtRelease = 8 deprecated 'Use VMTOFFSET in asm code';
vmtCreateObject = 12 deprecated 'Use VMTOFFSET in asm code'; 搞个代码来测试下:
procedure TForm1.Button1Click(Sender: TObject);
var
H:THuangJacky;
I,J:Integer;
begin
H:=THuangJacky.Create();
I:=PInteger(H)^; //获得THuangJacky的地址.
J:=I - 88;//vmt系列的开头
ShowMessageFmt('I:%8x,J:%8x',[I,J]);
H.Free;
end;
第一个: 这个就是THuangJacky的地址
改- 88 为 - 84,后看结果: 可以看出这个是一个TInterfaceTable,1是EntryCount,中间16个0是InterfaceEntry的GUID,$004b336c应该是VTable.
改- 84 为 - 80: 0,代表没有不过从名字看vmtAutoTable应该是Automation会用到了,暂时没有测试.
改- 80 为 - 76: 这个也是类THuangJacky的地址
改 –76 为 - 72: 结合名字来看这个应该是是一个PTypeInfo指针.
像上面那样一个一个试太累了,我们把代码改一下:
procedure TForm1.Button1Click(Sender: TObject);
var
H:THuangJacky;
I,J:Integer;
K,L: Integer;
F:TextFile;
buf:array[0..23] of Byte;
begin
H:=THuangJacky.Create();
AssignFile(F,'C:\11.txt');
try
I:=PInteger(H)^; //获得THuangJacky的地址.
if not FileExists('C:\11.txt') then
Rewrite(F)
else
Append(F);
for K := 0 to 25 do
begin
L:=4*k - 88;
J:=PInteger(I + L)^;
Writeln(F,Format('偏移:%d,值:%8x',[L,J]));
if J<>0 then
begin
//Copy24个字节来看看
CopyMemory(@buf[0],Pointer(J),24);
Writeln(F,Format('00-07 %8x %8x',
[PInteger(@buf[0])^,PInteger(@buf[4])^]));
Writeln(F,Format('08-0F %8x %8x',
[PInteger(@buf[8])^,PInteger(@buf[12])^]));
Writeln(F,Format('10-17 %8x %8x',
[PInteger(@buf[16])^,PInteger(@buf[20])^]));
end;
Writeln(F,'---------------------------------------------------------');
end;
Writeln(F,Format('对象指针:%8x,类指针:%8x,父类指针:%8x',
[Integer(H),I,Integer(TInterfacedObject)]));
Writeln(F,Format('对象VTable:%8x,父类VTable:%8',
[PInteger(Integer(H)+16)^,PInteger(Integer(H)+8)^]));
finally
CloseFile(F);
H.Free;
end;
end;
|
请发表评论