怎么将c++的成员函数转为静态函数?

前几天读了一位网友的文章《如何让API回调你的VC类成员函数而不是静态函数》。写得很好,到底是高手!可惜也因为是高手,很多细节都略过不谈了(高手嘛,你知道的,懒得说细节了:),所以我决定写这篇小文作为原文的补充。另外,本文写了一个独立的例子,示范了怎么将类成员函数转为消息函数传给windows。例子文件的链接: testwin

一共有4个补充:

1.成员函数调用与普通函数调用有什么区别?

区别是:调用成员函数时,ecx寄存器保存的是this指针。

2.关键代码段:

抛开乱七八糟的封装,让我把关键代码段展示给大家。整篇文章的核心技术实际上就是下面两个关键代码段。

1)代码段1:
代码段1包含了一条指令和两个指针。代码段对应着结构体_ACCallbackOpCodes。

在本文的例子中
wcex.lpfnWndProc = cb;//就是窗口消息函数的地址
实际上保存的是指向“代码段1”的地址。

1)call 偏移量 //“偏移量”指向的是代码段2
2)LONG_PTR _this;//这个指针里保存了this指针
3)LONG_PTR _func;//这个指针里保存了成员函数的指针

2)代码段2:
代码段2包含了4条指令。代码段2对应着STDACJMPProc函数。
1)POP ECX
//将栈里保存的this指针的偏移量pop到ecx里去
//你可能会觉得奇怪:this指针怎么会跑到栈里去了?
//呵呵,怪自己汇编太差吧:P
//这是因为call指令会把它的下一条指令压入栈呀!
//这也是为什么要把this指针放在call指令后面的原因。
//对于call的详细信息请参考汇编语言手册吧。
2)MOV EAX, DWORD PTR [ECX + 4] //成员函数的指针存入eax
3)MOV ECX, [ECX] //this指针存入ecx
4)JMP EAX //跳转到成员函数的起始地址!大功告成!

3.为什么要用VirtualAlloc函数?
先 举一个反例:如果在栈上分配一块内存,保存_ACCallbackOpCodes,那么就需要调用FlushInstructionCache函数,因为 _ACCallbackOpCodes需要动态更新某些参数,而cpu不会自动刷新这块内存(这样就必须调用FlushInstructionCache 来刷新)。但是如果用VirtualAlloc就不会有这个问题。

4.在CalcJmpOffset中,为什么要计算STDACJMPProc(也就是“代码段2”)相对于_ACCallbackOpCodes的偏移量?
我们知道_ACCallbackOpCodes的第一条指令是:
call STDACJMPProc(也就是“代码段2”);
但 是因为_ACCallbackOpCodes与“代码段2”不在同一个段,在执行call指令时,cs寄存器指向的是 _ACCallbackOpCodes所在段的段基址,所以想要跳转到“代码段2”上,就必须计算“代码段2”相对于 _ACCallbackOpCodes所在段的段基址的偏移量(这个段基址就是_ACCallbackOpCodes的首地址)。