For Windows APIs, callback functions are often required. In C ++ development, object-oriented functions are recommended. If a member function of the C ++ class can be called as a callback function, it would be great! However, all c ++ member functions implicitly use this pointer to point to the current object. It is really not easy to implement callback.
I have been familiar with thunk Technology about a year ago, and even have seen examples of using thunk to convert member functions into callback functions. However, I have never really understood what C ++ looks like after compilation. It's easy to get a tip, and I can't understand it. I don't dare to use their programs directly. After all, it's hard to handle errors. Some time ago, I think of thunk technology occasionally. Being unskillful, it is very likely that thunk will affect my programmer's career. So I decided to close my mind (no way, poor qualifications) and finally figured it out. That feeling is like a person who is honest and LIFO suddenly sees it, or changes to a metaphor that is close to himself: just as excited to see a beautiful girl for thousands of years.
I couldn't help but imitate the sighs of the man in the novel after he suddenly realized the greatest truths: It turns out like this!
Below I will share my gains, which are basically written to beginners. The hero must stop, and the younger brother is thin!
I have studied the code after C ++ assembly. Before calling C ++ member functions, the ECX register is used to save the object pointer, fortunately, the call conventions of C ++ member functions _ the parameter pressure stack sequence of thiscall and the maintenance of stack balance are the same as the call conventions of callback functions _ stdcall, therefore, you only need to construct an assembly to save the object pointer in the ECX register and then run JMP to the execution address of the member function. Write a C ++ structure to piece together the two Assembly codes:
# 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]
Usage:
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: Convert the function pointer to dword_ptr using the Union.
M_thunk.init (unioncasttype <dword_ptr> (& ctestclass: memfun), this );
Stdcallfun fun = (stdcallfun) m_thunk.getcodeaddress ();
Atlassert (fun! = NULL );
Fun (1, 3 );
}
};
The init method of memfuntostdcallthunk accepts the member function pointer and object pointer and constructs two Assembly codes. When fun () is called, the parameters 3 and 1 are first pushed into the stack, then jump to m_thunk, that is, the constructed two Assembly codes, save the object pointer to the ECX register, and then jump to the specified member function for execution. Everything is OK.
The code for the unioncasttype method is as follows:
Template <typename tdst, typename tsrc>
Tdst unioncasttype (tsrc SRC)
{
Union
{
Tdst udst;
Tsrc usrc;
} Umedia;
Umedia. usrc = SRC;
Return umedia. udst;
}