使用链式 WndProcs 时的正确清理

Proper Cleanup when using chained WndProcs

当使用 SetWindowLongPtr 构建 WndProc 链时,我们存储 parent WndProc(链中重要性较低的那个),因此我们能够恢复它并调用它,像这样:

LONG_PTR oldProc = SetWindowLongPtr(hWnd, GWL_WNDPROC, &myWndProc);

WndProc myWndProc(HANDLE hWnd, ...) {
  return CallWndProc(oldProc);
}

这只是我脑海中的粗略伪代码,所以请不要关注它。

然而,这个问题是,当试图从链中删除 myWndProc 时,如下所示:

SetWindowLongPtr(hWnd, GWL_WNDPROC, oldProc);

我们有效地将第一次挂钩时就位的过程设置为最顶层的 WndProc。 这意味着,所有已添加 after myWndProc 的 WndProcs(例如各种 Overlays)突然不再被调用。 此外,之后添加的所有挂钩都使用 oldProc.

指向无效内存

这意味着,以这种方式完成的挂钩无法在不弄乱的情况下正确卸载。 虽然如果所有 WndProc 都属于同一个(我们的)代码库,这将是可以修复的,但这不能用外部代码修复。

我还没有找到在设置新的 WndProc 时会调用的 WM,因此也无法跟踪在当前处理程序之后注册了哪些 WndProc,因此可以 re-build链条。这仍然无法解决 oldProcs.

无效的问题

WinAPI 是否还有其他可以帮助解决此问题的地方? 以这种方式使用 SetWindowLongPtr 的 Afaict 会像创建 child window(?) 这样的人可能会查询某些东西?

或者底线只是不能再安全地卸载 WndProc?

We effectively set the proc, that was in place when first hooking, as the topmost WndProc. This means, that all WndProcs, that have been added after myWndProc (e.g. all kinds of Overlays), suddenly won't be called anymore. Furthermore all hooks that are added afterwards point to invalid memory with their oldProc.

This means, hooks done that way can't be properly unloaded without messing something up. While this would be fixable if all WndProcs belong to the same (our) codebase, this is not fixable with external code.

但是你忘记了hwnd你的window,由你的进程创建,所以它在你的控制之下。这意味着您必须设计 您的 模块以正确恢复 window 过程链。没有可以将 你的 window proc 设置为其他东西的“外部代码”。

重点是“你的window”。

编辑

放回 WndProc 指针永远不会影响 window 过程链。只是放回去之后,你的程序就不会再被调用了,而是直接调用下一个,这正是你想要的。

Is there something else where the WinAPI can help fixing this problem?

是的,实际上。这正是引入 SetWindowSubclass() 来解决的情况。让它为您处理链接和解除链接。不要再使用 SetWindowLongPtr(GWLP_WNDPROC)

有关详细信息,请参阅 Safer subclassing and Subclassing Controls Using ComCtl32.dll version 6

也就是说,根据 为什么 您要进行子类化,SetWindowsHookEx() or SetWinEventHook() 可能会提供一个替代挂钩,您可以将其用于您的预期目的。