{
        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;
#else
typedef _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;
}
posted on 2014-10-21 16:18  归海一刀  阅读(551)  评论(0)    收藏  举报