C++成员函数回调的一种实现

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

Leave a Reply

Your email address will not be published. Required fields are marked *