附件资料
*类的内存信息(代码)
示例:获取类信息
说明:比对通过类地址以及通过类方法获取信息的效果。
代码:
type
TMyObject = class(TObject)
private
FData: Integer;
protected
procedure Test1;virtual;
procedure Test2;dynamic;
public
procedure Test;
published
procedure Test3;
end;
procedure TMyObject.Test;
var
i:Integer;
begin
ShowMessage('静态方法');
end;
procedure TMyObject.Test1;
begin
ShowMessage('虚方法');
end;
procedure TMyObject.Test2;
begin
ShowMessage('动态方法');
end;
procedure TMyObject.Test3;
begin
ShowMessage('published方法');
end;
procedure TForm1.Button1Click(Sender: TObject);
//---
{procedure _ShowClassInfo(ALines: TStrings; AClass: TClass);
begin
with ALines do
begin
Add(Format('类指针地址: %d ', [Integer(AClass)]));
//---
if AClass.ClassParent = nil then
Add('父类名: Nil')
else
Add(Format('父类名: %s', [AClass.ClassParent.ClassName]));
//---
Add(Format('对象大小: %d', [AClass.InstanceSize]));
Add(Format('类名: %s', [AClass.ClassName]));
end;
end;}
//---
procedure _ShowClassInfo(ALines: TStrings; AClass: TClass);
var
AClassAddress: Integer;
AParentClass: TClass;
AParentClassAddress: PPointer;
begin
AClassAddress := Integer(AClass);
with ALines do
begin
Add(Format('类指针地址: %d ', [AClassAddress]));
//---
AParentClassAddress := PPointer(PPointer(AClassAddress + vmtParent)^); //--PPointer(AClassAddress + vmtParent)^ = @TParentClass
if AParentClassAddress = nil then
Add(Format('偏移量: %d 父类指针地址: %d 父类名: Nil', [vmtParent,PInteger(AClassAddress + vmtParent)^]))
else
begin
Pointer(AParentClass) := AParentClassAddress^;
Add(Format('偏移量: %d 父类指针地址: %d 父类名: %s', [vmtParent,PInteger(AClassAddress + vmtParent)^,AParentClass.ClassName]));
end;
//---
Add(Format('偏移量: %d 对象大小: %d', [vmtInstanceSize,PInteger(AClassAddress + vmtInstanceSize)^]));
Add(Format('偏移量: %d 类名: %s', [vmtClassName,PShortString(PPointer(AClassAddress + vmtClassName)^)^]));
Add(Format('偏移量: %d 动态方法表地址: %d', [vmtDynamicTable,PInteger(AClassAddress + vmtDynamicTable)^]));
Add(Format('偏移量: %d Published方法表地址: %d', [vmtMethodTable,PInteger(AClassAddress + vmtMethodTable)^]));
Add(Format('偏移量: %d 虚方法表地址: %d', [vmtSelfPtr,PInteger(AClassAddress + vmtSelfPtr)^]));
end;
end;
begin
_ShowClassInfo(Memo1.Lines,TObject);
Memo1.Lines.Add('----------');
_ShowClassInfo(Memo1.Lines,TMyObject);
end;
*类的静态方法(代码)
示例:访问类的静态方法
说明:类的静态方法调用与对象的实际类型无关,仅与类相关。
代码:
type
TMyObject = class(TObject)
private
FData: Integer;
public
procedure Test;
end;
procedure TMyObject.Test;
var
i:Integer;
begin
ShowMessage('123');
end;
procedure TForm1.Button1Click(Sender: TObject);
var
AObject: TObject;
begin
AObject := TObject.Create;
TMyObject(AObject).Test;
AObject.Free;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
AObject:TMyObject;
begin
AObject.Test;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
TMyObject(nil).Test;
end;
procedure TForm1.Button1Click(Sender: TObject);
type
TFakeEvent = procedure() of object;
var
AEvent:TFakeEvent;
begin
AEvent := TMyObject(nil).Test;
AEvent();
end;
procedure TForm1.Button1Click(Sender: TObject);
type
TFakeEvent = procedure() of object;
TFakeEvent1 = procedure(const ASelf: Pointer);
var
AEvent:TFakeEvent;
AEvent1:Pointer;
begin
AEvent := TMyObject(nil).Test;
AEvent1 := TMethod(AEvent).Code; //获取方法地址
TFakeEvent1(AEvent1)(nil);
end;
汇编:
procedure TForm1.Button1Click(Sender: TObject);
begin
asm
MOV EAX, 0;
CALL TMyObject.Test;
end;
end;
汇编:
*类的虚方法(代码)
示例:访问类的虚方法
说明:vmtSelfPtr(虚方法表的指针)实际上就是指向TObject位置,所以类的虚拟方法是依次排在TObject所指向的位置之后。虚拟方法表包括本身以及父类所有的虚拟方法的地址。
代码:
type
TMyObject = class(TObject)
protected
procedure Test;virtual;
procedure Test1;virtual;
end;
TMyObject1 = class(TMyObject)
protected
procedure Test;override;
end;
procedure TMyObject.Test;
begin
ShowMessage('TMyObject 虚方法');
end;
procedure TMyObject.Test1;
begin
ShowMessage('TMyObject 虚方法1');
end;
procedure TMyObject1.Test;
begin
inherited;
ShowMessage('TMyObject1 虚方法');
end;
procedure TForm1.Button1Click(Sender: TObject);
type
TFakeEvent1 = procedure(const ASelf: Pointer);
var
AClassAddress: Integer;
AEvent:Pointer;
begin
AClassAddress := Integer(TMyObject1);
//---
AEvent := PPointer(AClassAddress)^; //获取方法地址
TFakeEvent1(AEvent)(nil);
//---
AEvent := PPointer(AClassAddress + 4)^; //获取方法地址
TFakeEvent1(AEvent)(nil);
end;
示例:访问类的虚方法
说明:看一下正常的虚方法是如何调用的。
代码:
type
TMyObject = class(TObject)
protected
procedure Test;virtual;
procedure Test1;virtual;
end;
procedure TMyObject.Test;
begin
ShowMessage('TMyObject 虚方法');
end;
procedure TMyObject.Test1;
begin
ShowMessage('TMyObject 虚方法');
end;
procedure TForm1.Button1Click(Sender: TObject);
var
AObject: TMyObject;
begin
AObject := TMyObject.Create;
AObject.Test;
AObject.Test1;
AObject.Free;
end;
汇编:
procedure TForm1.Button1Click(Sender: TObject);
var
AObject: TMyObject;
begin
…………
AObject.Test;
mov eax,[ebp - $08] ; eax存储为Aobject对象指针
mov edx,[eax] ; edx 存储为Aobject对象空间首4字节内容,即TmyObject的VMT
call dword ptr [edx] ; [edx] 为VMT中TMyObject.Test方法地址
AObject.Test1;
mov eax,[ebp - $08] ; eax存储为Aobject对象指针
mov edx,[eax] ; edx 存储为Aobject对象空间首4字节内容,即TmyObject的VMT
call dword ptr [edx + $04] ; [edx + $04]为VMT中TMyObject.Test1方法地址
…………
end;
*类的动态方法(代码)
示例:访问类的动态方法
说明:vmtDynamicTable(动态方法表的指针)指向的是动态方法表。动态方法表则只保存自己本身所包含的动态方法表,如果调用者的动态方法不属于自己,则根据索引号往上级父类遍历查询得到方法的地址。
代码:
type
TMyObject = class(TObject)
protected
procedure Test; dynamic;
procedure Test1; dynamic;
end;
TMyObject1 = class(TMyObject)
protected
procedure Test1;override;
procedure Test2; dynamic;
end;
procedure TMyObject.Test;
begin
ShowMessage('TMyObject 动态方法');
end;
procedure TMyObject.Test1;
begin
ShowMessage('TMyObject 动态方法1');
end;
procedure TMyObject1.Test1;
begin
ShowMessage('TMyObject1 动态方法1');
end;
procedure TMyObject1.Test2;
begin
ShowMessage('TMyObject1 动态方法2');
end;
procedure TForm1.Button1Click(Sender: TObject);
type
TFakeEvent1 = procedure(const ASelf: Pointer);
//---
procedure _ShowDMTInfo(ALines: TStrings; AClass: TClass);
var
AClassAddress,ADMTAddress: Integer;
AMethodCount,APos,i: Integer;
AEvent: Pointer;
begin
AClassAddress := Integer(AClass);
with ALines do
begin
Add(Format('类名: %s ', [AClass.ClassName]));
//---
ADMTAddress := Integer(PPointer(AClassAddress + vmtDynamicTable)^);
if ADMTAddress = 0 then
Exit;
//---
APos := 0;
AMethodCount := PWord(ADMTAddress + APos)^;
Add(Format('偏移量: %d 动态方法数量: %d', [APos,AMethodCount]));
Inc(APos,2);
//---
for i := 0 to AMethodCount - 1 do
begin
Add(Format('偏移量: %d 索引:%d 索引内容:%d', [APos,i,PWord(ADMTAddress + APos)^]));
Inc(APos,2);
end;
//---
for i := 0 to AMethodCount - 1 do
begin
AEvent := PPointer(ADMTAddress + APos)^; //获取方法地址
TFakeEvent1(AEvent)(nil);
//---
Add(Format('偏移量: %d 索引:%d 方法地址:%d', [APos,i,PInteger(ADMTAddress + APos)^]));
Inc(APos,4);
end;
end;
end;
begin
_ShowDMTInfo(Memo1.Lines,TMyObject);
_ShowDMTInfo(Memo1.Lines,TMyObject1);
end;
示例:访问类的动态方法
说明:看一下正常的动态方法如何调用的。
代码:
type
TMyObject = class(TObject)
protected
procedure Test; dynamic;
procedure Test1; dynamic;
end;
procedure TMyObject.Test;
begin
ShowMessage('TMyObject 动态方法');
end;
procedure TMyObject.Test1;
begin
ShowMessage('TMyObject 动态方法1');
end;
procedure TForm1.Button1Click(Sender: TObject);
var
AObject: TMyObject;
begin
AObject := TMyObject.Create;
AObject.Test;
AObject.Test1;
AObject.Free;
end;
汇编:
procedure TForm1.Button1Click(Sender: TObject);
var
AObject: TMyObject;
begin
…………
AObject.Test;
mov eax,[ebp - $08] ; eax存储为Aobject对象指针
mov si,$FFFF ; $FFFF为TMyObject.Test方法索引号
call @CallDynaInst
AObject.Test1;
mov eax,[ebp - $08] ; eax存储为Aobject对象指针
mov si,$FFFE ; $FFFE为TMyObject.Test1方法索引号
call @CallDynaInst
…………
end;
*类的公开方法(代码)
示例:访问类的published方法
说明:vmtMethodTable(Published Method表)指向Published Method表有序排列,只存储当前类的Published Method表,得到父类的Published Method表需要往上遍历。
凡是声明在类的 Published 部分的方法都可以通过调用 TObject.MethodName 获得方法的名字。
代码:
type
TMyObject = class(TObject)
published
procedure Test;
procedure Test1; virtual;
procedure Test2; dynamic;
end;
procedure TMyObject.Test;
begin
ShowMessage('TMyObject 静态方法');
end;
procedure TMyObject.Test1;
begin
ShowMessage('TMyObject 动态方法');
end;
procedure TMyObject.Test2;
begin
ShowMessage('TMyObject 虚方法');
end;
procedure TForm1.Button1Click(Sender: TObject);
//---
procedure _ShowDMTInfo(ALines: TStrings; AClass: TClass);
var
AClassAddress,AMTAddress,AMethodAddress,AMethodNameLen: Integer;
AMethodCount,APos,i: Integer;
AEvent: Pointer;
AMethodName:ShortString;
begin
AClassAddress := Integer(AClass);
with ALines do
begin
Add(Format('类名: %s ', [AClass.ClassName]));
//---
AMTAddress := Integer(PPointer(AClassAddress + vmtMethodTable)^);
if AMTAddress = 0 then
Exit;
//---
APos := 0;
AMethodCount := PWord(AMTAddress + APos)^;
Add(Format('偏移量: %d 动态方法数量: %d', [APos,AMethodCount]));
Inc(APos,4);
//---
for i := 0 to AMethodCount - 1 do
begin
AMethodAddress := PInteger(AMTAddress + APos)^;
Inc(APos,4);
//---
AMethodNameLen := PByte(AMTAddress + APos)^;
Inc(APos,1);
//---
AMethodName := PShortString(AMTAddress + APos - 1)^;
Inc(APos,AMethodNameLen + 2);
//---
Add(Format('索引:%d 方法地址:%s 方法名长度: %d 方法名:%s', [i,IntToHex(AMethodAddress,2),AMethodNameLen,AMethodName ]));
end;
end;
end;
//---
procedure _ShowDMTInfo1(ALines: TStrings; AClass: TClass);
begin
with ALines do
begin
Add(Format('类名: %s ', [AClass.ClassName]));
//---
Add(Format('方法名:%s 方法地址:%s',['Test',IntToHex(Integer(AClass.MethodAddress('Test')),2)]));
Add(Format('方法名:%s 方法地址:%s',['Test1',IntToHex(Integer(AClass.MethodAddress('Test1')),2)]));
Add(Format('方法名:%s 方法地址:%s',['Test2',IntToHex(Integer(AClass.MethodAddress('Test2')),2)]));
end;
end;
begin
_ShowDMTInfo(Memo1.Lines,TMyObject);
_ShowDMTInfo1(Memo1.Lines,TMyObject);
end;
内存:
示例:访问类的published方法
说明:看一下正常的published方法如何调用的。
代码:
type
TMyObject = class(TObject)
published
procedure Test;
procedure Test1;virtual;
procedure Test2;dynamic;
end;
procedure TMyObject.Test;
begin
ShowMessage('TMyObject 静态方法');
end;
procedure TMyObject.Test1;
begin
ShowMessage('TMyObject 动态方法');
end;
procedure TMyObject.Test2;
begin
ShowMessage('TMyObject 虚方法');
end;
procedure TForm1.Button1Click(Sender: TObject);
var
AObject: TMyObject;
begin
AObject := TMyObject.Create;
AObject.Test;
AObject.Test1;
AObject.Test2;
AObject.Free;
end;
汇编: