存储此指针以在WndProc中使用的最佳方法
我想知道最好的/通用的方式来存储this
指针供WndProc
使用。我知道几种方法,但据我所知每种方法都有其自身的缺点。我的问题是:
产生这种代码有什么不同的方式:
CWindow::WndProc(UINT msg, WPARAM wParam, LPARAM) { this->DoSomething(); }
我可以想到Thunks,HashMaps,线程本地存储和Window User Data结构。
每种方法的利弊是什么?
代码示例和建议的得分。
这纯粹是出于好奇。使用MFC之后,我一直想知道它是如何工作的,然后再考虑ATL等。
编辑:最早可以在窗口proc中有效使用HWND
的地方是什么?它被记录为" WM_NCCREATE",但是如果我们实际进行了试验,那并不是发送到窗口的第一条消息。
编辑:ATL使用thunk来访问此指针。 MFC使用HWND
的哈希表查找。
解决方案
我使用SetProp / GetProp来存储窗口本身的数据指针。我不确定它如何与我们提到的其他项目堆叠在一起。
我们应该使用GetWindowLongPtr()
/SetWindowLongPtr()
(或者不建议使用的GetWindowLong()
/SetWindowLong()
)。它们速度很快,并且完全可以完成我们想做的事情。唯一棘手的部分是弄清楚何时调用SetWindowLongPtr()
。我们需要在发送第一条窗口消息即" WM_NCCREATE"时执行此操作。
有关示例代码和更深入的讨论,请参见本文。
线程本地存储是个坏主意,因为我们可能在一个线程中运行多个窗口。
哈希映射也可以工作,但是为每个窗口消息(并且有很多)计算哈希函数可能会变得很昂贵。
我不知道你打算用什么东西来换东西;你如何绕过这些家伙?
在构造函数中,使用" this"作为lpParam参数调用CreateWindowEx。
然后,在WM_NCCREATE上,调用以下代码:
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) ((CREATESTRUCT*)lParam)->lpCreateParams); SetWindowPos(hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
然后,在窗口过程的顶部,我们可以执行以下操作:
MyWindowClass *wndptr = (MyWindowClass*) GetWindowLongPtr(hwnd, GWL_USERDATA);
这允许我们执行以下操作:
wndptr->DoSomething();
当然,我们可以使用相同的技术来调用类似我们上面的函数的内容:
wndptr->WndProc(msg, wparam, lparam);
...然后可以按预期使用其" this"指针。
使用SetWindowLongPtr和GetWindowLongPtr访问GWL_USERDATA听起来不错,但我强烈建议不要使用这种方法。
这正是Zeus编辑器所使用的方法,近年来,它只引起了痛苦。
我认为发生的情况是将第三方Windows消息发送到Zeus,该消息也设置了GWL_USERDATA值。特别是一个应用程序是Microsoft工具,它提供了一种在任何Windows应用程序中输入亚洲字符的替代方法(即某种软件键盘实用程序)。
问题是Zeus始终假定它设置了GWL_USERDATA数据,并尝试将该数据用作this指针,然后导致崩溃。
如果我现在要重新做一遍,那么我将采用一种将窗口句柄用作键的缓存哈希查找方法。
关于SetWindowLong()/ GetWindowLong()的安全性,根据Microsoft的说法:
The SetWindowLong function fails if the window specified by the hWnd parameter does not belong to the same process as the calling thread.
不幸的是,直到2004年10月12日安全更新发布时,Windows才会强制执行此规则,从而允许应用程序设置任何其他应用程序的GWL_USERDATA。因此,在未打补丁的系统上运行的应用程序很容易受到对SetWindowLong()的调用的攻击。