成员函数回调即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++成员函数回调的一种实现”