如何让API回调你的VC类成员函数而不是静态函数

VC6.0中改变控件、对话框字体(大小) 1 VC的对话框字体设置对所有控件都有效,你不能单独地改变某个静态文本的字体。需要首先用CreateFont来建立一个字体对象,然后调用控件的SetFont,就可以了。     例子:     1、改静态文体的ID,如:IDC_STATIC1     2、添加一个Edit控件,建立一个关联的控件m_editControl。 3、在OnInitDialog中添加如下代码: 阅读详情

只要在函数声明前加static就好了,哈哈哈哈哈~~~~~  

。。。开个玩笑。以前确实大家都是这样做的,在静态的成员函数中再查找this指针,它多半是全局变量,或者是回调函数提供的附加参数。如果是前者,就会大大破坏程序的结构。而现在,随着社会生产力的发展,偶们已经能做到将成员函数映射成为一个临时的静态函数了。本文就来演示一下这种实现方式。

首先需要包含一个由yzwykkldczsh同志编写的模板类-----万能多用自适应无限制回调模板(为纪念友人fishskin,此模板又称为H>W模板) 

/**************************************************************************
 *   ACCallback.h
 *   Helper class of Member function callback mechanism
 **************************************************************************/
#include "stdafx.h"
#include "windows.h"

#pragma pack(push, 1)
struct _ACCallbackOpCodes
{
 unsigned char tag;  // CALL e8
 LONG_PTR offset;  // offset (dest - src - 5, 5=sizeof(tag + offset))
 LONG_PTR _this;   // a this pointer
 LONG_PTR _func;   // pointer to real member function address
};
#pragma pack(pop)

static __declspec( naked ) int STDACJMPProc()
{
 _asm
 {
  POP ECX 
  MOV EAX, DWORD PTR [ECX + 4] // func
  MOV ECX, [ECX]     // this  
  JMP EAX
 }
}

static LONG_PTR CalcJmpOffset(LONG_PTR Src, LONG_PTR Dest)
{
 return Dest - (Src + 5);
}

/*
 * NOTE: _TPStdFunc: a type of function pointer to API or Callbacks, *MUST* be _stdcall
         _TPMemberFunc: a type of function pointer to class member function,
         *MUST* be the *DEFAULT* calling conversation, *NO* prefix should be added,
          that is, using ECX for "this" pointer, pushing parameters from right to left,
          and the callee cleans the stack.
          _TClass: the class who owns the callback function. The caller should only own the _stdcall function pointer
   LIFE TIME:  It is important to keep the ACCallback object alive until the CALLBACK is not required!!!
 */
template<typename _TPStdFunc, class _TClass, typename _TPMemberFunc>
class ACCallback
{
public:
 _TClass *m_pThis;
 _TPMemberFunc m_pFunc;

private:
 _TPStdFunc m_pStdFunc;

 void MakeCode()
 {
  if (m_pStdFunc) ::VirtualFree(m_pStdFunc, 0, MEM_RELEASE);
  m_pStdFunc = (_TPStdFunc)::VirtualAlloc(NULL, sizeof(_ACCallbackOpCodes), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  _ACCallbackOpCodes *p = (_ACCallbackOpCodes *)m_pStdFunc;
  p->_func = *(LONG_PTR *)&m_pFunc;
  p->_this = (LONG_PTR)m_pThis;
  p->tag = 0xE8;
  p->offset = CalcJmpOffset((LONG_PTR)p, (LONG_PTR)STDACJMPProc);
 }

public:
 ACCallback<_TPStdFunc, _TClass, _TPMemberFunc>()
 {
 }
 ACCallback<_TPStdFunc, _TClass, _TPMemberFunc>(_TClass* pThis,
  _TPMemberFunc pFunc
  )
 {
  m_pFunc = pFunc;
  m_pThis = pThis;
  m_pStdFunc = NULL;
  MakeCode();
 }
 void Assign(_TClass* pThis,
  _TPMemberFunc pFunc
  )
 {
  m_pFunc = pFunc;
  m_pThis = pThis;
  m_pStdFunc = NULL;
  MakeCode();
 }
 ~ACCallback<_TPStdFunc, _TClass, _TPMemberFunc>()
 {
  ::VirtualFree(m_pStdFunc, 0, MEM_RELEASE);
 }
 operator _TPStdFunc()
 {
  return m_pStdFunc;
 }
};

/********************************** EXAMPLE **********************************
class CClass1
{
public:
 TCHAR m_Buf[255];
 BOOL EnumWindowProc(HWND hwnd, LPARAM lp)
 {
  GetWindowText(hwnd, m_Buf, 255);
  printf("Enum window=%s/n", m_Buf);
  return TRUE;
 }
 typedef BOOL (CClass1::*CLASSWNDENUMPROC)(HWND, LPARAM);
};

TO USE:
 CClass1 c1;
 ACCallback<WNDENUMPROC, CClass1, CClass1::CLASSWNDENUMPROC> cb(&c1, &CClass1::EnumWindowProc);
 EnumWindows(cb, 0);

************************* END OF EXAMPLE *********************************/

模板的三个参数分别是:API函数指针的类型,类名字,类成员函数指针的类型(两种函数指针在参数和返回值上应该一样,只是前者声明为_stdcall,后者不加任何调用修饰,即默认的__thiscall方式)
该项头文件的注释中给了一个调用API函数EnumWindows的例子。现在偶们来试试调用SetTimer。

class CTestCallback
{
private:
 /* A callback of SetTimer, mirrored into member OnTimer */
 typedef void (CTestCallback::*CLASSTIMERPROC)(HWND, UINT, UINT_PTR, DWORD);
 void OnTimer (HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
 ACCallback<TIMERPROC, CTestCallback, CLASSTIMERPROC> m_DOnTimer;
}

调用时,只要这样写:
/* 初始化回调结构 */
m_DOnTimer.Assign(this, &CTestCallback::OnTimer);
m_uid = ::SetTimer( NULL, 0, 1000, m_DOnTimer);

最后记得在CTestCallback的析构函数中KillTimer。由于m_DOnTimer会实现转化到静态函数指针类型的操作符,所以调用的地方只要直接写回调结构的名字就可以了。

使用该模板需要注意两点:
1.API函数应当是_stdcall类型的(这一点绝大部分API都满足)。类成员函数必须是默认的调用方式,不要加_stdcall或_cdecl之类的修饰。此方式的重要条件就在于_stdcall和__thiscall之间只相差了一个ECX指出的this指针,所以我们才能实现这种映射(这种方式在VCL和ATL的窗口类中都有使用到);
2.回调结构的生存周期应当是在整个回调函数有效的时间内。因此,对于EnumWindows这样的函数,只要声明在栈上就可以了;但对于SetTimer,就必须定义为类成员变量,同时,在类的析构函数中必须及时销毁这个timer。

 

CLASSTIMERPROC是自定义类型,它的类型是
typedef void (CTestCallback::*CLASSTIMERPROC)(HWND, UINT, UINT_PTR, DWORD);
除了是类成员函数外,其声明与API需要的回调函数TIMERPROC类型相同。在需要显式传递TIMERPROC的地方,不需要做转换,因为ACCallback重载了操作符operator _TPStdFunc()

setwindows(hWnd,gwl_wndproc,m_Dmyproc) ,(应该是SetWindowLong(hWnd, GWL_WNDPROC, m_Dmyproc)吧?) 这里需要的参数是LONG,并不是偶们的函数提供的重载操作符的类型WNDPROC。只写LONG的话是转不过去的,因为类成员函数不能转成一个指针,因此这里需要转换两次。写法是:
SetWindowLong(hWnd, GWL_WNDPROC, (LONG)(WNDPROC)m_Dmyproc);

========================================================

最近在坛子上看到很多有关这方面的讨论的文章,看来确实有再解释一下的必要了。
1.它能实现什么?
简而言之,它能把一个普通的类成员函数变成为一个stdcall的静态函数。就是说从没有this指针到获得一个this指针。
它的用途,发散开来,可以用于:->窗口过程 ->线程过程 ->HOOK函数(特别是多线程下的HOOK函数) 可以使原来打乱程序面向对象的地方变得方便。在Delphi和C#中,类成员函数的委托调用使我们的程序变得非常简洁,如果在下一代C++环境中也使用这种方法,想必会为VC程序员提供不少便利。
听起来有点不可思议,但想明白了就很简单。举个例子:有一个CMyTimer类,它要调用SetTimer方法。它需要SetTimer回调它的成员函数CMyTimer::OnTimer。但是,在内存中,每个CMyTimer的实例是共享同一个OnTimer函数段的,在调用之前,调用方负责把this指针放在ECX里面传给它。在不修改回调方的这种情况下似乎没办法取得this指针。但我们改变一个思路,我们不传OnTimer的地址给回调方,而是为每个CMyTimer实例分配一段新的函数空间,这样在回调时就能根据当前地址查找到自己是哪一个实例了。更简单的,我们干脆就在这段函数空间中嵌入几个指令,让它自动把this指针取到放在ECX里面。这就是本方法的原理。
2.这种技术是什么?
这种技术称之为THUNK技术。(本文贴出来后,听别人说才知道的-___-b,我也是在VCL源代码中找到,然后改进到我的VC程序中)。它的关键在于取得当前地址。用MOV ECX, EIP显然是不可能的。于是我们用CALL指令。CALL指令会把下一条指令地址放到堆栈中。再POP它出来,就拿到了此地址。然后访问它可以得到先前放入的this指针和函数指针。
3.要注意什么?
要注意的方面很多,一个是内存的问题,这段内存必须是有执行的权限,并且生存周期要足够长,直到回调不再需要。还有就是调用约定的问题,使用本模板可以处理任何类型的函数,但必须是普通类成员函数转为stdcall的约定。如果不满足此条件,则需要自己动手写模板。

VC++ 回调函数使用实例 回调函数的理论我说不上多少,但以前给同事写的类中用到过,这次面试中又被问到相关的理论,故将其摘录出来,做一个实例,以后参考。 立即下载

相关推荐

VC++回调函数

绝对可以运行VS2008编译通过,很简单的回调函数例子,该回调函数封装在DLL中可以供C# C++调用

回调函数中使用MFC类的成员或对话框控件的简单方法

一:目的 1、在MFC的很多程序中,常常需要在回调函数中调用MFC类的类成员变量、类成员函数,亦或者对话框控件的句柄。由于回调函数是基于C编程的Windows SDK的技术,而类成员又有this指针客观条件限制...所以在回调函数中不能够直接访问类成员,或者操纵控制MFC对话框的控件。虽然可以把回调函数设为类的静态成员函数,以便消除this指针的限制,但诸多不便(比如只能访问静态成员等)仍让人不能够顺畅访问类成员。而下面这种方法便无需把回调函数设为类的静态成员,也能够顺利的访问类的成员和对话框控件。...

qq_40544338的博客 898

VC中回调函数(callback)深入解析

通过此文讲解,让希望学好vc的同学真正理解vc中回调函数的结构和意义

解决SetTimer()回调函数访问类成员变量的一个好方法

void* g_paclass=NULL;  ... g_paclass=this;SetTimer(hWnd,1,1000,callbackfun);void CALLBACK callbackfun(...){   Caclass* pthis=(Caclass*)g_paclass;   pthis->....   do something

vczxh的专栏 1473

vc++回调函数的用法

讲了c++中回调函数的基本知识,对回调函数的基本原理讲解细致

别让IO口保护二极管坑了你!实测讲解单片机电流倒灌的三种典型症状与防护

本文深入解析单片机电流倒灌的三大典型症状与防护方案,重点分析STM32等单片机在ADC采样异常、系统假死和通信速率异常中的表现。通过实测数据和电路设计建议,提供从基础二极管隔离到专业隔离器件的分级防护策略,帮助工程师有效预防和解决电流倒灌问题。

weixin_29280219的博客 344

从C++类成员函数作为回调函数说起

介绍使用boost.function和boost.bind将类的普通成员函数用于回调机制。以及这种回调机制相比通过虚函数和继承实现回调的优势。

MPForwd的专栏 5822

NVIDIA Jetson Orin DLA架构解析与AI推理优化实践

深度学习加速器(DLA)作为专用AI推理芯片,通过固定功能架构实现卷积、池化等神经网络操作的高效能比处理。其核心原理在于硬件级优化计算单元与数据流,相比通用GPU可提升3-5倍能效。在边缘计算场景中,DLA与GPU的异构协同成为关键,特别适合自动驾驶感知、机器人视觉等实时性要求高的应用。NVIDIA Jetson Orin平台通过结构化稀疏技术,在ResNet50等模型上实现近2倍性能提升。开发实践中需注意算子支持、精度校准等关键点,结合TensorRT工具链可充分发挥DLA在低功耗场景的独特优势。

weixin_32999557的博客 397

C/C++回调函数

C/C++回调函数 回调函数就像您随身带的BP机:告诉别人号码,在它有事情时Call您          回调用于层间协作,上层将本层函数安装在下层,这个函数就是回调,而下层在一定条件下触发回调,例如作为一个驱动,是一个底层,他在收到一个数据时,除了完成本层的处理工作外,还将进行回调,将这个数据交给上层应用层来做进一步处理,这在分层的数据通信中很普遍。

blacet的专栏 1507

数字IC后端先进工艺设计实现之TSMC 12nm 6Track工艺数字IC后端实现重点难点盘点

如果你仅仅是跑个先进工艺的flow,不懂先进工艺在数字IC后端实现上的不同点,为何有这样的不同点,针对这些不同之处后端实现阶段如何考虑它们,那么你等于0经验!大家可以针对本文所列的要点进行复盘,如果这些都搞透彻了,你就可以大胆把T12 nm的A72和A55项目写到你的简历中。T12这个工艺也是支持Preferred和Non-prefered两个方向来完成routing的,但是它对它们的最小线宽和最小线间距提出了更严格的要求。所有各个子模块的形状,彼此之间的间距都是有严格的要求。为何要有P48这一层?

weixin_37584728的博客 8340

【分子指纹】化学分子指纹的概念和应用(非常重要)

分子指纹(Molecular fingerprints)可以低计算成本的方式表示大规模化学数据集中化合物的化学(结构、物理化学等)性质。它们在将化学数据集中的分子转换为适合于计算方法的一致输入格式(bit向量或数值)方面发挥着重要作用。在这篇综述中,

DJJ5210的博客 1万+

linux多线程创建时使用类成员函数作为参数

实际上所有线程都是用来处理C函数的,而不是C++类成员函数。标准库中提供一个API函数,这个函数以回调函数指针作为线程的执行代码并在单独的线程中调用回调函数。问题是在这样的线程库中不能创建执行对象成员函数的线程;只能使用普通的函数。因此,下列代码是失败的: #inclu

dusiliang的专栏 3044

计算机国际会议中的proceedings、conference、paper、workshop、demo:如何区分与选择?

本文详细解析了计算机国际会议中的核心概念:Conference(会议)是学术交流的主舞台,Paper(论文)是研究成果的书面呈现,被接收后收录于Proceedings(会议论文集)。Workshop(研讨会)聚焦前沿细分领域,Demo(演示)则侧重系统与工具的可交互展示。文章为研究者根据自身阶段与工作类型,如何区分与选择投稿目标提供了实用指南。

weixin_29269441的博客 562

API和库函数有什么区别

C运行时库函数  是指 C语言本身支持的一些基本函数,通常是汇编直接实现的。。。。    API函数是操作系统提供给用户方便设计应用程序的函数,实现一些特定的功能,API函数也是c语言的函数实现的。  他们之间区别是:API函数是针对操作系统的,C语言运行时函数则是针对C语言本身的 1)运行时库就是 C run-time library,是C而非C++语言世界的概念:取这个名字就是因为你

su_changqi的博客 9690

【YOLOv11工业级实战】15. YOLOv11海洋监测实战:珊瑚礁健康评估(水下图像增强|机器人搭载)

摘要:本文针对珊瑚礁健康评估的海洋监测需求,基于YOLOv11提出解决方案,聚焦水下色偏、悬浮物干扰、珊瑚形态复杂三大难题。引用全球珊瑚礁监测网络(GCRMN)标准,采用CoralNet数据集,通过Jaffe-McGlamery物理模型引导增强与悬浮物去除算法,在浑浊水域(能见度<5m)实现87.3%的健康珊瑚识别率,藻类误判率低至7.2%。介绍了以BlueROV2水下机器人和Jetson AGX Orin为核心的部署方案,结合水下SLAM定位技术,实现珊瑚健康的实时评估与三维覆盖率计算。

专注于人工智能、软件开发、工控自动化、工厂数字化及智能化等领域,希望和大家共同进步! 1644

Mac 安装neo4j(解压版)最新版本教程

本文介绍了在Mac M系列操作系统上安装Neo4j图数据库的具体步骤。内容包括:从官网下载解压版Neo4j社区版压缩包;通过终端命令将其解压到/opt目录并重命名;配置环境变量以便全局访问;以及常用命令如启动/停止服务、重置密码等。安装完成后,用户可通过浏览器访问http://localhost:7474,使用默认账号neo4j/neo4j登录并根据提示重置密码。该教程简洁明了地指导用户完成Neo4j在Mac上的安装配置过程。

猿小白的博客 909

线程创建函数与类成员函数

http://www.yesky.com/345/1658345.shtml 问题的提出:   实际上所有线程都是用来处理C函数的,而不是C++类成员函数。标准库中提供一个API函数,这个函数以回调函数指针作为线程的执行代码并在单独的线程中调用回调函数。问题是在这样的线程库中不能创建执行对象成员函数的线程;只能使用普通的函数。因此,下列代码是失败的: //

cc96610536的专栏 699

【项目实战】SOVD协议(面向服务的车辆诊断协议)

此外,AUTOSAR组织将SOVD部分内容的软件实现定义在了R22-11及以上版本的AUTOSAR Adaptive规范中,还支持动态发现车内诊断功能、获取高性能计算机(HPC)的Log信息等新的诊断需求,更符合当前软件定义汽车概念下的行业趋势。而SOVDGateway作为边缘节点,也肩负着对SOVD Client进行认证以及权限控制的责任,从而保证车辆的信息安全。相较于传统的UDS(Unified Diagnostic Services)协议,SOVD不仅保留了传统的读写数据、例程控制、故障读取等功能,

奶爸的编程之路,也就一周冷个三天 1905

linux 线程函数 可以是 类的成员函数,linux多线程创建时使用类成员函数作为参数...

实际上所有线程都是用来处理C函数的,而不是C++类成员函数。标准库中提供一个API函数,这个函数以回调函数指针作为线程的执行代码并在单独的线程中调用回调函数。问题是在这样的线程库中不能创建执行对象成员函数的线程;只能使用普通的函数。因此,下列代码是失败的:#include "class1.h"int func (void *param ){pthread_t ptid;// 下列调用导致编译器错误...

weixin_42131601的博客 314

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

上一篇: ShellExecute执行没有反应
下一篇: 木马攻击与防御原理
ShenRui
博客等级 码龄20年 63粉丝 36原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值