Windows API often requires callback functions, and in the C + + development of object-oriented when the way, if you can make C + + class member functions become callback function, is simply great goodness! But C + + member functions implicitly imply that a this pointer is used to point to the current object. It's really not easy to implement callbacks.
I've been exposed to thunk technology about a year ago, and I've even seen examples of using thunk implementations to turn member functions into callback functions. But I really did not understand the appearance of C + + compiled, it is easy to drill the NIU Jiao Jian, see do not understand, direct use of their procedures and dare not, after all, bad after the error processing. Front-end time occasionally think of thunk technology, not understand the technology of the old hanging is likely to affect their programmer career, so determined to retreat ponder (no way, poor qualifications AH), finally understand. That feeling ah, just like the people of good faith Buddha suddenly see the same, or change the analogy to their own: just like the Millennium Goat to see beautiful women as excited. I can't help but imitate the true personage in the novel to realize the Sigh after the Enlightenment: so!
The following share of my harvest, is basically access to the entrance to beginners, the warrior must stop, the younger brother skin thin!
After a little study of C + + compiled code, the general call to C + + member functions, are using the ECX register to save the object's pointer, in the C + + member function of the calling convention __thiscall parameter stack order and stack balance maintenance are and callback function of the calling convention __ StdCall, so you just need to construct the assembler to save the object pointer to the ECX register JMP to the member function's execution address. First of all, write a C + + structure to cobble together the two-line code:
#pragma pack( push, 1 )
struct MemFunToStdCallThunk
{
BYTE m_mov;
DWORD m_this;
BYTE m_jmp;
DWORD m_relproc;
BOOL Init( DWORD_PTR proc, void* pThis )
{
m_mov = 0xB9;
m_this = PtrToUlong(pThis);
m_jmp = 0xe9;
m_relproc = DWORD((INT_PTR)proc - ((INT_PTR)this+sizeof(MemFunToStdCallThunk)));
::FlushInstructionCache( ::GetCurrentProcess(), this, sizeof(MemFunToStdCallThunk) );
return TRUE;
}
void* GetCodeAddress()
{
return this;
}
};
#pragma pack( pop )
This structure is equivalent to two assembly statements:
mov ecx, pThis
JMP [offset Address]
Use:
class CTestClass
{
private:
int m_nBase;
MemFunToStdCallThunk m_thunk;
void memFun( int m, int n )
{
int nSun = m_nBase + m + n;
CString str;
str.Format( _T("%d"), nSun );
AtlMessageBox( NULL, _U_STRINGorID( str ) );
}
public:
CTestClass()
{
m_nBase = 10;
}
void Test()
{
//UnionCastType:利用联合将函数指针转换成DWORD_PTR
m_thunk.Init( UnionCastType<DWORD_PTR>(&CTestClass::memFun), this );
StdCallFun fun = (StdCallFun)m_thunk.GetCodeAddress();
ATLASSERT( fun != NULL );
fun( 1, 3 );
}
};