傳統非 MFC Windows 程式習慣將窗口過程與消息處理函數寫為全局函數。為了實現面向對象封裝,本文提出一種通過動態生成機器碼(Hook 代碼塊)的技術,將窗口回調函數轉發至 C++ 類的虛擬成員函數,使每個窗口類擁有獨立的 WndProc 實現。
class CWndProc {
protected:
CWndProc(); virtual ~CWndProc();
virtual LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM) = 0;
private:
char m_hook[40]; // 機器碼存儲區
protected:
WNDPROC m_pfnWndProc; // 指向 Hook 代碼塊入口
};
在構造函數中:
m_hookthis 指針為當前對象地址WndProc 的相對偏移,修正 call 指令目標m_pfnWndProc 指向修改後的代碼塊機器碼模板功能:依調用約定從堆棧取出 hwnd, uMsg, wParam, lParam,壓入 this,調用派生類的 WndProc,最後 ret 10h 清理堆棧。
CWndProc::CWndProc() {
CopyMemory(m_hook, g_hook, sizeof(g_hook));
char* p = m_hook + 19; // 定位 mov ecx, 0
*((unsigned int*)p) = (unsigned int)this; // 替換為實際 this
LRESULT (CALLBACK CWndProc::*pfn)(HWND, UINT, WPARAM, LPARAM) = WndProc;
p = m_hook + 27; // 定位 call 指令
__asm {
mov eax, pfn; sub eax, 4; mov edi, p;
sub eax, edi; mov [edi], eax; // 計算相對偏移並回填
}
m_pfnWndProc = (WNDPROC)&m_hook[0];
}
派生類 CDialog 繼承 CWndProc,實現 WndProc 虛函數;進一步派生 CFormDlg、CInputDlg、CAboutDlg 等具體對話框類,皆可直接使用成員函數處理消息。示例工程在 Windows 2000 + VC++ 6.0 下編譯通過。
m_hook 所在內存頁面可執行(DEP 環境需調整)本文配套源碼包含完整實現與示例工程。