VC++类成员函数委托(第一版)

前置博文:

2010.10.3:《有关C++类成员函数的指针调用

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

本研究为XDL项目(Xiaofei Development Library)的一部分。

 

实现基于X86动态代码的函数委托,并提供基于模板类的代码中显式调用。

目前实现的功能:

1、除pascal外任何调用约定的C++类成员函数通过cdecl调用约定的C函数指针进行调用的适配器。

2、thiscall调用约定的C++类成员函数通过stdcall调用约定的C函数指针进行调用的适配器。

其中1多用于代码中调用的委托,2多用于系统API传入回调函数(如WndProc、ThreadProc)。因为目前仅做到了通过RTTI识别函数指针类型的调用约定,而未做到识别参数的个数与长度,如果要做到识别参数,RTTI的parser庞大到一个小型编译器的地步,并且我对文法分析已经忘的差不多了。所以对于以上两点之外的调用约定转换考虑到用到的机会几乎为零,暂时舍弃。

使用方法举例:

class Worker
{
public:
	void ThreadProc(void *param)
	{
		return;
	}
};

int main()
{
	Worker worker;
	Delegate<void(*)(void*)> callback(&Worker::ThreadProc, &worker);
	callback((void*)12345);
	_beginthread(callback, 0, (void*)12345);
	for(;;);
	return 0;
}

主要实现代码:

static HANDLE xdl_exec_heap_handle = NULL;

void* xdl_exec_heap_malloc(unsigned int size)
{
	if (xdl_exec_heap_handle == NULL)
		xdl_exec_heap_handle = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0);
	return HeapAlloc(xdl_exec_heap_handle, 0, size);
}

void xdl_exec_heap_free(void *p)
{
	HeapFree(xdl_exec_heap_handle, 0, p);
}

static char xdl_fn_adapter_get_callconv(const char *type_raw_name)
{
	char *p = (char*)type_raw_name;
	if (*p++ == '.' && *p++ == 'P')
	{
		if (*p == '6')
			return *++p;
		else if (*p++ == '8')
		{
			for ( ; !(*p == '@' && *(p + 1) == '@'); ++p);
			return *(p + 3);
		}
	}
	return -1;
}

void* xdl_fn_adapter(const void* fn, const void *obj, const type_info *_In_Type, const type_info *_Out_Type)
{
	int size = 0;
	char *buf = NULL;
	int pos = 0;
	if (obj == NULL)	//c function
	{
		size = 5;
		buf = (char*)xdl_exec_heap_malloc(size);
		buf[pos++] = '\xe9';	//jmp
		*(int*)&buf[pos] = (int)fn - ((int)&buf[pos] + 4); pos += 4;
	}
	else	//c++ member function
	{
		char in_callconv = xdl_fn_adapter_get_callconv(_In_Type->raw_name());
		char out_callconv = xdl_fn_adapter_get_callconv(_Out_Type->raw_name());
		if (out_callconv == 'A')	// cdecl
		{
			size = 45;
			buf = (char*)xdl_exec_heap_malloc(size);
			*(int*)&buf[pos] = '\x55\x8b\xec\x81';	//push ebp; mov ebp,esp; sub esp,100
			*(int*)&buf[pos + 4] = '\xec\x00\x01\x00';
			*(int*)&buf[pos + 8] = '\x00\x56\x8d\x75';	//push esi; lea esi,[ebp+8]
			*(int*)&buf[pos + 12] = '\x08\x57\x8d\xbd';	//push edi; lea edi,[ebp-100]
			*(int*)&buf[pos + 16] = '\x00\xff\xff\xff';
			*(int*)&buf[pos + 20] = '\xb9\x40\x00\x00';	//mov ecx,40
			*(int*)&buf[pos + 24] = '\x00\xf3\xa5\x5f';	//rep movsd; pop edi
			*(short*)&buf[pos + 28] = '\x5e\x68'; pos += 30;	//pop esi; push
			*(int*)&buf[pos] = (int)obj; pos += 4;
			if (in_callconv == 'E' || in_callconv == 'I')		//thiscall or fastcall
				buf[pos++] = '\x59';	//pop ecx
			if (in_callconv == 'I')
				buf[pos++] = '\x5a';	//pop edx
			buf[pos++] = '\xe8';	//call
			*(int*)&buf[pos] = (int)fn - ((int)&buf[pos] + 4);	pos += 4;
			*(int*)&buf[pos] = '\x8b\xe5\x5d\xc3'; pos += 4;	//mov esp,ebp; pop ebp; retn
		}
		else if (in_callconv == 'E' && out_callconv == 'G')	//thiscall to stdcall
		{
			size = 10;
			buf = (char*)xdl_exec_heap_malloc(size);
			buf[pos++] = '\xb9';	//MOV ECX
			*(int*)&buf[pos] = (int)obj; pos += 4;
			buf[pos++] = '\xe9';	//JMP
			*(int*)&buf[pos] = (int)fn - ((int)&buf[pos] + 4);	pos += 4;	
		}
	}
	return buf;
}

void xdl_fn_adapter_free(void *p)
{
	xdl_exec_heap_free(p);
}

template<typename _CFn>
struct Delegate
{
public:
	Delegate()
	{
		m_obj = NULL;
		m_fn_in = NULL;
		m_fn_out = NULL;
		m_type_in = NULL;
	}
	Delegate(const Delegate& d)
	{
		m_obj = d.m_obj;
		m_fn_in = d.m_fn_in;
		m_fn_out = NULL;
		m_type_in = d.m_type_in;
	}
	~Delegate()
	{
		if (m_fn_out)
			xdl_fn_adapter_free(m_fn_out);
	}
	Delegate(_CFn fn)
	{
		new(this) Delegate();
		bind(fn);
	}
	template<class _Fn>
	Delegate(_Fn fn, void* obj)
	{
		new(this) Delegate();
		bind(fn, obj);
	}

	void bind(_CFn fn)
	{
		m_fn_in = fn;
	}

	template<class _Fn>
	void bind(_Fn fn, void *obj)
	{
		m_obj = obj;
		m_fn_in = xdl_force_cast<void*>(fn);
		m_type_in = &typeid(_Fn);
	}

	operator _CFn()
	{
		if (!m_fn_out)
		{
			m_fn_out = xdl_fn_adapter(m_fn_in, m_obj, m_type_in, &typeid(_CFn));
		}
		assert(m_fn_out);
		return (_CFn)m_fn_out;
	}
private:
	const void* m_obj;
	const void* m_fn_in;
	void* m_fn_out;
	const type_info *m_type_in;
};

Leave a Reply

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