zoukankan      html  css  js  c++  java
  • 关于使用类成员函数作为回调的方法

    为什么类成员函数不能直接做为回调函数?

    因为windows中,回调函数都是显式使用CALLBACk修饰符修饰,也就是_stdcall参数传递方式。_stdcall修饰的函数,参数从右至左依次压入堆栈,被调用者负责平衡堆栈。

    而所有类的成员函数在定义的时候都被隐式(implicit)定义为__thiscall参数传递方式。__thiscall 修饰的函数参数从右至左依次压入堆栈,被调用者负责平衡堆栈。与所有参数传递方式均不相同的一点:成员函数所在类的this指针被存入ecx寄存器(这个特性只针对Intel x86架构)。

    如何让类成员函数成为回调函数

    根据第一节对回调函数与类成员函数各自特点的分析。不难发现,只要能想办法在类成员函数被调用之前设置好ecx寄存器,就能在__stdcall调用的基础上模拟出一个完好的__thiscall调用。 

    想一下,如果我们对象的方法也是一个stdcall调用约定的方法,那么和回调函数还差什么呢?
    只差一个参数,第一个参数对象实例的指针,在Delphi,Pascal,Ada中叫Self,C++,java,C#中叫this.VB中叫ME.
    那么我们只要塞给它这个对象的地址不就行了吗.好在stdcall约定参数是由右向左传递的,也就是说第一个参数是最后传递的,又由于stdccall约定
    参数全部是由栈传递的.所以我们只要把对象指针直接压入栈中就行了.
    但别忽略了一点,
    call指令相当于
         Push 返回地址
         Jmp  函数
    ret指令相当于
         pop  返回地址
         Jmp  返回地址
    也就是说实际上在调用函数的时候栈顶保留的是返回地址,如果我们直接压入实例指针的话原来,当跳到函数体中,函数会把返回地址当Self,而Self则
    会被当成返回地址,具体会有什么样的后果大家自己去想像一下
    所以我们做的事情就是弹出返回地址,压入实例地址,压入返回地址,跳到对象方法去执行.
    实际上我们就是要构造这样一段代码当回调用,这段代码插入对象实例参数到第一个参数,然后跳到对象方法:
         pop    eax            //弹出返回地址到eax
         push   对象实例       //压入对象实例
         push   eax            //压入返回地址
         jmp    对应的对象方法 //跳转到相应的对象方法

    具体实现如下 

    复制代码
    function Cunk(Obj: TObject; CallBackProc: Pointer): Pointer;
    const
      PageSize = 4096;
      SizeOfJmpCode = 5;
    type
      TCode = packed record
        Int3: Byte;
        PopEAX: Byte;
        Push: Byte;
        AddrOfSelf: TObject;
        PushEAX: Byte;
        Jmp: Byte;
        AddrOfJmp: Cardinal;
      end;
    var
      LCode: ^TCode;
    begin
      Result := VirtualAlloc(nil, PageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
      LCode := Result;
      LCode^.Int3 := $90; // nop
      // LCode^.Int3 := $CC; //Int 3
      LCode^.PopEAX := $58;
      LCode^.Push := $68;
      LCode^.AddrOfSelf := Obj;
      LCode^.PushEAX := $50;
      LCode^.Jmp := $E9;
      LCode^.AddrOfJmp := DWORD(CallBackProc) - (DWORD(@LCode^.Jmp) + SizeOfJmpCode); // 计算相对地址
    end;

    procedure Runk(Thunk: Pointer);
    begin
      VirtualFree(Thunk, 0, MEM_RELEASE);

    end;

     
    http://www.cnblogs.com/toosuo/archive/2012/01/07/2315574.html
  • 相关阅读:
    Atitit.Java exe bat  作为windows系统服务程序运行
    Atitit. Object-c语言 的新的特性  attilax总结
    Atitit. Object-c语言 的新的特性  attilax总结
    Atitit。Time base gc 垃圾 资源 收集的原理与设计
    Atitit。Time base gc 垃圾 资源 收集的原理与设计
    Atitit.go语言golang语言的新的特性  attilax总结
    Atitit.go语言golang语言的新的特性  attilax总结
    Atitit.pdf 预览 转换html attilax总结
    Atitit.pdf 预览 转换html attilax总结
    Atitit.office word  excel  ppt pdf 的web在线预览方案与html转换方案 attilax 总结
  • 原文地址:https://www.cnblogs.com/findumars/p/5360125.html
Copyright © 2011-2022 走看看