利用thunk实现类非静态函数成为回调函数

 首先建一个基于MFC对话框的工程.在dlg类中插入如下代码:

class CTestTimer01Dlg : public CDialog

{

//...........

 private:
  BYTE  m_codeCmd[10] ; //代码命令
        //mov 地址
        //jmp 地址
  void _InitCodeCmd(DWORD dwpThis, DWORD dwProcPtr);
  DWORD _GetMessageProcPtr();
 protected:
  
  // Construction
  int  m_iTestShow;
  //调用规则用默认的,即__thiscall
  //用其它调用规则不行(可能__fastcall可以,但尽量不用)
  virtual  VOID  _OnTimerSink(HWND hWnd, DWORD dwMsg , WPARAM wPa, LPARAM lPa); 

//...........

}
 

  
// CTestTimer01Dlg message handlers
VOID CTestTimer01Dlg::_OnTimerSink(HWND hWnd, DWORD dwMsg , WPARAM wPa, LPARAM lPa)
{
 ++m_iTestShow;
 InvalidateRect(NULL);
}

void CTestTimer01Dlg::_InitCodeCmd(DWORD dwpThis, DWORD dwProcPtr)
{
 
   /*
 这段机器码相当于如下汇编
 ---------------------------   -------------------------------------
 B9 ?? ?? ?? ??                mov    ecx, dwpThis ; Load ecx with this pointer
 E9 ?? ?? ?? ??                jmp    dwProcPtr  ; Jump to target message handler
    */
    m_codeCmd[0] = 0xB9; //mov命令,负责传递this指针
       //成员函数一般是
      //使用ECX传递this指针,即__thiscall调用,成员函数默认都是
      //__fastcall调用:编译器会尽量 使用ECX传递参数

    //使用栈传递this指针,即__stdcall或__cdecl调用
      //_stdcall是被调函数自己恢复调用栈
      //__cdecl是调用者恢复调用栈.可以用来实现不定长参数的函数
    m_codeCmd[5] = 0xE9; //jmp命令
    *((DWORD *) &m_codeCmd[1]) = (DWORD) dwpThis;
 //跳转偏移量
 DWORD dwDistance = (DWORD) dwProcPtr - (DWORD) &m_codeCmd[0] - 10;
    *((DWORD *) &m_codeCmd[6]) = dwDistance;
 
}

DWORD CTestTimer01Dlg::_GetMessageProcPtr()
{
 /*
 typedef VOID (CTestTimer01Dlg::*pmf)(HWND hWnd, DWORD dwMsg , WPARAM wPa, LPARAM lPa); 
 pmf p = &CTestTimer01Dlg::_OnTimerSink;
 DWORD off = 0;
 _asm
 {
  mov eax,  p     
  mov [off], eax
 }
 return off;
 //vc6中无法通过编译
 
 DWORD dwProcAddr = 0;
 __asm
 {
  mov eax, offset CTestTimer01Dlg::_OnTimerSink
  mov dword ptr [dwProcAddr], eax
 }

 return dwProcAddr;*/
 //得到定时器回调的指针
 //编译器不容许从CTestTimer01Dlg::_OnTimerSink强制转化到DWORD
 //为了实现兼容,用union_cast实行转化
 return union_cast<DWORD> (&CTestTimer01Dlg::_OnTimerSink); 
}

BOOL CTestTimer01Dlg::OnInitDialog()
{
 CDialog::OnInitDialog();
 _InitCodeCmd((DWORD)this, _GetMessageProcPtr());
 ::SetTimer(NULL, 1, 500, (TIMERPROC)(void *)m_codeCmd);

//下面代码省略

}

 

这里用到了类型转化函数,union_cast,定义如下:

template <class ToType, class FromType>
ToType union_cast(FromType f)
{
    union
    {   
  FromType _f;
  ToType   _t;
    } ut;

memset(&ut, 0, sizeof(ut));
    ut._f = f;
    return ut._t;
};

    ps:WTL正是靠这种手法,实现了成员函数处理消息

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如qsort 等函数需要函数指针才能回调 用此函数库可以将成员函数指针转为普通函数指针 测试代码如下 #include <stdio.h> #include <algorithm> #include <vector> #include <string> #include <iostream> #include <math.h> using cmpfunc = int(__cdecl*)(const void*, const void*); using DebugArrayFunc = void(__stdcall *)(std::string &out;); #include "thunk.h" class MySort { public: int Rule; MySort(int a):Rule(a){} // 回调函数 template<typename T> int __cdecl sort(const void* a, const void* b); }; class Test { public: std::vector<int> mm; void Sort(int (*comp)(const void *,const void *)) { return qsort(mm._Myfirst,mm.size(),sizeof(int),comp); } void Entry(DebugArrayFunc func) { std::string string; cmpfunc comp; TemplateThunk athunk; // 正序 comp = (cmpfunc)athunk.GetCall(&MySort;::sort<int>, &MySort;(0)); Sort(comp); func(string); std::cout << string << std::endl; // 逆序 comp = (cmpfunc)athunk.GetCall(&MySort;::sort<int>, &MySort;(1)); Sort(comp); func(string); std::cout << string << std::endl; } }; class CallBack { public: std::vector<int> *pthis; CallBack(std::vector<int> *ff):pthis(ff){} void __stdcall DebugArray(std::string &out;) { char buff[100]; char *aa = buff; for each (auto a in *pthis) { aa += sprintf(aa, "%d ", a); } out.assign(buff); } }; void main() { TemplateThunk athunk; Test tt; tt.mm = { 1, 3, 7, 8, 5, 6, 4, 2, 3, 10 }; tt.Entry(athunk.GetCall(&CallBack;::DebugArray,&CallBack;(&tt;.mm))); } template <typename T> int __cdecl MySort::sort(const void* a, const void* b) { return Rule ? *static_cast<const T*>(a)-*static_cast<const T*>(b) : *static_cast<const T*>(b)-*static_cast<const T*>(a); }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值