如何在 WNDPROC中如何得到 窗口类的this指针:
1. 第一种方法: Map<HWND, 窗口类指针>,结构简单明了,但系统每次调用WNDPROC时都需要查询map速度慢,还牵扯到线程安全问题。
2. 第二种方法:使用SetWindowLong将this指针地址写入USERDATA中,需要时调用GetWindowLong取得即可。缺点是,不小心重写了 USERDATA,程序运行会崩溃。
3. 第三种方法:使用Thunk方法,MFC/ATL/WTL 使用的该方法。简单讲是使用汇编代码,修改 WNDPROC的第一个参数,将 hwnd修改成当前类的this地址。当窗口创建完成后,hwnd会保存到类的m_hWnd中,WNDPROC函数的hWnd可以使用m_hWnd代替了。
3.1 WNDPROC回调函数:
LRESULT (CALLBACK *WNDPROC)(HWND,UINT,WPARAM,LPARAM);
3.2 定义结构体StdCallThunk:
#pragma pack(push,1)
typedef struct _StdCallThunk
{
DWORD m_mov; // = 0x042444C7
DWORD m_this; // = this
BYTE m_jmp; // = 0xe9
DWORD m_relproc; // = relative distance
} StdCallThunk;
#pragma pack(pop)
3.3 声明Thunk:
StdCallThunk *m_pStdthunk;
3.4 分配内存:使用VirtualAlloc分配,VirtualFree释放,修改程序运行代码是不安全行为。
m_pStdthunk = (StdCallThunk *)VirtualAlloc(NULL, sizeof(StdCallThunk),
MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
3.5 修改hwnd成this地址:
m_pStdthunk->m_mov = 0x042444c7; // mov dword ptr [esp+0x4]
m_pStdthunk->m_jmp = 0xe9; // jmp
m_pStdthunk->m_this = (DWORD)this; // this
m_pStdthunk->m_relproc = (DWORD)StaticWndProc - ((DWORD)m_pStdthunk + sizeof(StdCallThunk)); //relproc
SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)m_pStdthunk);
3.6 释放内存:
VirtualFree(m_pStdthunk, sizeof(StdCallThunk), MEM_RELEASE);
3.7 具体实现参考下面两篇文章