Pass Class Method as Static function to API Calls






1.86/5 (4 votes)
Mar 13, 2002

81450
Pass Class Methods to API Calls where Static Functions are required using CThunk class
Introduction
A number of times we need to define static functions in a class to pass them to API Calls like Window Proc, Thread Proc, etc.
Typically we define a static/global function and pass this
as one parameter and recast that parameter into object again.
In these cases, we can define a thunk that generates the Static Procedure at
runtime, and pass the generated Proc to the API Function.
All we need to set this
correctly at runtime and jump to required address.
Code listing
The following class CThunk
does the same thing.
/* Thunk Class Parameters: F1: Static Proc F2: Class function T1 : Class // Function Types F1 & F2 should have same parameters // and return type otherwise stack corruption will occur */ #if defined(_M_IX86) #pragma pack(push,1) template <typename F1,typename F2, typename T1> class CThunk { public: struct _ProcThunk { BYTE m_movcx; DWORD m_pThis; BYTE m_jmp; // jmp WndProc DWORD m_relproc; // relative jmp }; _ProcThunk thunk; void Init(F2 proc, T1 * pClass) { //make sure that 'this' is in ecx pointer // otherwsie code does not work #ifdef _DEBUG long pthis; _asm { mov dword ptr [pthis],ecx }; assert(pthis == (long) this); #endif // put values thunk.m_movcx = 0xB9; //B9 thunk.m_pThis= (DWORD)pClass; thunk.m_jmp = 0xe9; thunk.m_relproc = *((int *) &proc) - ((int)this+sizeof(_ProcThunk)); // write block from data cache and // flush from instruction cache FlushInstructionCache(GetCurrentProcess(), &thunk, sizeof(thunk)); } F1 GetStaticProc() { return reinterpret_cast<F1> (&thunk); } }; #pragma pack(pop) #else #error Only X86 supported #endif
Usage
The following code shows the usage of CThunk
class for threads Proc.
This class can be used in other places where Static procs are required. In this
code CThunk
class is used to create a thunk for the _beginthread
API Call.
//Test Thread class typedef void( __cdecl *THREADPROC )( void * ); class CThreadTest { protected: typedef LRESULT (CThreadTest::*FTN) (LPVOID lpThreadParameter); // THREADPROC & FTN have same signature CThunk<THREADPROC,FTN,CThreadTest> m_th; HANDLE m_hThread; public: //data int m_y; CThreadTest() { //Intialize the Thunk m_th.Init(CThreadTest::ThreadFunction,this); } LRESULT ThreadFunction(LPVOID lpThreadParameter) { int ntime =(int) lpThreadParameter; printf("%d",m_y); // this pointer is set correctly Sleep(ntime); return 0; } BOOL Create (int nSleeptime) { //Get Static Proc THREADPROC tp =m_th.GetStaticProc(); // pass proc to _beginthread m_hThread = (HANDLE)_beginthread(tp, 0, (LPVOID)nSleeptime); return TRUE; } void WaitForThread() { WaitForSingleObject(m_hThread,INFINITE); } }; int main(int argc, char* argv[]) { CThreadTest t1; t1.m_y=100; t1.Create(1000); t1.WaitForThread(); return 0; }