Introduction
Daniel Lohmann has a deep look at use member functions for C-style callbacks (see here). But he did still not deeply enough. there're some weak points:
- Those
EnumXXX
functions return after last callback finished. So this trick goes well. void SomeFunction()
{
m_nWindowCounter = 0;
adapter::CB2< A, LPCTSTR, BOOL, HWND, LPARAM >
adapter( this, &A::EnumWindowsCB1, "Hi all" );
::EnumWindows( adapter.Callback, (LPARAM) &adapter );
}
Adapter is constructed on stack. If EnumWindows
function returns, adapter is deconstructed then callback fails. Fortunately, EnumWindows
doesn't. If we call SetTimer
, it does return immediately. Our trick fails.
- We have to make a adapter for each caller function, like
win::EnumWindows
and its parameters are not same as the original one.
We need a more better and more generic way I said. Here is it.
Background - Some basic info
ATL windows bases on a thunk trick. It's stable and portable. How does it? This is the core struct
on x86 cpu.
#pragma pack(push,1)
struct _CallBackProcThunk
{
DWORD m_mov;
DWORD m_this;
BYTE m_jmp;
DWORD m_relproc;
};
#pragma pack(pop)
It's initialized by this function:
void _CallBackProcThunk::Init(DWORD_PTR proc, void* pThis)
{
m_mov = 0x042444C7;
m_this = PtrToUlong(pThis);
m_jmp = 0xe9;
m_relproc = DWORD((INT_PTR)proc - ((INT_PTR)this+sizeof(_stdcallthunk)));
FlushInstructionCache(GetCurrentProcess(), this, sizeof(_stdcallthunk));
}
In the comments, there is a small bug. I don't know why it exists from ATL 3.0 to 7.0. When we call some function pointed by proc, this struct
modified first parameter to pThis
. well, we can call our member function in that function with pThis
pointer. Here is an example:
class A{
_CallBackProcThunk thunk;
void Init(...){
thunk.Init((DWORD_PTR)StaticCallerProc, this);
SomeCallback(param1,..., (CALLBACK_TYPE)&thunk);
}
static void StaticCallerProc(HWND hWnd, ...){
A* pThis = (A*)hWnd;
pThis->MemberCallbackProc(mHWnd, ...);
}
void MemBerCallbackProc(HWND hWnd, ...){
}
};
Great hack way, is it? It EXECUTES a struct
! With this simple thunk trick, we can go farther.
What is it that we need?
Besides my example, I find four things we need to callback a member function: a class, a class member function to callback, a static wrap callback function and the important thunk.
How to put those things into my class? Inherit - I think it is always useful to do so.
template <CLASS class="" CallBackType MemCallBackType, Base,>
class CallBackAdapter{
typedef CallBackAdapter SelfType;
_CallBackProcThunk thunk;
void Init(CallBackType proc, SelfType* pThis)
{
thunk.m_mov = 0x042444C7;
thunk.m_this = (DWORD)pThis;
thunk.m_jmp = 0xe9;
thunk.m_relproc = (int)proc -
((int)this + sizeof(_CallBackProcThunk));
}
CallBackType _CallBackProcAddress(void){
return (CallBackType)&thunk;
}
MemCallBackType mTimerProc;
template <CLASS class="" Ret T5, T4, T3, , T2 T1,>
static Ret CALLBACK DefaultCallBackProc(
T1 p, T2 p2, T3 p3, T4 p4, T5 p5){
return (_ThisType(p)->*_MemberType(p))(0, p2, p3, p4, p5);
}
template <CLASS class="" Ret T4, T3, , T2 T1,>
static Ret CALLBACK DefaultCallBackProc( T1 p, T2 p2, T3 p3, T4 p4){
return (_ThisType(p)->*_MemberType(p))(0, p2, p3, p4);
}
template <CLASS class="" Ret T3, , T2 T1,>
static Ret CALLBACK DefaultCallBackProc( T1 p, T2 p2, T3 p3){
return (_ThisType(p)->*_MemberType(p))(0, p2, p3);
}
template <CLASS class="" Ret , T2 T1,>
static Ret CALLBACK DefaultCallBackProc( T1 p, T2 p2){
return (_ThisType(p)->*_MemberType(p))(0, p2);
}
public:
template <CLASS T>
static Base* _ThisType(T pThis){
return reinterpret_cast(pThis);
}
template <CLASS T>
static MemCallBackType _MemberType(T pThis){
return reinterpret_cast<SELFTYPE*>(pThis)->mTimerProc;
}
typedef MemCallBackType BaseMemCallBackType;
typedef CallBackType BaseCallBackType;
operator CallBackType(){
Init((CallBackType)&DefaultCallBackProc, this);
mTimerProc = &Base::CallbackProc;
return (CallBackType)&thunk;
}
CallBackType MakeCallback(MemCallBackType lpfn){
Init((CallBackType)&DefaultCallBackProc, this);
mTimerProc = lpfn;
return (CallBackType)&thunk;
}
};
#define TimerAdapter(Base) CallBackAdapter< Base, Base, \
void (Base:: * )( HWND , UINT , UINT , DWORD ), \
void (CALLBACK *)( HWND , UINT , UINT , DWORD )>
struct Test : TimerAdapter(Test){
bool mQuit;
void TimerProc2(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime ){
mQuit = true;
KillTimer(NULL, idEvent);
printf("good! %d\n", idEvent);
}
};
int main(void){
Test a;
a.mQuit = false;
SetTimer(NULL, 0, 100, a.MakeCallback(&Test::TimerProc2));
MSG msg;
while(!a.mQuit && GetMessage(&msg, 0, 0, 0) ){
printf("before dispatch!\n");
DispatchMessage(&msg);
}
return 0;
}
It's a generic way. With the operator CallBackType()
you can call SetTimer(NULL, 0, 100, a)
if you defined a member function named CallbackProc
This is my first thought. It did work, but only on G++ 3.3.1. VC++ can't handle some template usage. I had to modified it with ugly adapter like this:
template <class Base>
class TimerAdapter : public CallBackAdapter<
Base,
TimerAdapter<Base>,
void (Base:: * )( HWND , UINT , UINT , DWORD ),
void (CALLBACK *)( HWND , UINT , UINT , DWORD )
>
{
public:
typedef typename TimerAdapter>Base<::BaseMemCallBackType MemCallBackType;
UINT_PTR SetTimer(UINT uElapse, MemCallBackType lpTimerFunc){
return ::SetTimer(NULL, 0, uElapse, MakeCallBackProc(lpTimerFunc));
}
BOOL KillTimer(UINT_PTR uIDEvent){
return ::KillTimer(NULL, uIDEvent);
}
static void CALLBACK DefaultCallBackProc( HWND hwnd,
UINT uMsg, UINT idEvent, DWORD dwTime ){
(_ThisType(hwnd)->*_MemberType(hwnd))(0, uMsg, idEvent, dwTime);
}
};
template <class Base>
struct RasDailAdapter : public CallBackAdapter<
Base,
RasDailAdapter<Base>,
void (Base:: * )(UINT unMsg, RASCONNSTATE rasconnstate,DWORD dwError),
void (CALLBACK *)(UINT unMsg, RASCONNSTATE rasconnstate,DWORD dwError)
>
{
static void CALLBACK CallBackProc(UINT unMsg,
RASCONNSTATE rasconnstate,DWORD dwError){
(_ThisType(hwnd)->*_MemberType(hwnd))(WM_RASDIALEVENT,
rasconnstate, dwError);
}
};
struct Test : TimerAdapter<Test>{
bool mQuit;
void TimerProc2(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime ){
mQuit = true;
KillTimer(idEvent);
printf("good! %d\n", idEvent);
}
};
int main(void){
Test a;
a.mQuit = false;
a.SetTimer(100, &Test::TimerProc2)
MSG msg;
while(!a.mQuit && GetMessage(&msg, 0, 0, 0) ){
printf("before dispatch!\n");
DispatchMessage(&msg);
}
return 0;
}
Caution! This CallBackAdapter
has four template parameters, not three as above. In the Source package, I made them together peacefully, you can choose both ways!
Defect of my technique
We want prefect things, but things go faulty. There are some weak points for my way.
- If a class callback twice for different member function, I can't help it. You need careful code.
- Some compilers can't compile it.
- The implementation only works on x86 platform.
- The biggest one, we lost a parameter! But we almost do NOT need it or our class knows it before callback.
- More Complex than Daniel Lohmann's way
History