ATL中的Thunk機制學習 (轉)

amyz發表於2007-11-13
ATL中的Thunk機制學習 (轉)[@more@]

ATL利用一系列的類來管理視窗。為了使程式碼儘量緊湊而高效,ATL使用了一種有趣的技術來實現與視窗訊息相關聯的HWND和負責處理訊息的的this指標之間的對映。具體過程如下:

:namespace prefix = o ns = "urn:schemas--com::office" />

在視窗註冊時宣告的視窗過程為此視窗對應的視窗類的靜態成員StartWindowProc,當第一條訊息到達此函式時,其處理如下:

template

LRESULT CALLBACK CWindowImplBaseT< TBase, TWinTraits >::StartWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

  CWindowImplBaseT< TBase, TWinTraits >* pThis =

(CWindowImplBaseT< TBase, TWinTraits >*)_Module.ExtractCreateWndData();

  SERT(pThis != NULL);

  pThis->m_hWnd = hWnd;

  pThis->m_thunk.Init(pThis->GetWindowProc(), pThis);

  WNDPROC pProc = (WNDPROC)&(pThis->m_thunk.thunk);

  WNDPROC pOldProc = (WNDPROC)::SetWindowLong(hWnd, GWL_WNDPROC, (LONG)pProc);

#ifdef _DE

  // check if somebody has subclassed us already since we discard it

  if(pOldProc != StartWindowProc)

  ATLTRACE2(atlTraceWindowing, 0, _T("Subclassing through a hook discarded.n"));

#else

  pOldProc;  // avoid unused warning

#endif

  return pProc(hWnd, uMsg, wParam, lParam);

}

它先由全域性變數_Module中取得此視窗對應的視窗類的this指標,然後透過m_thunk運用指令改造此視窗類的視窗過程成員函式。m_thunk是CWndProcThunk的例項,每個視窗類各有一個。它的定義如下:

class CWndProcThunk

{

public:

  union

  {

  _AtlCreateWndData cd;

  _WndProcThunk thunk;

  };

  void Init(WNDPROC proc, void* pThis)

  {

#if defined (_M_IX86)

  thunk.m_mov = 0x042444C7;  //C7 44 24 0C

  thunk.m_this = (D)pThis;

  thunk.m_jmp = 0xe9;

  thunk.m_relproc = (int)proc - ((int)this+sizeof(_WndProcThunk));

#elif defined (_M_ALPHA)

  thunk.ldah_at = (0x279f0000 | HIWORD(proc)) + (LOWORD(proc)>>15);

  thunk.ldah_a0 = (0x261f0000 | HIWORD(pThis)) + (LOWORD(pThis)>>15);

  thunk.lda_at = 0x239c0000 | LOWORD(proc);

  thunk.lda_a0 = 0x22100000 | LOWORD(pThis);

  thunk.jmp = 0x6bfc0000;

#endif

  // write block from data cache and

  //  flush from instruction cache

  FlushInstructionCache(GetCurrentProcess(), &thunk, sizeof(thunk));

  }

};

其Init()函式完成對WndProcThunk結構的初始化工作。WndProcThunk結構針對X86體系的定義如下:

struct _WndProcThunk

{

  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

};

結構成員中儲存的是一組彙編指令。在X86體系下,在Init()函式中這組彙編指令被初始化為下面的指令:

mov dword ptr [esp+0x4], pThis

jmp (int)proc - ((int)this+sizeof(_WndProcThunk))

它完成的功能是,用視窗類的指標pThis代替視窗控制程式碼hWnd(esp+0x4中放的就是hWnd),然後跳轉到傳入的proc函式處((int)proc - ((int)this+sizeof(_WndProcThunk))是proc與thunk之間的距離)。

在完m_thunk.Init()函式之後,實際上就得到了經過改造的視窗過程(m_thunk.thunk),此視窗過程是視窗類的一個成員函式,它的第一個引數定義雖然是HWND,但實際上是它的類的this指標。最後使用SetWindowLong()用這個新的視窗過程替換掉原有的視窗過程(也就是StartWindowProc),以後的所有訊息都會被給新的視窗過程。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752019/viewspace-982219/,如需轉載,請註明出處,否則將追究法律責任。

相關文章