Delphi的Anymouse方法探秘

这段时间换工作.加之身体不太好.总是感冒和喉咙发炎.就整天躺在家里等待面试电话.刚好有了点时间能看看Delphi的一些新东西

自从Delphi2009以后增加了一种匿名方法.

通过反汇编跟踪发现是编译器利用插入接口,类,对象来实现的.

Delphi2010刚好有RTTI的增强.我们就可以还原这个接口和类.至于RTTI的用法可以参看我前面的文章.

 

说干就干,挽袖子操刀

多余的话不多说.

procedure Test(Strs : TStrings);
type
  TProc = reference to function () : TObject;
var
  p : TProc;
  R : TRttiContext;
  RT : TRttiType;
  Fs : TArray<TRttiField>;
  MS : TArray<TRttiMethod>;
  I : Integer;
  Obj : TObject;
  Interfaces : string;
begin
  Strs.Clear;
  p := function () : TObject
        begin
          //
          asm
            mov Result, eax //如果这个Anymouse方法被编译成成员方法的话,因为Delphi默认的Register调用约定,EAX中方的肯定是Self.
          end;
          //其实这句汇编代码不加应该也可以.因为固然Self在EAX中,Result也是EAX.所以写成空函数也没问题.这里这样写是为了更好读
        end;

  Obj := p();
  R := TRttiContext.Create;
  RT := R.GetType(Obj.ClassType);
  FS := RT.GetFields();
  MS := RT.GetMethods();

  Strs.Add('======================================================');
  Strs.Add(Format('Obj[%0.8x],ClassName[%s],InstanceSize[%d],UnitName[%s]',[Integer(Obj), obj.ClassName, Obj.InstanceSize, obj.UnitName]));
  Strs.Add('编译器会生成一个临时类,绑定一个临时接口的.类名规则是:Anymouse方法所在函数名+$ActRec,所以这里就是Test$ActRec');
  Strs.Add('临时接口至少有一个方法是Anymouse方法.但是通过RTTI我们未必能获取这个方法.因为尽管接口中的方法一定是Public的');
  Strs.Add('但是临时类的实现中该方法完全可能是Private或者Protected的.这样RTTI是取不到这些方法的');
  Strs.Add('======================================================');
  Strs.Add('类的形式如下:');
  for I := 0 to Obj.GetInterfaceTable()^.EntryCount - 1 do
     if I = 0 then
       Interfaces := Format('[%s]',[GUIDToString(Obj.GetInterfaceTable()^.Entries[i].IID)])
     else
       Interfaces := Interfaces + ','+ Format('[%s]',[GUIDToString(Obj.GetInterfaceTable()^.Entries[i].IID)]);
  Strs.Add('Type');
  Strs.Add(format('Class %s = class(%s,%s) //如果GUID全零,说明绑定的该接口没有GUID',[obj.ClassName, obj.ClassParent.ClassName, Interfaces]));
  for I := 0 to Length(FS) - 1 do
    Strs.Add(Format('  %s : %s;//继承自%s',[FS[i].Name, FS[i].FieldType.Name ,Fs[i].Parent.Name]));
  for I := 0 to Length(MS) - 1 do
     Strs.Add(Format('  %s;//继承自%s',[MS[i].ToString(), MS[i].Parent.Name]));
  Strs.Add('end;');

  R.Free;

end;

 

procedure TForm2.btn1Click(Sender: TObject);
begin
  Test(memo1.Lines);
end;

 

那么点击Form上的按钮以后Memo1的内容就是:

======================================================
Obj[00AEA330],ClassName[Test$ActRec],InstanceSize[20],UnitName[Unit2]
编译器会生成一个临时类,绑定一个临时接口的.类名规则是:Anymouse方法所在函数名+$ActRec,所以这里就是Test$ActRec
临时接口至少有一个方法是Anymouse方法.但是通过RTTI我们未必能获取这个方法.因为尽管接口中的方法一定是Public的
但是临时类的实现中该方法完全可能是Private或者Protected的.这样RTTI是取不到这些方法的
======================================================
类的形式如下:

Type
Class Test$ActRec = class(TInterfacedObject,[{00000000-0000-0000-0000-000000000000}]) //如果GUID全零,说明绑定的该接口没有GUID
  FRefCount : Integer;//继承自TInterfacedObject
  procedure AfterConstruction;//继承自TInterfacedObject
  procedure BeforeDestruction;//继承自TInterfacedObject
  class function NewInstance: TObject;//继承自TInterfacedObject
  constructor Create;//继承自TObject
  procedure Free;//继承自TObject
  class function InitInstance(Instance: Pointer): TObject;//继承自TObject
  procedure CleanupInstance;//继承自TObject
  function ClassType: TClass;//继承自TObject
  class function ClassName: string;//继承自TObject
  class function ClassNameIs(const Name: string): Boolean;//继承自TObject
  class function ClassParent: TClass;//继承自TObject
  class function ClassInfo: Pointer;//继承自TObject
  class function InstanceSize: Integer;//继承自TObject
  class function InheritsFrom(AClass: TClass): Boolean;//继承自TObject
  class function MethodAddress(const Name: ShortString): Pointer;//继承自TObject
  class function MethodAddress(const Name: string): Pointer;//继承自TObject
  class function MethodName(Address: Pointer): string;//继承自TObject
  function FieldAddress(const Name: ShortString): Pointer;//继承自TObject
  function FieldAddress(const Name: string): Pointer;//继承自TObject
  function GetInterface(const IID: TGUID; out Obj): Boolean;//继承自TObject
  class function GetInterfaceEntry(const IID: TGUID): PInterfaceEntry;//继承自TObject
  class function GetInterfaceTable: PInterfaceTable;//继承自TObject
  class function UnitName: string;//继承自TObject
  function Equals(Obj: TObject): Boolean;//继承自TObject
  function GetHashCode: Integer;//继承自TObject
  function ToString: string;//继承自TObject
  function SafeCallException(ExceptObject: TObject; ExceptAddr: Pointer): HRESULT;//继承自TObject
  procedure AfterConstruction;//继承自TObject
  procedure BeforeDestruction;//继承自TObject
  procedure Dispatch(var Message);//继承自TObject
  procedure DefaultHandler(var Message);//继承自TObject
  class function NewInstance: TObject;//继承自TObject
  procedure FreeInstance;//继承自TObject
  class destructor Destroy;//继承自TObject
end;

我们再看如果有多个Anymouse方法的话是怎样处理的.

procedure Test(Strs : TStrings);
type
  TProc = reference to function () : TObject;
var
  p : TProc;
  R : TRttiContext;
  RT : TRttiType;
  Fs : TArray<TRttiField>;
  MS : TArray<TRttiMethod>;
  I : Integer;
  Obj : TObject;
  Interfaces : string;
begin
  Strs.Clear;
  p := function () : TObject
        begin
          //
          asm
            mov Result, eax //如果这个Anymouse方法被编译成成员方法的话,因为Delphi默认的Register调用约定,EAX中方的肯定是Self.
          end;
          //其实这句汇编代码不加应该也可以.因为固然Self在EAX中,Result也是EAX.所以写成空函数也没问题.这里这样写是为了更好读
        end;

  Obj := p();
  R := TRttiContext.Create;
  RT := R.GetType(Obj.ClassType);
  FS := RT.GetFields();
  MS := RT.GetMethods();

  Strs.Add('======================================================');
  Strs.Add(Format('Obj[%0.8x],ClassName[%s],InstanceSize[%d],UnitName[%s]',[Integer(Obj), obj.ClassName, Obj.InstanceSize, obj.UnitName]));
  Strs.Add('编译器会生成一个临时类,绑定一个临时接口的.类名规则是:Anymouse方法所在函数名+$ActRec,所以这里就是Test$ActRec');
  Strs.Add('临时接口至少有一个方法是Anymouse方法.但是通过RTTI我们未必能获取这个方法.因为尽管接口中的方法一定是Public的');
  Strs.Add('但是临时类的实现中该方法完全可能是Private或者Protected的.这样RTTI是取不到这些方法的');
  Strs.Add('======================================================');
  Strs.Add('类的形式如下:');
  for I := 0 to Obj.GetInterfaceTable()^.EntryCount - 1 do
     if I = 0 then
       Interfaces := Format('[%s]',[GUIDToString(Obj.GetInterfaceTable()^.Entries[i].IID)])
     else
       Interfaces := Interfaces + ','+ Format('[%s]',[GUIDToString(Obj.GetInterfaceTable()^.Entries[i].IID)]);
  Strs.Add('Type');
  Strs.Add(format('Class %s = class(%s,%s) //如果GUID全零,说明绑定的该接口没有GUID',[obj.ClassName, obj.ClassParent.ClassName, Interfaces]));
  for I := 0 to Length(FS) - 1 do
    Strs.Add(Format('  %s : %s;//继承自%s',[FS[i].Name, FS[i].FieldType.Name ,Fs[i].Parent.Name]));
  for I := 0 to Length(MS) - 1 do
     Strs.Add(Format('  %s;//继承自%s',[MS[i].ToString(), MS[i].Parent.Name]));
  Strs.Add('end;');

  R.Free;

  //===========================================================================
  p := function () : TObject
        begin

          asm
            mov Result, eax
          end;
          GetTickcount(); //稍加变化
        end;

  Obj := p();
  R := TRttiContext.Create;
  RT := R.GetType(Obj.ClassType);
  FS := RT.GetFields();
  MS := RT.GetMethods();

  Strs.Add('======================================================');
  Strs.Add(Format('Obj[%0.8x],ClassName[%s],InstanceSize[%d],UnitName[%s]',[Integer(Obj), obj.ClassName, Obj.InstanceSize, obj.UnitName]));
  Strs.Add('编译器会生成一个临时类,绑定一个临时接口的.类名规则是:Anymouse方法所在函数名+$ActRec,所以这里就是Test$ActRec');
  Strs.Add('临时接口至少有一个方法是Anymouse方法.但是通过RTTI我们未必能获取这个方法.因为尽管接口中的方法一定是Public的');
  Strs.Add('但是临时类的实现中该方法完全可能是Private或者Protected的.这样RTTI是取不到这些方法的');
  Strs.Add('======================================================');
  Strs.Add('类的形式如下:');
  for I := 0 to Obj.GetInterfaceTable()^.EntryCount - 1 do
     if I = 0 then
       Interfaces := Format('[%s]',[GUIDToString(Obj.GetInterfaceTable()^.Entries[i].IID)])
     else
       Interfaces := Interfaces + ','+ Format('[%s]',[GUIDToString(Obj.GetInterfaceTable()^.Entries[i].IID)]);
  Strs.Add('Type');
  Strs.Add(format('Class %s = class(%s,%s) //如果GUID全零,说明绑定的该接口没有GUID',[obj.ClassName, obj.ClassParent.ClassName, Interfaces]));
  for I := 0 to Length(FS) - 1 do
    Strs.Add(Format('  %s : %s;//继承自%s',[FS[i].Name, FS[i].FieldType.Name ,Fs[i].Parent.Name]));
  for I := 0 to Length(MS) - 1 do
     Strs.Add(Format('  %s;//继承自%s',[MS[i].ToString(), MS[i].Parent.Name]));
  Strs.Add('end;');

  R.Free;
  Strs.Add('以上重复两次,发现对象地址都是一样的.说明临时类和临时对象都只创建一份.');
  Strs.Add('但是临时类绑定了两个接口.说明每个Anymouse方法都绑定到一个独立的Interface上.');

end;

把Anymouse方法和对象解析部分在复制一遍.

执行后memo1的内容就是:

======================================================
Obj[00AE2960],ClassName[Test$ActRec],InstanceSize[24],UnitName[Unit2]
编译器会生成一个临时类,绑定一个临时接口的.类名规则是:Anymouse方法所在函数名+$ActRec,所以这里就是Test$ActRec
临时接口至少有一个方法是Anymouse方法.但是通过RTTI我们未必能获取这个方法.因为尽管接口中的方法一定是Public的
但是临时类的实现中该方法完全可能是Private或者Protected的.这样RTTI是取不到这些方法的
======================================================
类的形式如下:
Type
Class Test$ActRec = class(TInterfacedObject,[{00000000-0000-0000-0000-000000000000}],[{00000000-0000-0000-0000-000000000000}]) //如果GUID全零,说明绑定的该接口没有GUID
  FRefCount : Integer;//继承自TInterfacedObject
  procedure AfterConstruction;//继承自TInterfacedObject
  procedure BeforeDestruction;//继承自TInterfacedObject
  class function NewInstance: TObject;//继承自TInterfacedObject
  constructor Create;//继承自TObject
  procedure Free;//继承自TObject
  class function InitInstance(Instance: Pointer): TObject;//继承自TObject
  procedure CleanupInstance;//继承自TObject
  function ClassType: TClass;//继承自TObject
  class function ClassName: string;//继承自TObject
  class function ClassNameIs(const Name: string): Boolean;//继承自TObject
  class function ClassParent: TClass;//继承自TObject
  class function ClassInfo: Pointer;//继承自TObject
  class function InstanceSize: Integer;//继承自TObject
  class function InheritsFrom(AClass: TClass): Boolean;//继承自TObject
  class function MethodAddress(const Name: ShortString): Pointer;//继承自TObject
  class function MethodAddress(const Name: string): Pointer;//继承自TObject
  class function MethodName(Address: Pointer): string;//继承自TObject
  function FieldAddress(const Name: ShortString): Pointer;//继承自TObject
  function FieldAddress(const Name: string): Pointer;//继承自TObject
  function GetInterface(const IID: TGUID; out Obj): Boolean;//继承自TObject
  class function GetInterfaceEntry(const IID: TGUID): PInterfaceEntry;//继承自TObject
  class function GetInterfaceTable: PInterfaceTable;//继承自TObject
  class function UnitName: string;//继承自TObject
  function Equals(Obj: TObject): Boolean;//继承自TObject
  function GetHashCode: Integer;//继承自TObject
  function ToString: string;//继承自TObject
  function SafeCallException(ExceptObject: TObject; ExceptAddr: Pointer): HRESULT;//继承自TObject
  procedure AfterConstruction;//继承自TObject
  procedure BeforeDestruction;//继承自TObject
  procedure Dispatch(var Message);//继承自TObject
  procedure DefaultHandler(var Message);//继承自TObject
  class function NewInstance: TObject;//继承自TObject
  procedure FreeInstance;//继承自TObject
  class destructor Destroy;//继承自TObject
end;
======================================================
Obj[00AE2960],ClassName[Test$ActRec],InstanceSize[24],UnitName[Unit2]
编译器会生成一个临时类,绑定一个临时接口的.类名规则是:Anymouse方法所在函数名+$ActRec,所以这里就是Test$ActRec
临时接口至少有一个方法是Anymouse方法.但是通过RTTI我们未必能获取这个方法.因为尽管接口中的方法一定是Public的
但是临时类的实现中该方法完全可能是Private或者Protected的.这样RTTI是取不到这些方法的
======================================================
类的形式如下:
Type
Class Test$ActRec = class(TInterfacedObject,[{00000000-0000-0000-0000-000000000000}],[{00000000-0000-0000-0000-000000000000}]) //如果GUID全零,说明绑定的该接口没有GUID
  FRefCount : Integer;//继承自TInterfacedObject
  procedure AfterConstruction;//继承自TInterfacedObject
  procedure BeforeDestruction;//继承自TInterfacedObject
  class function NewInstance: TObject;//继承自TInterfacedObject
  constructor Create;//继承自TObject
  procedure Free;//继承自TObject
  class function InitInstance(Instance: Pointer): TObject;//继承自TObject
  procedure CleanupInstance;//继承自TObject
  function ClassType: TClass;//继承自TObject
  class function ClassName: string;//继承自TObject
  class function ClassNameIs(const Name: string): Boolean;//继承自TObject
  class function ClassParent: TClass;//继承自TObject
  class function ClassInfo: Pointer;//继承自TObject
  class function InstanceSize: Integer;//继承自TObject
  class function InheritsFrom(AClass: TClass): Boolean;//继承自TObject
  class function MethodAddress(const Name: ShortString): Pointer;//继承自TObject
  class function MethodAddress(const Name: string): Pointer;//继承自TObject
  class function MethodName(Address: Pointer): string;//继承自TObject
  function FieldAddress(const Name: ShortString): Pointer;//继承自TObject
  function FieldAddress(const Name: string): Pointer;//继承自TObject
  function GetInterface(const IID: TGUID; out Obj): Boolean;//继承自TObject
  class function GetInterfaceEntry(const IID: TGUID): PInterfaceEntry;//继承自TObject
  class function GetInterfaceTable: PInterfaceTable;//继承自TObject
  class function UnitName: string;//继承自TObject
  function Equals(Obj: TObject): Boolean;//继承自TObject
  function GetHashCode: Integer;//继承自TObject
  function ToString: string;//继承自TObject
  function SafeCallException(ExceptObject: TObject; ExceptAddr: Pointer): HRESULT;//继承自TObject
  procedure AfterConstruction;//继承自TObject
  procedure BeforeDestruction;//继承自TObject
  procedure Dispatch(var Message);//继承自TObject
  procedure DefaultHandler(var Message);//继承自TObject
  class function NewInstance: TObject;//继承自TObject
  procedure FreeInstance;//继承自TObject
  class destructor Destroy;//继承自TObject
end;
以上重复两次,发现对象地址都是一样的.说明临时类和临时对象都只创建一份.
但是临时类绑定了两个接口.说明每个Anymouse方法都绑定到一个独立的Interface上.

 

 

别的也不多解释了.自己看吧

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值