zoukankan      html  css  js  c++  java
  • 利用Thunk让C++成员函数变回调函数

    Windows API经常需要回调函数,而在C++开发中面向对象当行其道,若能让C++类的成员函数成为回调函数,简直就是大善!但是C++成员函数都隐含了一个this指针用于指向当前的对象.要实现回调确实不容易.

    我大约一年前就接触到Thunk技术,甚至也看过利用Thunk实现将成员函数变成回调函数的例子.但是我实在没了解过C++汇编后的样子,很容易钻了牛角尖,看都看不懂,直接用他们的程序又不敢,毕竟出错后不好处理.前端时间偶尔想起Thunk技术,对未懂技术老这样悬着很可能影响自己的程序员生涯的,于是决心闭关参悟(没办法,资质差啊),终于弄明白了。那种感觉啊,就像诚信礼佛的人突然见到如来一样,或者换了贴近自己的比喻:就像千年色鬼见到美女一样的兴奋. 我忍不住的模仿小说中的修真人士突悟大道后的感叹:原来如此!

    下面的分享一下我的收获,基本上是出入门径的写给初学者的,大侠千万要止步,小弟皮薄!

    稍微研究了一下C++汇编后的代码,一般调用C++的成员函数之前,都是使用ECX寄存器保存对象的指针,好在C++成员函数的调用约定__thiscall的参数压栈顺序和堆栈平衡的维护都是和回调函数的调用约定__stdcall一样,所以只需要构造汇编将对象指针保存在ECX寄存器后JMP到成员函数的执行地址就可以了.先写个C++结构拼凑这两条汇编码:

    #pragma pack( push, 1 )
    struct  MemFunToStdCallThunk
    {
        BYTE          m_mov;
        DWORD      m_this;   
    //mov ecx pThis
        BYTE          m_jmp;
        DWORD      m_relproc;  
    //jmp  偏移地址

        BOOL  Init( DWORD_PTR proc, 
    void* pThis )
        {
            m_mov 
    = 0xB9;   // mov ecx
            m_this = PtrToUlong(pThis);
            m_jmp 
    = 0xe9;          //jmp
            
    //jmp跳转的地址为相对地址,相对地址 = 目标地址 - 当前指令下一条指令的地址
            m_relproc = DWORD((INT_PTR)proc - ((INT_PTR)this+sizeof(MemFunToStdCallThunk)));
            ::FlushInstructionCache( ::GetCurrentProcess(), 
    thissizeof(MemFunToStdCallThunk) );
            
    return TRUE;
        }

        
    void* GetCodeAddress()
        {
            
    return this;
        }
    };
    #pragma  pack( pop )

    这个结构相当于两条汇编语句:

    mov ecx, pThis

    jmp [偏移地址]

    使用:

    typedef  void  ( __stdcall *StdCallFun )( int, int );

    class  CTestClass
    {
    private:
        
    int  m_nBase;
        MemFunToStdCallThunk  m_thunk;

        
    void  memFun( int m, int n )
        {
            
    int  nSun = m_nBase + m + n;
            CString str;
            str.Format( _T(
    "%d"), nSun );
            AtlMessageBox( NULL, _U_STRINGorID( str ) );
        }

    public:
        CTestClass()
        {
            m_nBase  
    = 10;
        }

        
    void  Test()
        {
            
    //UnionCastType:利用联合将函数指针转换成DWORD_PTR
            m_thunk.Init( UnionCastType<DWORD_PTR>(&CTestClass::memFun), this );
            StdCallFun fun 
    = (StdCallFun)m_thunk.GetCodeAddress();
            ATLASSERT( fun 
    != NULL );
            fun( 
    13 );
        }
    };

    MemFunToStdCallThunk的Init方法接受成员函数指针和对象指针后就构造成2条汇编码,当调用fun(1,3)时,

    首先将参数3和1压入堆栈,之后跳转到m_thunk处,也就是那构造的2条汇编码处,将对象指针保存到ECX寄存器,之后跳转到指定的成员函数处执行.一切OK了.

    UnionCastType方法的代码如下:

    template< typename TDst, typename TSrc >
    TDst  UnionCastType( TSrc src )
    {
        union
        {
            TDst  uDst;
            TSrc  uSrc;
        }uMedia;
        uMedia.uSrc  
    =  src;
        
    return uMedia.uDst;
    }
  • 相关阅读:
    Android_EditText 密码框默认是小圆点 怎么改成其它的(*)?
    Android_view的生命周期
    Android_对android虚拟机的理解,包括内存管理机制垃圾回收机制。dalvik和art区别
    Android_触摸事件传递机制
    Android_OnLowMemory和OnTrimMemory
    Android_ FragmentTabHost切换Fragment时避免重复加载UI
    位运算&字节运算
    C#编程简短总结
    IOS随机随学
    计算机图形学
  • 原文地址:https://www.cnblogs.com/fangkm/p/1488727.html
Copyright © 2011-2022 走看看