存储此指针以在WndProc中使用的最佳方法

时间:2020-03-06 14:34:02  来源:igfitidea点击:

我想知道最好的/通用的方式来存储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()的调用的攻击。