zoukankan      html  css  js  c++  java
  • Delphi的Anymouse方法探秘

    匿名函数是用Interface来实现的,具体细节可以看
    http://www.raysoftware.cn/?p=38
    匿名函数还是非常方便的。
    比如自己封装的异步调用。
    Async(procedure(AParam : TValue)
          begin
          end,
           procedure (AParam : TValue; AResult: TValue)
           begin
           end );
    第一个匿名函数,第一个是在线程内执行的,第二个是当异步执行完的回调,在主线程执行。用起来分厂方便。
    不过Delphi的匿名函数我觉得不如C++的Lamda方便,也不如Java的接口回调方便。不过很多时候比之前做什么事情都要正儿八经封装一个class再写一个事件方便很多。

    http://bbs.2ccc.com/topic.asp?topicid=529818

    自从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上.

  • 相关阅读:
    Hanoi塔
    采药
    进制转换(大数)
    Load Balancing with NGINX 负载均衡算法
    upstream模块实现反向代理的功能
    epoll
    在nginx启动后,如果我们要操作nginx,要怎么做呢 别增加无谓的上下文切换 异步非阻塞的方式来处理请求 worker的个数为cpu的核数 红黑树
    粘性会话 session affinity sticky session requests from the same client to be passed to the same server in a group of servers
    负载均衡 4层协议 7层协议
    A Secure Cookie Protocol 安全cookie协议 配置服务器Cookie
  • 原文地址:https://www.cnblogs.com/findumars/p/6557105.html
Copyright © 2011-2022 走看看