类成员函数作为回调函数

 文章来自: http://www.cnblogs.com/zhangminaxiang/archive/2013/02/27/2934466.html

缘由:

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

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

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

Thunk技术

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

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

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

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

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

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

代码实现:

1
thunk.h 文件
1
2
3
4
5
6
7
8
#include"TCHAR.h"
#include<iostream>
#include<string>
#include<windows.h>
using namespace std;
 
//设定对齐
#pragma pack(push,1) //强制编译器,使数据按字节边界对齐。 <br>                     //默认情况下VC6.0是按4字节对齐,VC7.0按8字节对齐 <br>                    //指令本不是按双字边界对齐的,所以必须使其按字节边界对齐,否则出错
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//定义一个结构体 表示thunk
struct Thunk
{
     BYTE op_movecx;  //
     DWORD_PTR val_ecx;
     BYTE op_call;
     DWORD_PTR val_address;
};
#pragma pack(pop)
 
typedef LRESULT (*pfaCallBack)( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
 
class CThunk
{
public :
     CThunk(){}; //构造函数
     ~CThunk(){}; //析构函数
     LRESULT MyProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); //类成员函数,我们将会让这个类成员函数作为窗口的窗口过程
public :
     void InitThunk(); //初始化函数
     pfaCallBack GetEntry()
     {
         return (pfaCallBack)&m_thunk;  
     }
private :
     Thunk m_thunk;
};

 thunk.cpp文件

复制代码
#include"thunk.h"
void CThunk::InitThunk()
{
    m_thunk.op_movecx=0xB9;// 0xb9 mov ecx,数值  机器码
    m_thunk.val_ecx=(DWORD_PTR)this;//给ecx寄存器赋值 将this赋值给ecx
    m_thunk.op_call=0xE9;//0xE9是Jmp 相对地址 的机器码
    DWORD_PTR off=0;
    _asm
    {
        mov eax,CThunk::MyProc
        mov DWORD PTR[off],eax
    }
    m_thunk.val_address=off-((DWORD_PTR)(&m_thunk.val_address)+sizeof(DWORD_PTR));//得到成员函数的地址
}
LRESULT CThunk::MyProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
    switch(uMsg)
    {
    case WM_CLOSE:
        DestroyWindow(hWnd);
        PostQuitMessage(NULL);
        break;
    case WM_CREATE:
        MessageBox(NULL,_T("WM_CREATE"),_T("WM_CREATE"),MB_OK);
        break;
    default:
        return DefWindowProc(hWnd,uMsg,wParam,lParam);
    }
    return 0;
}
复制代码

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

main.cpp文件

复制代码
#include"thunk.h"

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


int main()
{
    CThunk thunk;
    thunk.InitThunk();

    
    HINSTANCE hInstance;
    hInstance=(HINSTANCE)GetModuleHandle(NULL);
    MSG stMsg;
    TCHAR szClassName[]=_T("MCI");
    
    WNDCLASSEX stWC;
    ZeroMemory(&stWC,sizeof(stWC));
    stWC.hCursor=LoadCursor(NULL,IDC_ARROW);
    stWC.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
    stWC.cbSize=sizeof(stWC);
    stWC.style=CS_HREDRAW|CS_VREDRAW;
    //stWC.lpfnWndProc=DefWindowProc;
    stWC.lpfnWndProc=(WNDPROC)thunk.GetEntry(); //这里是关键
    stWC.lpszClassName=szClassName;
    stWC.cbClsExtra=0;
    stWC.hInstance=hInstance;

    RegisterClassEx(&stWC);
    HWND hWnd;
    hWnd=CreateWindowEx(WS_EX_CLIENTEDGE,szClassName,szClassName,WS_OVERLAPPEDWINDOW,100,100,600,700,NULL,NULL,hInstance,NULL);
    if(hWnd==NULL)
    {
        MessageBox(NULL,_T("WORNG"),_T("ERROR"),MB_OK);
    }
    ShowWindow(hWnd,SW_SHOWNORMAL);
    UpdateWindow(hWnd);
    while(GetMessage(&stMsg,NULL,0,0))
    {
        TranslateMessage(&stMsg);
        DispatchMessage(&stMsg);
    }
    return 0 ;    
}
复制代码

程序执行效果:

 

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

注意:

特别请注意:

typedef LRESULT (*pfaCallBack)(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam);这个的定义!

刚开始的时候,我是定义为

typedef LRESULT (CALLBACK *pfaCallBack)(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam);

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

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

CALLBACK 的定义为__stdcall 我们模拟的是__thiscall。所以出现了问题。

如qsort 等函数需要函数指针才能回调 用此函数库可以将成员函数指针转为普通函数指针 测试代码如下 #include <stdio.h> #include <algorithm> #include <vector> #include <string> #include <iostream> #include <math.h> using cmpfunc = int(__cdecl*)(const void*, const void*); using DebugArrayFunc = void(__stdcall *)(std::string &out;); #include "thunk.h" class MySort { public: int Rule; MySort(int a):Rule(a){} // 回调函数 template<typename T> int __cdecl sort(const void* a, const void* b); }; class Test { public: std::vector<int> mm; void Sort(int (*comp)(const void *,const void *)) { return qsort(mm._Myfirst,mm.size(),sizeof(int),comp); } void Entry(DebugArrayFunc func) { std::string string; cmpfunc comp; TemplateThunk athunk; // 正序 comp = (cmpfunc)athunk.GetCall(&MySort;::sort<int>, &MySort;(0)); Sort(comp); func(string); std::cout << string << std::endl; // 逆序 comp = (cmpfunc)athunk.GetCall(&MySort;::sort<int>, &MySort;(1)); Sort(comp); func(string); std::cout << string << std::endl; } }; class CallBack { public: std::vector<int> *pthis; CallBack(std::vector<int> *ff):pthis(ff){} void __stdcall DebugArray(std::string &out;) { char buff[100]; char *aa = buff; for each (auto a in *pthis) { aa += sprintf(aa, "%d ", a); } out.assign(buff); } }; void main() { TemplateThunk athunk; Test tt; tt.mm = { 1, 3, 7, 8, 5, 6, 4, 2, 3, 10 }; tt.Entry(athunk.GetCall(&CallBack;::DebugArray,&CallBack;(&tt;.mm))); } template <typename T> int __cdecl MySort::sort(const void* a, const void* b) { return Rule ? *static_cast<const T*>(a)-*static_cast<const T*>(b) : *static_cast<const T*>(b)-*static_cast<const T*>(a); }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值