Why is this window subclass code broken?

I am trying to subclass a window that currently has focus. I do this by monitoring HCBT_ACTIVATE events using the CBT hook, and sets and cancels WndProc focused and pre-focused windows.

The problem is that it only works when I have a breakpoint somewhere in the code .

If there is no breakpoint, as soon as my application exits, all the windows that I have subclassed fail, although I removed the subclass and restored the original WndProc.

I checked that Unsubclass() is called whenever my application shuts down.

 // code extracts HINSTANCE hInst; HHOOK hHook; #pragma data_seg(".shared") HWND hWndSubclass = 0; FARPROC lpfnOldWndProc = NULL; #pragma data_seg() #pragma comment(linker, "/section:.shared,rws") void Unsubclass() { // if the window still exists if (hWndSubclass != 0 && IsWindow(hWndSubclass)) { SetWindowLongPtr(hWndSubclass, GWLP_WNDPROC, (LPARAM)lpfnOldWndProc); hWndSubclass = 0; } } static LRESULT CALLBACK SubClassFunc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { if (message == WM_MOVING) { // this is just test code so I can see it works (it does) RECT* r = (RECT*)lParam; r->right = r->left + 500; r->bottom = r->top + 500; return TRUE; } else if (message == WM_DESTROY) { Unsubclass(); } return CallWindowProc((WNDPROC)lpfnOldWndProc, hWndSubclass, message, wParam, lParam); } void SubclassWindow(HWND hWnd) { // remove the subclassing for the old window Unsubclass(); // subclass the new window lpfnOldWndProc = (FARPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LPARAM)SubClassFunc); hWndSubclass = hWnd; } static LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode == HCBT_ACTIVATE) { SubclassWindow((HWND)wParam); } return 0; } // ... code that initializes the CBT proc __declspec(dllexport) BOOL Setup() { hHook = SetWindowsHookEx(WH_CBT, CBTProc, hInst, 0); } __declspec(dllexport) BOOL Teardown() { UnhookWindowsHookEx(hHook); Unsubclass(); } BOOL APIENTRY DllMain( HINSTANCE hInstance, DWORD Reason, LPVOID Reserved ) { switch(Reason) { case DLL_PROCESS_ATTACH: hInst = hInstance; return TRUE; case DLL_PROCESS_DETACH: Unsubclass(); return TRUE; } return TRUE; } 
+1
source share
4 answers

Your problems depend on several fronts:

  • UnHookWindowsHook does not unload the injected DLLs, all it does is remove the hook handler. If dlls need to be unloaded, then they need to come up with some sort of unloading mechanism.
  • SetWindowLongPtr usually fails when called from a process other than the process to which the window belongs.

The nett result from this is very difficult to safely remove window hooks. First, your OldWindowProc pointer should not be stored in the shared data area. Then, to remove the subclass, you should be able to share the (currently) subclassed process to execute the non-subclass.

What you can do is first register a new unique identifier for the message and put it in your common area using RegisterWindowMessage. WM_REMOVE_HOOK .

 UINT idWM_REMOVE_HOOK = RegisterWindowMessage("WM_REMOVE_HOOK"); 

Now that you need to remove the hook,

 SendMessage(hWndSubClass,idWM_REMOVE_HOOK,0,0); 

In your subclass of proc:

 if(uMsg == WM_DESTROY || uMsg == idWM_REMOVE_HOOK) { Unsubclass(hwnd); } 

Remove the UnSubClass call in DLL_PROCESS_DETATCH. This is a dangerous race condition that will cause your DLL to be unloaded into some random process in order to destroy the hook data of a potentially valid hook in another process.

+3
source

lpfnOldWndProc and hWndSubclass are global pointers. It looks like you have only one process. What if the process creates multiple windows?

Then you disable only the last one.

EDIT: Also, why are you coming off at Process DETACH?

0
source

You create a global system-wide hook in the DLL. You need to save the HHOOK descriptor and your subclass information in the shared memory block so that all instances of your DLL in all running processes can access them. Your variables are declared global in the code, but each individual instance of the DLL will have its own local copy, and therefore, they will not be initialized in all but one of your DLL instances (the one that calls Setup ()). They must be available globally throughout the system.

You should also not call TearDown () on DLL_PROCESS_DETACH. Each instance of the DLL will call TearDown () when their respective processes terminate, but only one instance, which is actually called Setup (), must be the one called by Teardown ().

0
source

If the debugger leads to a successful process by adding a breakpoint, this is most likely a synchronization issue. Your main application may close and free resources before window subclasses receive the messages necessary to remove the subclass. You might want to give them several processing cycles to process your own messages between uncoupling and deny. (In Delphi, you can do this by calling Application.ProcessMessages, but in your C ++ version. You don't know the answer to this question.

0
source

Source: https://habr.com/ru/post/1304132/


All Articles