成员函数回调即C语言函数(如Windows API)调用C++类成员函数,相当于C#中的委托。
实现这个功能有两个难点,一是C语言中不允许从成员函数指针到万能指针void*的类型强转,这个可以通过union来绕过。
第二是从C调用带this指针的C++要求每个函数C函数指针能够保存不同的this指针。这个必须通过动态代码技术实现。
这两个问题记得都困扰了很久,今天终于写出一种方案,能够简单地将stdcall调用约定的C函数转到thiscall调用约定的C++函数,这样便能实现WindowProc直接窗口子类化到类成员函数中,而不需要再通过窗口句柄hash表来查找窗口类指针。动态代码也很简单,因为stdcall与thiscall对堆栈处理相同,故只需要向ecx中填写this指针然后简单地jump到成员函数的代码地址就可以。
static HANDLE xdl_exec_heap_handle = NULL;
void* xdl_thiscall_adapter(void *obj, void *fn)
{
if (xdl_exec_heap_handle == NULL)
xdl_exec_heap_handle = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0);
unsigned char *buf = (unsigned char*)HeapAlloc(xdl_exec_heap_handle, 0, 10);
buf[0] = 0xB9; //MOV ECX
*(unsigned int*)&buf[1] = (unsigned int)obj;
buf[5] = 0xE9; //JMP
*(unsigned int*)&buf[6] = (unsigned int)fn - ((unsigned int)buf + 10);
return (void*)buf;
}
void xdl_thiscall_adapter_delete(void *p)
{
HeapFree(xdl_exec_heap_handle, 0, p);
}
template<class _Out, class _In>
__inline _Out xdl_force_cast(_In value)
{
union
{
_In in;
_Out out;
} u = {0};
u.in = value;
return u.out;
}
template<class _Obj, class _Fn>
__inline LPVOID xdl_thiscall_adapter(_Obj* obj, _Fn fn)
{
return xdl_thiscall_adapter((void*)obj, xdl_force_cast<void*>(fn));
}
One Reply to “C++成员函数回调的一种实现”