利用Thunk让C++成员函数变回调函数

C++中的Thunk技术和它的使用场景 阅读详情

申明:本文非笔者原创,原文转载自:https://www.cnblogs.com/fangkm/archive/2009/05/25/1488727.html

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;
}


C++ Thunk技术(初学版) 今天看到一篇关于Thunk技术的文章,特转过来。这里想说的是:代码中的关键点为用指令jmp pFunc跳转到你想要执行的函数pFunc。指令“jmp xxxx”占5个字节,代码中用了个一字节对齐的结构体struct Thunk ,当然也可以用 unsigned char code[5]; 说另一个关键点就是地址计算了,jmp xxxx指令用了相对跳转地址,相对地址 = 要跳转函数的 阅读详情

相关推荐

QT中使用THUNK技术实现回调问题

<br />   C++中使用回调,考虑了多种都不行,感觉不能达到自己的要求。搜索资料看到THUNK技术,貌似在基于对象实现回调这一块用的很广泛。看了看大致原理,的确不错。把成员函数的调用原理加以分析,相对应的寄存器写入this指针的值。THUNK形成汇编代码块,调用底层指令,对相应的寄存器操作。这些操作也就是基本模仿成员函数调用时的操作,实现成员函数的调用。大致这样吧,按照网上的方法,其实是能够达到效果的。但是总会有问题啊,而且很郁闷。<br />    m_thunk.Init(pointer_

fngyq的专栏 1115

关于Thunk

最近心情越来越差了,我心情不好的时候就喜欢倒腾些小玩意和吃东西,下面就是我的小玩意之一,其实也是两个月以前的代码了,现在把它写成文档,希望能帮我回复心情,现在开始正题~~     两年前我就听过这个技术,大概其也算是懂点,但是没实做过,前些时用MFC做UI时需要一个对SetTimer API的封装,因为这类API对C++对象的支持很不好,所以,我们总得耍点花招对付它,于是重拾起了这个Thunk

Solidus的专栏 1008

C++ Thunk技术

一. 使用回调函数时遇到的问题 在使用Win32 API(如CreateThread等)时我们经常需要传入回调函数,这个回调函数不能是类的成员函数,只能是友元函数或静态函数。为了在回调函数中访问某个类的成员量或函数,我们通常不得不通过某种方式将该类的This指针传入到回调函数中,从而实现在回调函数中访问类的公有的成员量或函数。 如果有一种方式可以实现将类的成员函数作为回调函数来使用,那么就...

while(1) { smile(); } 5万+

任意改参数的64位Thunk

首先大家要明白什么是Thunk,为什么要用Thunk,通俗的来说Thunk就是将windows中一些回调函数成一种面向对象的技术。 VC中函数前有callback的就是其中一种,其中最有代表的函数 SetWindowLong 这个用来子类化窗体时要求一个回调函数如下 复制代码 LRESULT CALLBACK WindowProc(HWND hwnd,              

嬴政 1085

c++ thunk

<br />之前参加了一次面试,问到了SetTimer中回调设计成类成员函数的问题.后来想写一下,但是一直没有找到相关文章.后来lori哥指点,原来这就是C++ Thunk技术.<br /> <br />Thunk : 将一段机器码对应的字节保存在一个连续内存结构里, <br />然后将其指针强制转换成函数. 即用作函数来执行.<br /><br />这份代码用于将C语方的回调函数转换为具有this指针的C++成<br />员函数.<br /><br />在Windows系统编程中, 可以将一些系统回函数封

Johny的专栏 7252

C++中的Thunk技术 / 非静态类成员函数作为回调函数 的实现方法

申明:本文非笔者原创,原文转载自:http://www.cnblogs.com/memset/p/thunk_in_cpp.html 用我的理解通俗地解释一下什么是C++中的Thunk技术吧!   Thunk技术就是申请一段可执行的内存, 并通过手动构造CPU指令的形式来生成一个小巧的, 具有明确作用的代码块.   小巧? 具有明确作用? 你曾经初学C++时, 如果我没猜

计算机视觉小菜鸟的专栏 3689

Windows下利用thunk实现回调成员函数(1)

今天由于工作需要,研究了下thunk技术,下面的内容算是个小小的总结吧。我们知道,在Windows和x86下,函数调用主要有以下几种方式:__stdcall这是Windows下默认的函数调用方式。调用者负责把函数参数从右到左压入堆栈,函数在返回前负责清理堆栈。用汇编语言表示就是:调用方:mov esi, esp;保存ESPpush ....

glyc的专栏 1472

ATL之thunk技术

<br /><br /><br />学习下 ATL 的 thunk<br />由于 C++ 成员函数的调用机制问题,对C语言回调函数C++ 封装是件比较棘手的事。为了保持C++对象的独立性,理想情况是将回调函数设置到成员函数,而一般的回调函数格式通常是普通的C函数,尤其是 Windows API 中的。好在有些回调函数中留出了一个额外参数,这样便可以由这个通道将 this 指针传入。比如线程函数的定义为:<br />typedef DWORD (WINAPI *PTHREAD_START_ROUTINE

xsh_123321的专栏 4787

如何让C++类的成员函数作为回调函数

转自:http://my.oschina.net/opening/blog/55289 为什么类(class)的成员函数(member function)不能作为回调函数(callback function)? 首先来看看回调函数有怎样的特点。windows中,回调函都显式(explicit)使用CALLBACK修饰符(decorator)修饰 (decorated)。实际上CAL...

Tel18545051023的博客 151

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

为什么类(class)的成员函(member function)数不能作为回调函数(callback function)? 首先来看看回调函数有怎样的特点。windows中,回调函都显式(explicit)使用CALLBACK修饰符(decorator)修饰 (decorated)。实际上CALLBACK就是_stdcall参数传递方式(calling convention)的宏定义。MSDN中

rongwenbin的专栏 625

c++ THUNK技术

这里想说的是:代码中的关键点为用指令jmp pFunc跳转到你想要执行的函数pFunc。 指令“jmp xxxx”占5个字节,代码中用了个一字节对齐的结构体struct Thunk , 当然也可以用 unsigned char code[5]; 说另一个关键点就是地址计算了,jmp xxxx指令用了相对跳转地址, 相对地址 = 要跳转函数的地址 - “jmp xxxx”指令的下一

ghevinn欢迎您光临 2448

C++ 通过Thunk在WNDPROC中访问this指针

本文基本只讨论原理,具体实现请参见后续文章《C++ 通过Thunk在WNDPROC中访问this指针实现细节》 当注册窗口类时,WNDCLASSEX结构的lpfnWndProc成员应设置为窗口过程函数的地址,这是一个C风格的函数指针,所以我们只能使用全局或静态函数的地址,这在我们将窗口封装为C++类时会很麻烦,因为我们无法在一个全局或静态的WindowProc函数中直接访问类实例,这就需要一些手

Bad Alloc 1463

回调函数

如何做类的回调函数 前些日子用一个PIPE类封装了WINDOWS的录音放音设备,程序写得有点类似与操作系统的PV信号互锁机制,这里面需要将辅助录音现程采集到的 数据存储到Buffer,然后做一个回掉函数做处理,以下是几种实现回掉的方法,程序最后使用了THUNK机制,再说明这种机制之前。先澄清一下回掉函数 的概念。 所谓回调,就是客户程序C调用服务程序S中的某个函数A,然后S又在某个时候反过来调用C中的某个函数B,对于C来说,这个B便叫做回调函数。例如Win32下的窗口过程函数就是一个典型的

hiside的专栏 1939

C++ 通过Thunk在WNDPROC中访问this指针实现细节

本文代码使用了一些C++11特性,需要编译器支持。本文仅讨论x86_64平台的相关实现,x86平台理论上只需修改 thunk 相关机器码即可。 THUNK的原理参见之前的一篇博文《C++ 通过Thunk在WNDPROC中访问this指针》 首先定义我们的window类,该类实现对一个Win32窗口句柄的封装。 该类将在构造函数中创建窗口,在析构时销毁窗口; 窗口的消息过程函数(Window

Bad Alloc 1661
上一篇: 在函数前面加上WINAPI、CALLBACK
下一篇: C++中的Thunk技术 / 非静态类成员函数作为回调函数 的实现方法
ChenLee_1
博客等级 码龄17年 6200粉丝 179原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值