How can I keep an event from being delivered to the GUI until my code finished running?
I installed a global mouse hook function like this:
mouseEventHook = ::SetWindowsHookEx( WH_MOUSE_LL, mouseEventHookFn, thisModule, 0 );
The hook function looks like this:
RESULT CALLBACK mouseEventHookFn( int code, WPARAM wParam, LPARAM lParam )
{
if ( code == HC_ACTION ) {
PMSLLHOOKSTRUCT mi = (PMSLLHOOKSTRUCT)lParam;
// .. do interesting stuff ..
}
return ::CallNextHookEx( mouseEventHook, code, wParam, lParam );
}
Now, my problem is that I cannot control how long the 'do interesting stuff' part takes exactly. In particular, it might take longer than the LowLevelHooksTimeout defined in the Windows registry. This means that, at least on Windows XP, the system no longer delivers mouse events to my hook function. I'd like to avoid this, but at the same time I need the 'do interesting stuff' part to happen before the target GUI receives the event.
I attempted to solve this by doing the 'interesting stuff' work in a separate thread so that the mouseEventHookFn
above can post a message to the worker thread and then do a return 1;
immediately (which ends the hook function but avoids that the event is handed to the GUI). The idea was that the worker thread, when finished, performs the CallNextHookEx
call itself.
However, this causes a crash inside of CallNextHookEx
(in fact, the crash occurs inside an int开发者_如何学Goernal function called PhkNextValid
. I assume it's not safe to call CallNextHookEx
from outside a hook function, is this true?
If so, does anybody else know how I can run code (which needs to interact with the GUI thread of an application) before the GUI receives the event and avoid that my hook function blocks too long?
assume it's not safe to call CallNextHookEx from outside a hook function, is this true?
I believe this is true.
Since there is a finite number of operations that you can receive through your low-level mouse hook, you could just put them onto a queue to be re-posted to the receiving window once your long-running operation has finished. If you put your long-running on another thread, you'll not 'lock up' the UI, but merely 'eat' or 'defer' the user actions. Return 1 to prevent other hooks happening. Use a boolean flag to signify whether you're collecting events (because your long-running operation has to run yet) or re-posting them (and thus shouldn't hook them).
There aren't likely to be (m)any other low-level hooks in the system that you're cancelling, but you should test this mechanism thoroughly in your situation. I have used it only to block operations before (kill right-mouse-click) rather than defer them.
No fix, you'll have to make your code faster. These hooks are potentially very detrimental to the user interface responsiveness, Windows makes sure that a misbehaving one gets put in the cellar. Even if the timeout were configurable, it would never be documented. That would defeat the purpose of having a timeout in the first place.
Why are you using a mouse event hook? are you hooking the mouse generally or just for a specific window? If it is for a specific window then you need to - instead of using a hook - actually subclass the target window.
This is normally a 2 stage process - hooks always need to be in dll's as the hook needs to be executed in the context of the process (and thread) thats actually handling the message.
So you start by writing a hook dll that, when sent a message, calls SetWindowLong on an HWND to replace the GWL_WINDOWPROC with your new window proc.
In your WindowProc you can take as long as you want to handle messages.
精彩评论