• 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上.

  • 相关阅读:
    原始mvc1
    ssh3,hibernate和maven
    hibernate1
    mybatis和spring一起用
    python怎么生成requirements.txt文件
    selenium.common.exceptions.ElementNotVisibleException: Message: element not interactable (Session info: chrome=74.0.3729.108) (Driver info: chromedriver=2.43.600210
    Android SDK 安装与配置
    pip超时问题解决
    爬取网页数据
    pip安装报错: Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ProxyError('Cannot connect to proxy
  • 原文地址:https://www.cnblogs.com/findumars/p/6557105.html
Copyright © 2020-2023  润新知