diff --git a/dlls/user/hook.c b/dlls/user/hook.c index 7dacc47a539..af362298586 100644 --- a/dlls/user/hook.c +++ b/dlls/user/hook.c @@ -97,6 +97,17 @@ static const char * const hook_names[WH_MAXHOOK - WH_MINHOOK + 1] = }; +/*********************************************************************** + * get_ll_hook_timeout + * + */ +static UINT get_ll_hook_timeout(void) +{ + /* FIXME: should retrieve LowLevelHooksTimeout in HKEY_CURRENT_USER\Control Panel\Desktop */ + return 2000; +} + + /*********************************************************************** * set_windows_hook * @@ -123,7 +134,8 @@ static HHOOK set_windows_hook( INT id, HOOKPROC proc, HINSTANCE inst, DWORD tid, } else /* system-global hook */ { - if (!inst || !GetModuleFileNameW( inst, module, MAX_PATH )) + if (id == WH_KEYBOARD_LL || id == WH_MOUSE_LL) inst = 0; + else if (!inst || !GetModuleFileNameW( inst, module, MAX_PATH )) { SetLastError( ERROR_INVALID_PARAMETER ); return 0; @@ -269,28 +281,47 @@ LRESULT HOOK_CallHooks( INT id, INT code, WPARAM wparam, LPARAM lparam, BOOL uni { MESSAGEQUEUE *queue = QUEUE_Current(); HOOKPROC proc = NULL; - HHOOK prev; + HHOOK handle = 0; + DWORD pid = 0, tid = 0; WCHAR module[MAX_PATH]; BOOL unicode_hook = FALSE; LRESULT ret = 0; if (!queue) return 0; - prev = queue->hook; SERVER_START_REQ( start_hook_chain ) { req->id = id; wine_server_set_reply( req, module, sizeof(module)-sizeof(WCHAR) ); - if (!wine_server_call_err( req )) + if (!wine_server_call( req )) { module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0; - queue->hook = reply->handle; - proc = reply->proc; + handle = reply->handle; + proc = reply->proc; + pid = reply->pid; + tid = reply->tid; unicode_hook = reply->unicode; } } SERVER_END_REQ; - if (proc) + if (tid) + { + TRACE( "calling hook in thread %04lx %s code %x wp %x lp %lx\n", + tid, hook_names[id-WH_MINHOOK], code, wparam, lparam ); + + switch(id) + { + case WH_KEYBOARD_LL: + MSG_SendInternalMessageTimeout( pid, tid, WM_WINE_KEYBOARD_LL_HOOK, wparam, lparam, + SMTO_ABORTIFHUNG, get_ll_hook_timeout(), &ret ); + break; + case WH_MOUSE_LL: + MSG_SendInternalMessageTimeout( pid, tid, WM_WINE_MOUSE_LL_HOOK, wparam, lparam, + SMTO_ABORTIFHUNG, get_ll_hook_timeout(), &ret ); + break; + } + } + else if (proc) { TRACE( "calling hook %p %s code %x wp %x lp %lx module %s\n", proc, hook_names[id-WH_MINHOOK], code, wparam, lparam, debugstr_w(module) ); @@ -298,19 +329,22 @@ LRESULT HOOK_CallHooks( INT id, INT code, WPARAM wparam, LPARAM lparam, BOOL uni if (!module[0] || (proc = get_hook_proc( proc, module )) != NULL) { int locks = WIN_SuspendWndsLock(); + HHOOK prev = queue->hook; + queue->hook = handle; ret = call_hook( proc, id, code, wparam, lparam, unicode, unicode_hook ); + queue->hook = prev; WIN_RestoreWndsLock( locks ); } - SERVER_START_REQ( finish_hook_chain ) - { - req->id = id; - wine_server_call( req ); - } - SERVER_END_REQ; } + else return 0; - queue->hook = prev; + SERVER_START_REQ( finish_hook_chain ) + { + req->id = id; + wine_server_call( req ); + } + SERVER_END_REQ; return ret; } @@ -406,24 +440,27 @@ BOOL WINAPI UnhookWindowsHookEx( HHOOK hhook ) LRESULT WINAPI CallNextHookEx( HHOOK hhook, INT code, WPARAM wparam, LPARAM lparam ) { MESSAGEQUEUE *queue = QUEUE_Current(); - HHOOK prev; - WCHAR module[MAX_PATH]; HOOKPROC proc = NULL; + WCHAR module[MAX_PATH]; + HHOOK handle = 0; + DWORD pid = 0, tid = 0; INT id = 0; BOOL prev_unicode = FALSE, next_unicode = FALSE; LRESULT ret = 0; if (!queue) return 0; - prev = queue->hook; + SERVER_START_REQ( get_next_hook ) { - req->handle = prev; + req->handle = queue->hook; wine_server_set_reply( req, module, sizeof(module)-sizeof(WCHAR) ); if (!wine_server_call_err( req )) { module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0; - queue->hook = reply->next; + handle = reply->next; id = reply->id; + pid = reply->pid; + tid = reply->tid; proc = reply->proc; prev_unicode = reply->prev_unicode; next_unicode = reply->next_unicode; @@ -431,15 +468,36 @@ LRESULT WINAPI CallNextHookEx( HHOOK hhook, INT code, WPARAM wparam, LPARAM lpar } SERVER_END_REQ; - if (proc) + if (tid) + { + TRACE( "calling hook in thread %04lx %s code %x wp %x lp %lx\n", + tid, hook_names[id-WH_MINHOOK], code, wparam, lparam ); + + switch(id) + { + case WH_KEYBOARD_LL: + MSG_SendInternalMessageTimeout( pid, tid, WM_WINE_KEYBOARD_LL_HOOK, wparam, lparam, + SMTO_ABORTIFHUNG, get_ll_hook_timeout(), &ret ); + break; + case WH_MOUSE_LL: + MSG_SendInternalMessageTimeout( pid, tid, WM_WINE_MOUSE_LL_HOOK, wparam, lparam, + SMTO_ABORTIFHUNG, get_ll_hook_timeout(), &ret ); + break; + } + } + else if (proc) { TRACE( "calling hook %p %s code %x wp %x lp %lx module %s\n", proc, hook_names[id-WH_MINHOOK], code, wparam, lparam, debugstr_w(module) ); if (!module[0] || (proc = get_hook_proc( proc, module )) != NULL) - return call_hook( proc, id, code, wparam, lparam, prev_unicode, next_unicode ); + { + HHOOK prev = queue->hook; + queue->hook = handle; + ret = call_hook( proc, id, code, wparam, lparam, prev_unicode, next_unicode ); + queue->hook = prev; + } } - queue->hook = prev; return ret; } diff --git a/dlls/user/message.c b/dlls/user/message.c index 8064ae4d332..a7ca4832d7a 100644 --- a/dlls/user/message.c +++ b/dlls/user/message.c @@ -21,6 +21,8 @@ #include "config.h" #include "wine/port.h" +#include + #include "winbase.h" #include "wingdi.h" #include "winuser.h" @@ -553,6 +555,12 @@ static size_t pack_message( HWND hwnd, UINT message, WPARAM wparam, LPARAM lpara case WM_WINE_SETWINDOWPOS: push_data( data, (WINDOWPOS *)lparam, sizeof(WINDOWPOS) ); return 0; + case WM_WINE_KEYBOARD_LL_HOOK: + push_data( data, (KBDLLHOOKSTRUCT *)lparam, sizeof(KBDLLHOOKSTRUCT) ); + return 0; + case WM_WINE_MOUSE_LL_HOOK: + push_data( data, (MSLLHOOKSTRUCT *)lparam, sizeof(MSLLHOOKSTRUCT) ); + return 0; /* these contain an HFONT */ case WM_SETFONT: @@ -795,6 +803,13 @@ static BOOL unpack_message( HWND hwnd, UINT message, WPARAM *wparam, LPARAM *lpa if (!*lparam) return TRUE; if (!get_buffer_space( buffer, sizeof(BOOL) )) return FALSE; break; + case WM_WINE_KEYBOARD_LL_HOOK: + minsize = sizeof(KBDLLHOOKSTRUCT); + break; + case WM_WINE_MOUSE_LL_HOOK: + minsize = sizeof(MSLLHOOKSTRUCT); + break; + /* these contain an HFONT */ case WM_SETFONT: case WM_GETFONT: @@ -1086,6 +1101,10 @@ static LRESULT handle_internal_message( HWND hwnd, UINT msg, WPARAM wparam, LPAR return EnableWindow( hwnd, wparam ); case WM_WINE_SETACTIVEWINDOW: return (LRESULT)SetActiveWindow( (HWND)wparam ); + case WM_WINE_KEYBOARD_LL_HOOK: + return HOOK_CallHooks( WH_KEYBOARD_LL, HC_ACTION, wparam, lparam, TRUE ); + case WM_WINE_MOUSE_LL_HOOK: + return HOOK_CallHooks( WH_MOUSE_LL, HC_ACTION, wparam, lparam, TRUE ); default: FIXME( "unknown internal message %x\n", msg ); return 0; @@ -1261,6 +1280,7 @@ static BOOL post_dde_message( DWORD dest_tid, struct packed_message *data, const { req->id = dest_tid; req->type = info->type; + req->flags = 0; req->win = info->hwnd; req->msg = info->msg; req->wparam = info->wparam; @@ -1756,6 +1776,46 @@ static LRESULT send_inter_thread_message( DWORD dest_tid, const struct send_mess } +/*********************************************************************** + * MSG_SendInternalMessageTimeout + * + * Same as SendMessageTimeoutW but sends the message to a specific thread + * without requiring a window handle. Only works for internal Wine messages. + */ +LRESULT MSG_SendInternalMessageTimeout( DWORD dest_pid, DWORD dest_tid, + UINT msg, WPARAM wparam, LPARAM lparam, + UINT flags, UINT timeout, PDWORD_PTR res_ptr ) +{ + struct send_message_info info; + LRESULT ret, result; + + assert( msg & 0x80000000 ); /* must be an internal Wine message */ + + info.type = MSG_UNICODE; + info.hwnd = 0; + info.msg = msg; + info.wparam = wparam; + info.lparam = lparam; + info.flags = flags; + info.timeout = timeout; + + if (USER_IsExitingThread( dest_tid )) return 0; + + if (dest_tid == GetCurrentThreadId()) + { + result = handle_internal_message( 0, msg, wparam, lparam ); + ret = 1; + } + else + { + if (dest_pid != GetCurrentProcessId()) info.type = MSG_OTHER_PROCESS; + ret = send_inter_thread_message( dest_tid, &info, &result ); + } + if (ret && res_ptr) *res_ptr = result; + return ret; +} + + /*********************************************************************** * SendMessageTimeoutW (USER32.@) */ diff --git a/dlls/user/message.h b/dlls/user/message.h index 9cbe941f734..dc776aeaa46 100644 --- a/dlls/user/message.h +++ b/dlls/user/message.h @@ -57,8 +57,9 @@ extern BOOL MSG_process_raw_hardware_message( MSG *msg, ULONG_PTR extra_info, HW UINT first, UINT last, BOOL remove ); extern BOOL MSG_process_cooked_hardware_message( MSG *msg, ULONG_PTR extra_info, BOOL remove ); extern void MSG_JournalPlayBackMsg(void); - -/* sendmsg.c */ +extern LRESULT MSG_SendInternalMessageTimeout( DWORD dest_pid, DWORD dest_tid, + UINT msg, WPARAM wparam, LPARAM lparam, + UINT flags, UINT timeout, PDWORD_PTR res_ptr ); extern BOOL MSG_peek_message( MSG *msg, HWND hwnd, UINT first, UINT last, int flags ); /* spy.c */ diff --git a/include/user.h b/include/user.h index 1362696c7b2..0c26c0be56e 100644 --- a/include/user.h +++ b/include/user.h @@ -58,7 +58,9 @@ enum wine_internal_message WM_WINE_SETPARENT, WM_WINE_SETWINDOWLONG, WM_WINE_ENABLEWINDOW, - WM_WINE_SETACTIVEWINDOW + WM_WINE_SETACTIVEWINDOW, + WM_WINE_KEYBOARD_LL_HOOK, + WM_WINE_MOUSE_LL_HOOK }; /* internal SendInput codes (FIXME) */ diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 4684f189825..f87ce73eddc 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -3012,6 +3012,8 @@ struct start_hook_chain_reply { struct reply_header __header; user_handle_t handle; + process_id_t pid; + thread_id_t tid; void* proc; int unicode; /* VARARG(module,unicode_str); */ @@ -3041,6 +3043,8 @@ struct get_next_hook_reply struct reply_header __header; user_handle_t next; int id; + process_id_t pid; + thread_id_t tid; void* proc; int prev_unicode; int next_unicode; @@ -3622,6 +3626,6 @@ union generic_reply struct set_clipboard_info_reply set_clipboard_info_reply; }; -#define SERVER_PROTOCOL_VERSION 115 +#define SERVER_PROTOCOL_VERSION 116 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/hook.c b/server/hook.c index 65bd3bad709..fa42d8d07f9 100644 --- a/server/hook.c +++ b/server/hook.c @@ -28,6 +28,7 @@ #include "winuser.h" #include "object.h" +#include "process.h" #include "request.h" #include "user.h" @@ -91,16 +92,16 @@ static struct hook_table *alloc_hook_table(void) } /* create a new hook and add it to the specified table */ -static struct hook *add_hook( struct thread *thread, int index ) +static struct hook *add_hook( struct thread *thread, int index, int global ) { struct hook *hook; - struct hook_table *table = thread ? get_queue_hooks(thread) : global_hooks; + struct hook_table *table = global ? global_hooks : get_queue_hooks(thread); if (!table) { if (!(table = alloc_hook_table())) return NULL; - if (thread) set_queue_hooks( thread, table ); - else global_hooks = table; + if (global) global_hooks = table; + else set_queue_hooks( thread, table ); } if (!(hook = mem_alloc( sizeof(*hook) ))) return NULL; @@ -145,7 +146,10 @@ static struct hook *find_hook( struct thread *thread, int index, void *proc ) /* get the hook table that a given hook belongs to */ inline static struct hook_table *get_table( struct hook *hook ) { - return hook->thread ? get_queue_hooks(hook->thread) : global_hooks; + if (!hook->thread) return global_hooks; + if (hook->index + WH_MINHOOK == WH_KEYBOARD_LL) return global_hooks; + if (hook->index + WH_MINHOOK == WH_MOUSE_LL) return global_hooks; + return get_queue_hooks(hook->thread); } /* get the first hook in the chain */ @@ -236,6 +240,25 @@ static void release_hook_chain( struct hook_table *table, int index ) } } +/* remove all global hooks owned by a given thread */ +void remove_thread_hooks( struct thread *thread ) +{ + int index; + + if (!global_hooks) return; + + /* only low-level keyboard/mouse global hooks can be owned by a thread */ + for (index = WH_KEYBOARD_LL - WH_MINHOOK; index <= WH_MOUSE_LL - WH_MINHOOK; index++) + { + struct hook *hook = get_first_hook( global_hooks, index ); + while (hook) + { + struct hook *next = HOOK_ENTRY( list_next( &global_hooks->hooks[index], &hook->chain ) ); + if (hook->thread == thread) remove_hook( hook ); + hook = next; + } + } +} /* set a window hook */ DECL_HANDLER(set_hook) @@ -243,6 +266,7 @@ DECL_HANDLER(set_hook) struct thread *thread; struct hook *hook; WCHAR *module; + int global; size_t module_size = get_req_data_size(); if (!req->proc || req->id < WH_MINHOOK || req->id > WH_MAXHOOK) @@ -250,7 +274,14 @@ DECL_HANDLER(set_hook) set_error( STATUS_INVALID_PARAMETER ); return; } - if (!req->tid) + 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 ); + module = NULL; + global = 1; + } + else if (!req->tid) { if (!module_size) { @@ -259,14 +290,16 @@ DECL_HANDLER(set_hook) } if (!(module = memdup( get_req_data(), module_size ))) return; thread = NULL; + global = 1; } else { module = NULL; + global = 0; if (!(thread = get_thread_from_id( req->tid ))) return; } - if ((hook = add_hook( thread, req->id - WH_MINHOOK ))) + if ((hook = add_hook( thread, req->id - WH_MINHOOK, global ))) { hook->proc = req->proc; hook->unicode = req->unicode; @@ -319,8 +352,20 @@ DECL_HANDLER(start_hook_chain) !(hook = get_first_valid_hook( global_hooks, req->id - WH_MINHOOK ))) return; /* no hook set */ } + + if (hook->thread && hook->thread != current) /* must run in other thread */ + { + reply->pid = get_process_id( hook->thread->process ); + reply->tid = get_thread_id( hook->thread ); + reply->proc = 0; + } + else + { + reply->pid = 0; + reply->tid = 0; + reply->proc = hook->proc; + } reply->handle = hook->handle; - reply->proc = hook->proc; reply->unicode = hook->unicode; table->counts[hook->index]++; if (hook->module) set_reply_data( hook->module, hook->module_size ); @@ -358,9 +403,20 @@ DECL_HANDLER(get_next_hook) { reply->next = next->handle; reply->id = next->index + WH_MINHOOK; - reply->proc = next->proc; 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) + { + reply->pid = get_process_id( next->thread->process ); + reply->tid = get_thread_id( next->thread ); + reply->proc = 0; + } + else + { + reply->pid = 0; + reply->tid = 0; + reply->proc = next->proc; + } } } diff --git a/server/protocol.def b/server/protocol.def index 6a8c46040af..7020c9cf83b 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -2108,6 +2108,8 @@ enum message_type int id; /* id of the hook */ @REPLY user_handle_t handle; /* handle to the next hook */ + process_id_t pid; /* process id for low-level keyboard/mouse hooks */ + thread_id_t tid; /* thread id for low-level keyboard/mouse hooks */ void* proc; /* hook procedure */ int unicode; /* is it a unicode hook? */ VARARG(module,unicode_str); /* module name */ @@ -2126,6 +2128,8 @@ enum message_type @REPLY user_handle_t next; /* handle to the next hook */ int id; /* id of the next hook */ + process_id_t pid; /* process id for low-level keyboard/mouse hooks */ + thread_id_t tid; /* thread id for low-level keyboard/mouse hooks */ void* proc; /* next hook procedure */ int prev_unicode; /* was the previous a unicode hook? */ int next_unicode; /* is the next a unicode hook? */ diff --git a/server/queue.c b/server/queue.c index ce7246292f8..eb2c11cf0b0 100644 --- a/server/queue.c +++ b/server/queue.c @@ -239,6 +239,7 @@ void free_msg_queue( struct thread *thread ) { struct process *process = thread->process; + remove_thread_hooks( thread ); if (!thread->queue) return; if (process->queue == thread->queue) /* is it the process main queue? */ { diff --git a/server/trace.c b/server/trace.c index 5c7970c4cae..bfab0824b94 100644 --- a/server/trace.c +++ b/server/trace.c @@ -2437,6 +2437,8 @@ static void dump_start_hook_chain_request( const struct start_hook_chain_request static void dump_start_hook_chain_reply( const struct start_hook_chain_reply *req ) { fprintf( stderr, " handle=%p,", req->handle ); + fprintf( stderr, " pid=%04x,", req->pid ); + fprintf( stderr, " tid=%04x,", req->tid ); fprintf( stderr, " proc=%p,", req->proc ); fprintf( stderr, " unicode=%d,", req->unicode ); fprintf( stderr, " module=" ); @@ -2457,6 +2459,8 @@ static void dump_get_next_hook_reply( const struct get_next_hook_reply *req ) { fprintf( stderr, " next=%p,", req->next ); fprintf( stderr, " id=%d,", req->id ); + fprintf( stderr, " pid=%04x,", req->pid ); + fprintf( stderr, " tid=%04x,", req->tid ); fprintf( stderr, " proc=%p,", req->proc ); fprintf( stderr, " prev_unicode=%d,", req->prev_unicode ); fprintf( stderr, " next_unicode=%d,", req->next_unicode ); diff --git a/server/user.h b/server/user.h index e4229a08b75..a075dd71f28 100644 --- a/server/user.h +++ b/server/user.h @@ -50,6 +50,7 @@ extern void cleanup_clipboard_thread( struct thread *thread ); /* hook functions */ extern void close_global_hooks(void); +extern void remove_thread_hooks( struct thread *thread ); /* queue functions */