大家都知道,atl对话框有个开始接收消息的宏BEGIN_MSG_MAP(theClass),其实他是对ProcessWindowMessage函数的实现,也就是说对话框的消息都会通过ProcessWindowMessage函数处理,并通过MESSAGE_HANDLE等宏转发给相应的对象或函数。
又是哪里调用了这个ProcessWindowMessage函数呢?究起源,可以在atlwin.h文件的CDialogImplBaseT模板类中的DialogProc函数里面发现有这么一句程序:BOOL bRet = pThis->ProcessWindowMessage(pThis->m_hWnd, uMsg, wParam, lParam, lRes, 0);接着查找DialogProc函数的调用之处,不难发现,其实alt对话框的窗体过程被子类化到DialogProc函数中去了。
绕了一大圈该回到题目中的问题来了,我在跟踪DialogProc函数的时候发现里面有这么一句代码:CDialogImplBaseT< TBase >* pThis = (CDialogImplBaseT< TBase >*)hWnd;顿时我就迷惑了。然不成HAND句柄可以强制转换成对象的this指针?
我眼花了吗,这怎么可能?我再三地确认之,没错,它就是这么存在着,没有一丝凌乱,没有一点抱怨的存在着。于是,一股念想在我的脑海里油然而生:是什么东西能让它有如此大的勇气冒着易于让人误会的风险面不改色地屹立在这里。
废话还真多,闲话休题。其实alt对话框在create的时候调用了CreateDialogParam函数并把窗口过程设置给了StartDialogProc函数,又在StartDialogProc使用SetWindowLongPtr的方法把窗体过程指定给DialogProc函数。而在StartDialogProc函数里面还做了另一件事情,就是通过CWndProcThunk封装了一个_stdcallthunk的对象,_stdcallthunk对象把DialogProc函数的hWnd参数修改了。它把DialogProc中的hWnd参数的动态的修改为了pThis。_stdcallthunc代码如下:
struct _stdcallthunk
{
DWORD m_mov; // mov dword ptr [esp+0x4], pThis (esp+0x4 is hWnd)
DWORD m_this; //
BYTE m_jmp; // jmp WndProc
DWORD m_relproc; // relative jmp
BOOL Init(
_In_ DWORD_PTR proc,
_In_opt_ void* pThis)
{
m_mov = 0x042444C7; //C7 44 24 0C
m_this = PtrToUlong(pThis);
m_jmp = 0xe9;
m_relproc = DWORD((INT_PTR)proc - ((INT_PTR)this+sizeof(_stdcallthunk)));
// write block from data cache and
// flush from instruction cache
FlushInstructionCache(GetCurrentProcess(), this, sizeof(_stdcallthunk));
return TRUE;
}
//some thunks will dynamically allocate the memory for the code
void* GetCodeAddress()
{
return this;
}
_Ret_opt_bytecount_x_(sizeof(_stdcallthunk)) void* operator new(_In_ size_t)
{
return __AllocStdCallThunk();
}
void operator delete(_In_opt_ void* pThunk)
{
__FreeStdCallThunk(pThunk);
}
};
在StartDialogProc函数里面是这样调用的(m_thunk就是CWndProcThunk对象):
pThis->m_thunk.Init((WNDPROC)pThis->GetDialogProc(), pThis);
DLGPROC pProc = (DLGPROC)pThis->m_thunk.GetWNDPROC();
DLGPROC pOldProc = (DLGPROC)::SetWindowLongPtr(hWnd, DWLP_DLGPROC, (LONG_PTR)pProc);
所以可以在DialogProc函数里面可以强制把hWnd转换成对象的pThis指针。