{ return 0; } pThis->m_hWnd = hWnd; // Initialize the thunk. This is allocated in CWindowImplBaseT::Create, // so failure is unexpected here. pThis->m_thunk.Init(pThis->GetWindowProc(), pThis); WNDPROC pProc = pThis->m_thunk.GetWNDPROC(); WNDPROC pOldProc = (WNDPROC)::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)pProc);#ifdef _DEBUG // check if somebody has subclassed us already since we discard it if(pOldProc != StartWindowProc) ATLTRACE(atlTraceWindowing, 0, _T("Subclassing through a hook discarded.\n"));#else (pOldProc); // avoid unused warning#endif return pProc(hWnd, uMsg, wParam, lParam);} |
函数首先通过_AtlWinModule.ExtractCreateWndData()获取到当前正在创建的窗口的CWindowImpl对象的this指针,
实际上,在_AtlWinModule中保存了一个链表,用于保存当前正在创建窗口的窗口对象的指针,当调用CWindowImpl的
Create方法时将该对象的指针到链表中,在StartWindowProc时取出,这个链表是根据ThreadID进行关联的,所以可以
保证在多个线程创建窗口是,可以在StartWindowProc取到正确的this指针。
取到对象的this指针后,首先对其m_hWnd赋值,然后初始化该对象的thunk成员(用于实现窗口和窗口对象关联的关键
对象)。该结构定义如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
class CWndProcThunk{public: _AtlCreateWndData cd; CStdCallThunk thunk; BOOL Init(WNDPROC proc, void* pThis) { return thunk.Init((DWORD_PTR)proc, pThis); } WNDPROC GetWNDPROC() { return (WNDPROC)thunk.GetCodeAddress(); }};struct _AtlCreateWndData{ void* m_pThis; DWORD m_dwThreadID; _AtlCreateWndData* m_pNext;};#if defined(_M_IX86) || defined (_M_AMD64) #pragma pack(push,8)class CDynamicStdCallThunk{public: _stdcallthunk *pThunk; CDynamicStdCallThunk() { pThunk = NULL; } ~CDynamicStdCallThunk() { if (pThunk) { delete pThunk; } } BOOL Init(DWORD_PTR proc, void *pThis) { if (pThunk == NULL) { pThunk = new _stdcallthunk; if (pThunk == NULL) { return FALSE; } } return pThunk->Init(proc, pThis); } void* GetCodeAddress() { return pThunk->GetCodeAddress(); }};#pragma pack(pop)typedef CDynamicStdCallThunk CStdCallThunk;#elsetypedef _stdcallthunk CStdCallThunk;#endif |
CWndProcThunk中的成员cd就是上文所述的_AtlWinModule.ExtractCreateWndData()中保存的创建信息,成员thunk用于窗口和窗口对象关联。定义如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
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(DWORD_PTR proc, 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; } void* operator new(size_t) { return __AllocStdCallThunk(); } void operator delete(void* pThunk) { __FreeStdCallThunk(pThunk); }}; |
该结构实际上是一段代码,用于替换真正的窗口过程,该代码被StartWindowProc通过SetWindowLongPtr设置为窗口的窗口过程,由于系统调用窗口过程是采用的是stdcall,
所以会将窗口过程的参数从逆序压栈,窗口过程的原型如下:
|
1
|
typedef LRESULT (CALLBACK* WNDPROC)(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) |
所以在调用m_thunk时栈内容如下:
[esp + 00]-->| |
[esp + 04]-->| HWND |
[esp + 08]-->|message|
[esp + 0C]-->|wParam |
[esp + 10]-->|lParam |
thunk中的代码等效于
mov dword ptr [esp+0x4], pThis
jmp WndProc
实际上就是把栈上hwnd参数修改为窗口对象的this指针,并跳转到窗口对象的WindowProc函数
(默认为CWindowImplBaseT< TBase, TWinTraits >::WindowProc,是个静态函数)。
而thunk的成员pThis和m_realproc是在StartWindowProc中初始化化的。
这样在下次系统调用窗口过程时,就会支持thunk的代码,并跳转到指定的WindowProc函数中,
在WindowProc中会将hwnd转化为this指针,并调用对应的对象进行消息处理。
CWindowImplBaseT< TBase, TWinTraits >::WindowProc定义如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
template <class TBase, class TWinTraits>LRESULT CALLBACK CWindowImplBaseT< TBase, TWinTraits >::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){ CWindowImplBaseT< TBase, TWinTraits >* pThis = (CWindowImplBaseT< TBase, TWinTraits >*)hWnd; // set a ptr to this message and save the old value _ATL_MSG msg(pThis->m_hWnd, uMsg, wParam, lParam); const _ATL_MSG* pOldMsg = pThis->m_pCurrentMsg; pThis->m_pCurrentMsg = &msg; // pass to the message map to process LRESULT lRes; BOOL bRet = pThis->ProcessWindowMessage(pThis->m_hWnd, uMsg, wParam, lParam, lRes, 0); // restore saved value for the current message ATLASSERT(pThis->m_pCurrentMsg == &msg); // do the default processing if message was not handled if(!bRet) { if(uMsg != WM_NCDESTROY) lRes = pThis->DefWindowProc(uMsg, wParam, lParam); else { // unsubclass, if needed LONG_PTR pfnWndProc = ::GetWindowLongPtr(pThis->m_hWnd, GWLP_WNDPROC); lRes = pThis->DefWindowProc(uMsg, wParam, lParam); if(pThis->m_pfnSuperWindowProc != ::DefWindowProc && ::GetWindowLongPtr(pThis->m_hWnd, GWLP_WNDPROC) == pfnWndProc) ::SetWindowLongPtr(pThis->m_hWnd, GWLP_WNDPROC, (LONG_PTR)pThis->m_pfnSuperWindowProc); // mark window as destryed pThis->m_dwState |= WINSTATE_DESTROYED; } } if((pThis->m_dwState & WINSTATE_DESTROYED) && pOldMsg== NULL) { // clear out window handle HWND hWndThis = pThis->m_hWnd; pThis->m_hWnd = NULL; pThis->m_dwState &= ~WINSTATE_DESTROYED; // clean up after window is destroyed pThis->m_pCurrentMsg = pOldMsg; pThis->OnFinalMessage(hWndThis); }else { pThis->m_pCurrentMsg = pOldMsg; } return lRes;} |
浙公网安备 33010602011771号