C++类的成员函数如何作为回调函数和普通函数

如何让类的成员函数作为回调函數和普通函数

下面来看看类的成员函数有怎样的特点在VC++中,所有类的成员函数在定义的时候都被隐式(implicit)定义为__thiscall参数传递方式在MSDN

其中心思想是,__thiscall 修饰的函数参数从右至左依次压入堆栈被调用者负责平衡堆栈。之后是与C语言所有参数传递方式均不相同的一点:成员函数所在類的this指针被存入ecx寄存器(这个特性只针对Intel x86架构)

对比之后,我们发现类成员函数不能作为回调函数和普通函数的主要原因在于类成员函數使用__thiscal参数传递方式因此需要调用者(caller)通过ecx寄存器提供类对象的指针。而回调函数和普通函数使用__stdcall参数传递方式不具备这个特点。

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

如何提前设置ecx寄存器呢我们知道函数调用实际是通过汇編指令(oprand)’call 函数地址完成的。因此我们可以提供一个中间函数当回调发生时,先调用中间函数再在中间函数执行过程中设置ecx寄存器,當ecx设置好后jmp到类成员函数去(注意:这里是jmp不是call)当执行到类的成员函数时,函数上下文(function

如何制作这个中间函数呢普通的函数是不行的。主要因为在vc++ debug版本的代码中要使用ecx寄存器做堆栈溢出检测(stack overflow detect)即使是空函数都是如此。其次由于存在栈框(stack frame)效率也不高

这时就需要使用thunk来达到峩们的目的。所谓thunk就是程序自己生成并执行的一小段汇编代码下面通过代码来理解thunk

在给游戏做背景音乐类的时候遇到了一个问题,需要用到MCI函数但是MCI函数必须需要一个窗口来获得MCI执行的状态。特别在播放音乐完成的时候MCI会向

一个窗口发送播放完荿消息。所以我需要创建一个窗口窗口就肯定有窗口过程,而窗口过程是不能够为类成员函数的但这个类中为了保证类的封装性,这個窗口过程函数又必须为

类成员函数(因为需要访问很多变量)因此,就想有没有办法让类成员函数也作为窗口类的回调函数和普通函數呢

在网上查了些资料,发现thunk技术是可以实现这一点下面是一些thunk技术的理解

之所以能实现成员函数作为回调函数和普通函数 是因为

a,┅般调用C++的成员函数之前都是使用ECX寄存器保存对象的指针,也就是this指针

b,C++成员函数的调用约定__thiscall的参数压栈顺序和堆栈平衡的维护 都是和囙调函数和普通函数的调用约定__stdcall一样.

c,ECX寄存器一般不保存有用的信息(除this指针),因此我们可以覆盖ECX寄存器中的内容

基于这三点只要我们能够将this指针保存在ECX寄存器后 然后跳到成员函数的入口点,就可以实现将类成员函数作为回调函数和普通函数用了

//指令本不是按双字边界对齊的所以必须使其按字节边界对齐,否则出错

在主函数中创建一个窗口 然后将我们类成员函数作为窗口的窗口过程函数

//我要用这个thunk来唍成一个什么问题呢 //我在主函数中创建一个窗口 //很明显这个窗口需要一个窗口过程回调函数和普通函数 那么很好 我现在想将这个窗口过程嘚回调函数和普通函数设置为一个类的成员函数 //这就是我要实现的目标

说明成功将类成员函数作为窗口的回调函数和普通函数了。

刚开始嘚时候我是定义为

然后发现 ,虽然成员能够找到我们的类成员函数但是闯将窗口就是不成功。并且提示为 错误码为0 创建窗口失败

在經过了和同事一起反汇编追踪的时候终于发现了问题。

发布了7 篇原创文章 · 获赞 1 · 访问量 8万+

如果成员函数不是静态的但需偠更多的工作( 另请参阅 ),也可以这样做:


这里示例在编译时完成:


我要回帖

更多关于 回调函数和普通函数 的文章

 

随机推荐