Trying to track down the problem with our DInput implementation I found some interesting stuff - our global hooks don't work correctly because hook callbacks are never called if event is generated in the different thread. I don't think this is a revelation to number of people who knew that before. But how do we fix it?
Her is what I dug up so far: 1. Installing global hook we setting it as WINEVENT_INCONTEXT while msdn and tests indicate that it qualifies as WINEVENT_OUTOFCONTEXT instead. Unless of course we don't want to call post_win_event in step 5 and go to step 6 instead. 2. All HW mouse messages start here: http://source.winehq.org/source/dlls/x11drv/mouse.c#L191 From looking at it I assume that all the HW mouse hooks should be handled by HOOK_CallHooks function. This is because there are no code related to hooks in send_message server call. 3. First thing that HOOK_CallHooks does is calling HOOK_IsHooked with checks only current thread for installed hooks. So this is first place that is wrong. Let's comment that check out for now. I think we need to move that check farther down, after the call to server. 4. We call start_hook_chain http://source.winehq.org/source/server/hook.c#L468 to start hook callback chain. There we call get_first_valid_hook on local and then global if no local hooks exist. But there again we check for http://source.winehq.org/source/server/hook.c#L211 the hook callback thread being local (which is again not the case for global hooks). This is problem #2. 5. Now we have post_win_event of but right before it we hitting an assert so this is not the right way? 6. Say we returned with success and back to HOOK_CallHooks that now tries to call MSG_SendInternalMessageTimeout. Which for some reason fails to deliver message to our global hook.
So my question is to anyone who knows. What is the proper way to deliver these messages to global hooks that are in a different thread/process?
Attached is the small program to test part of this. Fixing problems (1) & (2) made it work. But it still not enough for the game (for some reason).
Vitaliy
Vitaliy Margolen [wine-devel@kievinfo.com] wrote:
So my question is to anyone who knows. What is the proper way to deliver these messages to global hooks that are in a different thread/process?
Just an observation I did in a Windows program running under WinXP. Not sure if this is related somehow but it seems quite like the opposite of what you describe here although with windows message hooks.
I had a program that was hooking window messages for a particular window. When using SendMessage from the same thread as in which the Windows message loop was running (doesn't make much sense of course but it was just for testing purposes) I could not receive any events above WM_USER, lower events did seem to get received properly though. When placing SendMessage in a different thread the events did come through.
Not exactly sure yet what this is all about but message handling and especially hooking in Windows for sure can be a tricky business.
Rolf Kalbermatter
On Tue, 2006-01-24 at 23:50 -0700, Vitaliy Margolen wrote:
Attached is the small program to test part of this. Fixing problems (1) & (2) made it work. But it still not enough for the game (for some reason).
Vitaliy, could you transform hook_test.c to an appropriate Wine test suite while leaving hook.dll a PE dll with all the ok()s which show what's wrong with current implementation?
Vitaliy Margolen wine-devel@kievinfo.com writes:
Trying to track down the problem with our DInput implementation I found some interesting stuff - our global hooks don't work correctly because hook callbacks are never called if event is generated in the different thread. I don't think this is a revelation to number of people who knew that before. But how do we fix it?
That's because the new win event code changed the meaning of the thread field, and the low-level hooks haven't been updated for it. Try something like this:
diff --git a/server/hook.c b/server/hook.c index dd7b177..f8d4488 100644 --- a/server/hook.c +++ b/server/hook.c @@ -199,6 +199,15 @@ inline static int run_hook_in_current_th return 0; }
+/* check if a given hook should run in the owner thread instead of the current thread */ +inline static int run_hook_in_owner_thread( struct hook *hook ) +{ + if ((hook->index == WH_MOUSE_LL - WH_MINHOOK || + hook->index == WH_KEYBOARD_LL - WH_MINHOOK)) + return hook->owner != current; + return 0; +} + /* find the first non-deleted hook in the chain */ inline static struct hook *get_first_valid_hook( struct hook_table *table, int index, int event, user_handle_t win, @@ -389,7 +398,11 @@ DECL_HANDLER(set_hook) if (req->id == WH_KEYBOARD_LL || req->id == WH_MOUSE_LL) { /* low-level hardware hooks are special: always global, but without a module */ - thread = (struct thread *)grab_object( current ); + if (thread) + { + set_error( STATUS_INVALID_PARAMETER ); + goto done; + } module = NULL; global = 1; } @@ -488,10 +501,10 @@ DECL_HANDLER(start_hook_chain) return; /* no hook set */ }
- if (hook->thread && hook->thread != current) /* must run in other thread */ + if (run_hook_in_owner_thread( hook )) { - reply->pid = get_process_id( hook->thread->process ); - reply->tid = get_thread_id( hook->thread ); + reply->pid = get_process_id( hook->owner->process ); + reply->tid = get_thread_id( hook->owner ); } else { @@ -541,10 +554,10 @@ DECL_HANDLER(get_next_hook) reply->prev_unicode = hook->unicode; reply->next_unicode = next->unicode; if (next->module) set_reply_data( next->module, next->module_size ); - if (next->thread && next->thread != current) + if (run_hook_in_owner_thread( next )) { - reply->pid = get_process_id( next->thread->process ); - reply->tid = get_thread_id( next->thread ); + reply->pid = get_process_id( next->owner->process ); + reply->tid = get_thread_id( next->owner ); } else {
Thursday, January 26, 2006, 6:32:13 AM, Alexandre Julliard wrote:
Vitaliy Margolen wine-devel@kievinfo.com writes:
Trying to track down the problem with our DInput implementation I found some interesting stuff - our global hooks don't work correctly because hook callbacks are never called if event is generated in the different thread. I don't think this is a revelation to number of people who knew that before. But how do we fix it?
That's because the new win event code changed the meaning of the thread field, and the low-level hooks haven't been updated for it. Try something like this:
It does work thank you. The only question I have : does that cover WINEVENT_OUTOFCONTEXT hooks too?
I'm working on a test to test hooks. Hopefully there will be a way to test it.
And it looks like we will have to rethink our DInput - the game still doesn't work. It seems that the thread which acquired mouse doesn't have a message loop.
Vitaliy
On Thu, 2006-01-26 at 08:04 -0700, Vitaliy Margolen wrote:
It does work thank you. The only question I have : does that cover WINEVENT_OUTOFCONTEXT hooks too?
I'm working on a test to test hooks. Hopefully there will be a way to test it.
There are some tests for hooks and winevents in dlls/user/tests/msg.c, test_winevents(). Just add your new tests there.
Vitaliy Margolen wine-devel@kievinfo.com writes:
It does work thank you. The only question I have : does that cover WINEVENT_OUTOFCONTEXT hooks too?
No, that's a different mechanism. Do you have evidence that they are currently broken too?
Thursday, January 26, 2006, 12:43:58 PM, Alexandre Julliard wrote:
Vitaliy Margolen wine-devel@kievinfo.com writes:
It does work thank you. The only question I have : does that cover WINEVENT_OUTOFCONTEXT hooks too?
No, that's a different mechanism. Do you have evidence that they are currently broken too?
No i was just asking. If it's in the different place then this change shouldn't affect them.
Vitaliy.