简体   繁体   English

使用链式 WndProcs 时正确清理

[英]Proper Cleanup when using chained WndProcs

When building a chain of WndProcs using SetWindowLongPtr , we store the parent WndProc (the one that has less importance in the chain), so we are able to restore it and call it, like so:当使用SetWindowLongPtr构建 WndProcs 链时,我们存储父 WndProc(在链中不太重要的那个),因此我们能够恢复并调用它,如下所示:

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

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

This is just a rough pseudocode from the top of my head, so please don't focus on that.这只是我脑海中的一个粗略的伪代码,所以请不要专注于此。

The problem with this, however, is, that when trying to remove myWndProc from the chain, like so:然而,问题在于,当试图从链中删除myWndProc时,如下所示:

SetWindowLongPtr(hWnd, GWL_WNDPROC, oldProc);

We effectively set the proc, that was in place when first hooking, as the topmost WndProc.我们有效地将第一次挂钩时就位的 proc 设置为最顶层的 WndProc。 This means, that all WndProcs, that have been added after myWndProc (eg all kinds of Overlays), suddenly won't be called anymore.这意味着,在myWndProc之后添加的所有 WndProc(例如各种 Overlays)突然不再被调用。 Furthermore all hooks that are added afterwards point to invalid memory with their oldProc .此外,之后添加的所有钩子都指向无效的 memory 及其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.虽然如果所有 WndProcs 都属于同一个(我们的)代码库,这将是可修复的,但外部代码无法修复此问题。

I haven't found a WM that would be called when a new WndProc is set, so it's also not possible to keep track of which WndProcs have been registered after the current handler, so one could re-build the chain.我还没有找到在设置新的 WndProc 时会调用的WM ,因此也无法跟踪在当前处理程序之后注册了哪些 WndProcs,因此可以重新构建链。 That would still not solve the issue with invalid oldProc s.那仍然无法解决无效oldProc的问题。

Is there something else where the WinAPI can help fixing this problem? WinAPI 还有什么可以帮助解决这个问题的吗? Afaict using SetWindowLongPtr in that way does something like creating a child window(?) that way one could maybe query something?以这种方式使用SetWindowLongPtr的 Afaict 会执行类似于创建子窗口(?)的事情,这样人们可能会查询某些东西?

Or is the bottom line just, that a WndProc cannot safely be unloaded anymore?或者只是底线,WndProc 不能再安全地卸载?

Is there something else where the WinAPI can help fixing this problem? WinAPI 还有什么可以帮助解决这个问题的吗?

Yes, actually.是的,实际上。 This is exactly the kind of situation that SetWindowSubclass() was introduced to address.这正是引入SetWindowSubclass()来解决的那种情况。 Let it handle the chaining and unchaining for you.让它为您处理链接和取消链接。 Don't use SetWindowLongPtr(GWLP_WNDPROC) at all anymore.不要再使用SetWindowLongPtr(GWLP_WNDPROC)了。

SeeSafer subclassing and Subclassing Controls Using ComCtl32.dll version 6 for more information.有关详细信息,请参阅使用 ComCtl32.dll 版本 6的更安全的子类化和子类化控件。

That being said, depending on why you are subclassing, SetWindowsHookEx() or SetWinEventHook() may provide an alternative hook you can use for your intended purposes.话虽这么说,根据您进行子类化的原因SetWindowsHookEx()SetWinEventHook()可能会提供一个替代挂钩,您可以将其用于您的预期目的。

We effectively set the proc, that was in place when first hooking, as the topmost WndProc.我们有效地将第一次挂钩时就位的 proc 设置为最顶层的 WndProc。 This means, that all WndProcs, that have been added after myWndProc (eg all kinds of Overlays), suddenly won't be called anymore.这意味着,在 myWndProc 之后添加的所有 WndProc(例如各种 Overlays)突然不再被调用。 Furthermore all hooks that are added afterwards point to invalid memory with their oldProc.此外,之后添加的所有钩子都指向无效的 memory 及其 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.虽然如果所有 WndProcs 都属于同一个(我们的)代码库,这将是可修复的,但外部代码无法修复此问题。

But you forget that hwnd is your window, created by your process, so it is under your control.但是您忘记了hwnd您的window,由您的进程创建,因此它在您的控制之下。 It means you must design your module to properly restore the chain of window procs.这意味着您必须设计您的模块以正确恢复 window procs 链。 There is no "external code" that can set your window proc to something else.没有“外部代码”可以将您的window proc 设置为其他内容。

The emphasis is on "your window."重点是“您的 window”。

EDIT编辑

Putting back the WndProc pointer will never affect the chain of window procedures.放回WndProc指针永远不会影响 window 过程链。 It is just that after putting it back, your procedure will not be called anymore, but the next one is called directly, which is exactly what you want.只是放回去之后,你的过程就不会再被调用了,而是直接调用下一个,这正是你想要的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM