利用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;
    BYTE      m_jmp;
    DWORD      m_relproc;

    BOOL  Init( DWORD_PTR proc, void* pThis )
    {
        m_mov = 0xB9;
        m_this = PtrToUlong(pThis);
        m_jmp = 0xe9;
        m_relproc = DWORD((INT_PTR)proc - ((INT_PTR)this+sizeof(MemFunToStdCallThunk)));
        ::FlushInstructionCache( ::GetCurrentProcess(), this, sizeof(MemFunToStdCallThunk) );
        return TRUE;
    }

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

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

mov ecx, pThis

jmp [偏移地址]

使用:

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( 1, 3 );
    }
};

时间: 2024-08-30 03:37:09

利用Thunk让C++成员函数变回调函数的相关文章

怎么实现类的成员函数作为回调函数_C 语言

如果试图直接使用C++的成员函数作为回调函数将发生错误,甚至编译就不能通过.其错误是普通的C++成员函数都隐含了一个传递函数作为参数,亦即"this"指针,C++通过传递this指针给其成员函数从而实现程序函数可以访问C++的数据成员.这也可以理解为什么C++类的多个实例可以共享成员函数却-有不同的数据成员.由于this指针的作用,使得将一个CALL-BACK型的成员函数作为回调函数安装时就会因为隐含的this指针使得函数参数个数不匹配,从而导致回调函数安装失败.要解决这一问题的关键就

vc++6 0-全局键盘钩子函数的回调函数有时无效

问题描述 全局键盘钩子函数的回调函数有时无效 DLL代码: myHooke.c #include "myHooke.h" #include <windows.h> #include <winuser.h> #include <stdlib.h> #include <stdio.h> int ndown = 0; HHOOK hhkHook = NULL; //定义钩子句柄 HINSTANCE hInstance = NULL; //程序实

关于C#调用C++函数绑定回调函数后发生CrossThreadMessagingException异常的问题

问题描述 代码如下://事件函数原型//typedefBRIINT32(CALLBACK*PCallBack_EventEx)(PBRI_EVENTpEvent,BRIUINT32dwUserData);//事件函数代理publicdelegateInt32CallbackDelegate(BriSDKLib.TBriEvent_DataeventData,Int32dwUserData);//代理对象publicstaticCallbackDelegatecallback;//事件回调函数pr

C++回调函数用法

一回调函数 我们经常在C++设计时通过使用回调函数可以使有些应用(如定时器事件回调处理.用回调函数记录某操作进度等)变得非常方便和符合逻辑,那么它的内在机制如何呢,怎么定义呢?它和其它函数(比如钩子函数)有何不同呢? 使用回调函数实际上就是在调用某个函数(通常是API函数)时,将自己的一个函数(这个函数为回调函数)的地址作为参数传递给那个函数. 而 那个函数在需要的时候,利用传递的地址调用回调函数,这时你可以利用这个机会在回调函数中处理消息或完成一定的操作.至于如何定义回调函数,跟具体使用的 A

C语言回调函数和this指针详细介绍

在C里面,经常需要提供一个函数地址,注册到结构里,然后在程序执行到特定阶段时,回调该函数.创建线程,注册线程运行的主函数就是一个典型的例子.这里以简单的回调实例,说明C++中回调函数为成员函数时有关this指针的问题.由于C++对C的继承关系,C++没有自己的线程封装技术,一般而言我们创建线程时,还是用C的回调函数机制.类似的例子也挺多的.在Java等纯粹的面向对象语言,则不一样,不光有自己的独立的线程类型,对于回调,也是注册整个对象,而不是注册一个方法,如常用的观察者模式.这里,在网上查阅了大

如何向回调函数中传入其他参数

如何向回调函数中传参数   最近写JS经常会因为向回调函数中传参而头疼,今天总结一下向回调函数中传参的方法,以后的应用中就不用在到处去找了.   首先构建一个需要向回调函数中传入参数的典型应用.在一个页面中产生了一系列的向Ajax Proxy的请求,传入的是一个ID,根据ID返回了不同的内容值,我们需要把这些内容打印在页面上,同时给页面元素赋予ID,这个时候就需要向回调函数中传入ID,以产生带ID的页面元素.   第一种方法就是使用全局变量,能够被函数和回调函数同时访问.这种方法虽然不够优雅,但

javascript回调函数(模式)原理和示例深入分析

                                                                                 广大网友读懂了我之前论述的javascript原理这篇文章很容易懂 回调函数来自一种著名的编程范式--函数式编程,在基本层面上,函数式编程指定的了函数的参数.函数式编程虽然现在的使用范围变小了,但它一直被"专业的聪明的"程序员看作是一种难懂的技术,以前是这样,未来也将是如此. 幸运的是,函数式编程已经被阐述的像你我这样的一般人

从零开始学_JavaScript_系列(四)——jquery(基础,选择器,触发条件,动画,回调函数)

jQuery语法   (1)引用jquery文件及下载库: http://jquery.com/download/ 下载 Download the compressed, production jQuery 2.2.2 这个是用户版的,已经被精简和压缩. 然后使用 <script src="jquery.js"></script> 来启动这个库文件,记得将下载的文件重命名为jquery.js 也可以使用谷歌和微软的CDN,不过这里略.   (2)jQuery语法

delphi-Delphi 调用C++ dll 回调函数

问题描述 Delphi 调用C++ dll 回调函数 用Delphi 调用容联云通信的动态库CCPAppClient.DLL,其中有一个函数 Function CCPinit( CallbackInterface: CCPCALLBACKINTERFACE ):Integer;stdcall; 参数CCPCALLBACKINTERFACE 是一个结构体指针,包含 onConnected , onConnectError 等回调函数. 在调用函数CCPinit调用成功后(返回值0),会触发CCPC