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

探秘窗口过程函数(WndProc)的Thunk技术 最近看了一篇讲ATL Thunk技术的好文章(下载),收获较多,在此做一个总结。 Thunk技术的由来 我们知道,经典的Windows程序设计采用纯Windows API来实现,创建一个窗口必须严格遵循“定义窗口类,注册窗口类,创建窗口,显示窗口,更新窗口,启动消息循环”的步骤。虽然经典的Windows程序设计在一定程度上已经体现出了OOP的思想 (对象 = 数据 + 代码),但是与当代的OO 阅读详情

申明:本文非笔者原创,原文转载自: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(), 
this, sizeof(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( 
1, 3 );
    }
};
复制代码

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技术和它的使用场景 阅读详情

相关推荐

C++ Thunk技术(初学版)

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

VxWorks driver (PCI, USB, SERIAL) 3690

关于Thunk

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

Solidus的专栏 1013

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

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

fngyq的专栏 1117

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的专栏 4790

DSP28335新手避坑指南:手把手教你用CCS6.2生成10KHz SPWM(附完整工程)

本文详细介绍了如何使用DSP28335开发板和CCS6.2环境生成10KHz SPWM波形的完整工程指南。从开发环境配置、ePWM模块设置到SPWM算法实现,特别针对新手常见问题提供了解决方案和调试技巧,帮助工程师快速掌握DSP28335的SPWM生成技术。

weixin_42522374的博客 182

科研绘图系列:R语言绘制世界地图数据分布(world map)

科研绘图系列:R语言绘制世界地图数据分布(world map)

专注生信领域 527

什么是thunk技术实现?

/************************ Copyright (c) 2013 ************************ 功能描述:     thunk技术实现.利用这个类可以把回调函数完全封装成类成员函数. 调用示例:  //导入头文件及库文件  #include "ZThunk.h"  class CTimer  {  private:   // 此对像

FreeBird的专栏 1575

CryoEM - 使用 ChimeraX 工具 操作 冷冻电镜的 MRC 密度图(Density Map) 教程

由冷冻电镜 CryoSparc 软件所生成的 Volume,需要观察其内部结构,建议使用 ChimeraX 进行操作。

AGI 4962

c++ thunk

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

Johny的专栏 7347

【图论】系列(七)图论的中心性度量方法及重要性评价指标

图论中的中心性度量方法是用来衡量图中各个节点(或顶点)相对重要性的指标。

qq_30066733的博客 1076

四非人的保研之路,2024(2025届)四非计算机的保研经验分享(西南交通、苏大nlp、西电、北邮、山软、山计、电科、厦大等)

本身我就是福建人,xmu 3小时就能到家,然后导这边毕业要求容易,不想读博,达到要求就能放实习,每周打卡35h,比较自由,遂考虑。北邮弱com,联系了蛮多老师,几乎都给了考核机会,面试的老师好几位都考核人工智能基础,会问你卷积的计算,问的还是蛮细的。当时结果是暂定,以为是稳了,遂没联系其他老师,但是之后询问发现只是候补,懒得联系其他老师,928候补到,放弃。东北大学,2000人大机试,入营点送,全国巡回面试,每个人就面不到10分钟,我就被问了3个问题就结束了,候补。科研组,导师有人脉,想读博出成果可以去。

专注于AI领域前沿知识,业余爱好开发软件 4352

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

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

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

多模型机器学习生存预测分析:AUC比较、生存分层与SHAP解释

本文研究了多种机器学习模型在心力衰竭患者生存预测中的应用。通过比较逻辑回归、随机森林、XGBoost、LightGBM和梯度提升机五种模型的AUC表现,选择最优预测模型。基于最优模型的预测概率对患者进行风险分层,并使用Kaplan-Meier生存分析和log-rank检验验证分层的有效性。最后采用SHAP方法解释模型决策过程,识别关键预测因素。研究提供了从模型构建、评估到解释的完整技术流程,为临床预后预测提供了可复现的分析框架。

AI的内部世界的博客 157

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

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

glyc的专栏 1475

Claude Code Skills 入门:内置技能与自定义技能开发

│ Skill 的特点 │1. 封装性:把复杂操作封装成简单命令2. 可复用:一次定义,随处使用3. 可定制:支持参数传入4. 可发现:通过 /skill-name 方式调用打个比方,如果说 Claude Code 的基础工具是「原材料」,那 Skills 就是「预制菜」——只需要简单加热(调用),就能得到成品(结果)。你可以创建自己的 Skill 来封装常用操作。Skill 支持参数,通过。

gaoxiang_120的专栏 671

全能媒体工具深度测评(下篇一):AI图文生成+全能解压+图片去文字,一键搞定素材精修

解压50多种格式,文生图,图生图

cs821984831的博客 40

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

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

Tel18545051023的博客 155

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

C++ Thunk技术

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

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

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

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

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

rongwenbin的专栏 626

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

c++ THUNK技术

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

ghevinn欢迎您光临 2450

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

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

Bad Alloc 1467

回调函数

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

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

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值