在SOUI中,所有界面最基本的类型就是SHostWnd对象。如果我们获取到一人SHostWnd的hwnd值,如何将它转换成SHostWnd*?
SOUI使用了thunk技术, 在窗口关联的WindowProc数据中保存有this指针,可以参考下面代码:
[C++] 纯文本查看 复制代码 LRESULT CALLBACK SNativeWnd::StartWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
SNativeWnd *pThis = (SNativeWnd *)SNativeWndHelper::getSingletonPtr()->GetSharePtr();
pThis->m_hWnd = hWnd;
// 初始化Thunk,做了两件事:1、mov指令替换hWnd为对象指针,2、jump指令跳转到WindowProc
pThis->m_pThunk->Init((DWORD_PTR)WindowProc, pThis);
// 得到Thunk指针
WNDPROC pProc = (WNDPROC)pThis->m_pThunk->GetCodeAddress();
// 调用下面的语句后,以后消息来了,都由pProc处理
::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)pProc);
return pProc(hWnd, uMsg, wParam, lParam);
}
从上面代码可以发现,GWLP_WNDPROC保存了和pThis相关的thunk对象地址。
查看GetCodeAddress的代码可以发现,它实际上就是pThis->m_pThunk的地址。
因此可以通过GWLP_WNDPROC获取到与该hwnd对应的m_pThunk值。
由于pThis在pThis->m_pThunk->Init((DWORD_PTR)WindowProc, pThis)中被保存到了tagThunk的m_this数据中,因此获取到pThis->m_pThunk即可以获取到pThis的值。参考下面x86的tagThunk的代码。
[C++] 纯文本查看 复制代码 #pragma pack(push, 1)
struct tagThunk
{
DWORD m_mov; // 4个字节
DWORD m_this;
BYTE m_jmp;
DWORD m_relproc;
//关键代码 //////////////////////////////////////
void Init(DWORD_PTR proc, void *pThis)
{
m_mov = 0x042444C7;
m_this = (DWORD)(
ULONG_PTR)pThis; // mov [esp+4], pThis;而esp+4本来是放hWnd,现在被偷着放对象指针了.
m_jmp = 0xe9;
// 跳转到proc指定的入口函数
m_relproc = (DWORD)((INT_PTR)proc - ((INT_PTR)this + sizeof(tagThunk)));
// 告诉CPU把以上四条语句不当数据,当指令,接下来用GetCodeAddress获得的指针就会运行此指令
FlushInstructionCache(GetCurrentProcess(), this, sizeof(tagThunk));
}
void *GetCodeAddress()
{
return this; // 指向this,那么由GetCodeAddress获得的函数pProc是从DWORD m_mov;开始执行的
}
};
因此通过HWND要获取SHostWnd*,可以使用下面代码:
[C++] 纯文本查看 复制代码 #pragma pack(push, 1)
SHostWnd * Hwnd2HostWnd(HWND hWnd){
WNDPROC proc = (WNDPROC)::GetWindowLongPtr(m_hWnd,GWL_WNDPROC);
tagThunk *thunk = (tagThunk*)proc;
#if defined(_M_IX86)
SNativeWnd *pThis=(SNativeWnd *)thunk->m_this;
#elif defined(_M_AMD64)
SNativeWnd *pThis=(SNativeWnd *)thunk->RcxImm;
#else
return NULL;
#endif
SHostWnd *pHostWnd =(SHostWnd*)pThis;
return pHostWnd;
}
|