c++cwinapiwndproc

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:

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:

SetWindowLongPtr(hWnd, GWL_WNDPROC, oldProc);

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.

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. That would still not solve the issue with invalid oldProcs.

Is there something else where the WinAPI can help fixing this problem? Afaict using SetWindowLongPtr in that way does something like creating a child window(?) that way one could maybe query something?

Or is the bottom line just, that a WndProc cannot safely be unloaded anymore?


Solution

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

    Yes, actually. This is exactly the kind of situation that SetWindowSubclass() was introduced to address. Let it handle the chaining and unchaining for you. Don't use SetWindowLongPtr(GWLP_WNDPROC) at all anymore.

    See Safer subclassing and Subclassing Controls Using ComCtl32.dll version 6 for more information.

    That being said, depending on why you are subclassing, SetWindowsHookEx() or SetWinEventHook() may provide an alternative hook you can use for your intended purposes.