winepulse: Use GetModuleFileName() instead of hardcoded module filename for registry...
[wine.git] / dlls / win32u / hook.c
blob4b9f22f7afd731ca9727a359ed4eb3be23952a51
1 /*
2 * Windows hook functions
4 * Copyright 2002 Alexandre Julliard
5 * Copyright 2005 Dmitry Timoshkov
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #if 0
23 #pragma makedep unix
24 #endif
26 #include <assert.h>
27 #include "win32u_private.h"
28 #include "ntuser_private.h"
29 #include "wine/server.h"
30 #include "wine/debug.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(hook);
34 #define WH_WINEVENT (WH_MAXHOOK+1)
36 static const char * const hook_names[WH_WINEVENT - WH_MINHOOK + 1] =
38 "WH_MSGFILTER",
39 "WH_JOURNALRECORD",
40 "WH_JOURNALPLAYBACK",
41 "WH_KEYBOARD",
42 "WH_GETMESSAGE",
43 "WH_CALLWNDPROC",
44 "WH_CBT",
45 "WH_SYSMSGFILTER",
46 "WH_MOUSE",
47 "WH_HARDWARE",
48 "WH_DEBUG",
49 "WH_SHELL",
50 "WH_FOREGROUNDIDLE",
51 "WH_CALLWNDPROCRET",
52 "WH_KEYBOARD_LL",
53 "WH_MOUSE_LL",
54 "WH_WINEVENT"
57 static const char *debugstr_hook_id( unsigned int id )
59 if (id - WH_MINHOOK >= ARRAYSIZE(hook_names)) return wine_dbg_sprintf( "%u", id );
60 return hook_names[id - WH_MINHOOK];
63 BOOL is_hooked( INT id )
65 struct user_thread_info *thread_info = get_user_thread_info();
67 if (!thread_info->active_hooks) return TRUE;
68 return (thread_info->active_hooks & (1 << (id - WH_MINHOOK))) != 0;
71 /***********************************************************************
72 * NtUserSetWindowsHookEx (win32u.@)
74 HHOOK WINAPI NtUserSetWindowsHookEx( HINSTANCE inst, UNICODE_STRING *module, DWORD tid, INT id,
75 HOOKPROC proc, BOOL ansi )
77 HHOOK handle = 0;
79 if (!proc)
81 RtlSetLastWin32Error( ERROR_INVALID_FILTER_PROC );
82 return 0;
85 if (tid) /* thread-local hook */
87 if (id == WH_JOURNALRECORD ||
88 id == WH_JOURNALPLAYBACK ||
89 id == WH_KEYBOARD_LL ||
90 id == WH_MOUSE_LL ||
91 id == WH_SYSMSGFILTER)
93 /* these can only be global */
94 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
95 return 0;
98 else /* system-global hook */
100 if (id == WH_KEYBOARD_LL || id == WH_MOUSE_LL) inst = 0;
101 else if (!inst)
103 RtlSetLastWin32Error( ERROR_HOOK_NEEDS_HMOD );
104 return 0;
108 SERVER_START_REQ( set_hook )
110 req->id = id;
111 req->pid = 0;
112 req->tid = tid;
113 req->event_min = EVENT_MIN;
114 req->event_max = EVENT_MAX;
115 req->flags = WINEVENT_INCONTEXT;
116 req->unicode = !ansi;
117 if (inst) /* make proc relative to the module base */
119 req->proc = wine_server_client_ptr( (void *)((char *)proc - (char *)inst) );
120 wine_server_add_data( req, module->Buffer, module->Length );
122 else req->proc = wine_server_client_ptr( proc );
124 if (!wine_server_call_err( req ))
126 handle = wine_server_ptr_handle( reply->handle );
127 get_user_thread_info()->active_hooks = reply->active_hooks;
130 SERVER_END_REQ;
132 TRACE( "%s %p %x -> %p\n", debugstr_hook_id(id), proc, (int)tid, handle );
133 return handle;
136 /***********************************************************************
137 * NtUserUnhookWindowsHookEx (win32u.@)
139 BOOL WINAPI NtUserUnhookWindowsHookEx( HHOOK handle )
141 NTSTATUS status;
143 SERVER_START_REQ( remove_hook )
145 req->handle = wine_server_user_handle( handle );
146 req->id = 0;
147 status = wine_server_call_err( req );
148 if (!status) get_user_thread_info()->active_hooks = reply->active_hooks;
150 SERVER_END_REQ;
151 if (status == STATUS_INVALID_HANDLE) RtlSetLastWin32Error( ERROR_INVALID_HOOK_HANDLE );
152 return !status;
155 /* see UnhookWindowsHook */
156 BOOL unhook_windows_hook( INT id, HOOKPROC proc )
158 NTSTATUS status;
160 TRACE( "%s %p\n", debugstr_hook_id(id), proc );
162 SERVER_START_REQ( remove_hook )
164 req->handle = 0;
165 req->id = id;
166 req->proc = wine_server_client_ptr( proc );
167 status = wine_server_call_err( req );
168 if (!status) get_user_thread_info()->active_hooks = reply->active_hooks;
170 SERVER_END_REQ;
171 if (status == STATUS_INVALID_HANDLE) RtlSetLastWin32Error( ERROR_INVALID_HOOK_HANDLE );
172 return !status;
175 /***********************************************************************
176 * NtUserCallMsgFilter (win32u.@)
178 BOOL WINAPI NtUserCallMsgFilter( MSG *msg, INT code )
180 /* FIXME: We should use NtCallbackReturn instead of passing (potentially kernel) pointer
181 * like that, but we need to consequently use syscall thunks first for that to work. */
182 if (call_hooks( WH_SYSMSGFILTER, code, 0, (LPARAM)msg, sizeof(*msg) )) return TRUE;
183 return call_hooks( WH_MSGFILTER, code, 0, (LPARAM)msg, sizeof(*msg) );
186 static UINT get_ll_hook_timeout(void)
188 /* FIXME: should retrieve LowLevelHooksTimeout in HKEY_CURRENT_USER\Control Panel\Desktop */
189 return 2000;
192 /***********************************************************************
193 * call_hook
195 * Call hook either in current thread or send message to the destination
196 * thread.
198 static LRESULT call_hook( struct win_hook_params *info, const WCHAR *module )
200 DWORD_PTR ret = 0;
202 if (info->tid)
204 struct hook_extra_info h_extra;
205 h_extra.handle = info->handle;
206 h_extra.lparam = info->lparam;
208 TRACE( "calling hook in thread %04x %s code %x wp %lx lp %lx\n",
209 (int)info->tid, hook_names[info->id-WH_MINHOOK],
210 info->code, (long)info->wparam, info->lparam );
212 switch(info->id)
214 case WH_KEYBOARD_LL:
215 send_internal_message_timeout( info->pid, info->tid, WM_WINE_KEYBOARD_LL_HOOK,
216 info->wparam, (LPARAM)&h_extra, SMTO_ABORTIFHUNG,
217 get_ll_hook_timeout(), &ret );
218 break;
219 case WH_MOUSE_LL:
220 send_internal_message_timeout( info->pid, info->tid, WM_WINE_MOUSE_LL_HOOK,
221 info->wparam, (LPARAM)&h_extra, SMTO_ABORTIFHUNG,
222 get_ll_hook_timeout(), &ret );
223 break;
224 default:
225 ERR("Unknown hook id %d\n", info->id);
226 assert(0);
227 break;
230 else if (info->proc)
232 struct user_thread_info *thread_info = get_user_thread_info();
233 HHOOK prev = thread_info->hook;
234 BOOL prev_unicode = thread_info->hook_unicode;
235 struct win_hook_params *params = info;
236 ULONG size = sizeof(*params);
237 ULONG lparam_ret_size = params->lparam_size;
238 CREATESTRUCTW *cs = NULL;
239 void *ret_ptr;
240 ULONG ret_len;
242 if (params->lparam_size)
244 lparam_ret_size = params->lparam_size;
246 if (params->id == WH_CBT && params->code == HCBT_CREATEWND)
248 cs = ((CBT_CREATEWNDW *)params->lparam)->lpcs;
249 params->lparam = 0;
250 lparam_ret_size = 0;
251 params->lparam_size = sizeof(*cs);
252 if (!IS_INTRESOURCE( cs->lpszName ))
253 params->lparam_size += (wcslen( cs->lpszName ) + 1) * sizeof(WCHAR);
254 if (!IS_INTRESOURCE( cs->lpszClass ))
255 params->lparam_size += (wcslen( cs->lpszClass ) + 1) * sizeof(WCHAR);
258 size += params->lparam_size;
261 if (module && module[0]) size += (lstrlenW( module ) + 1) * sizeof(WCHAR);
262 if (size != sizeof(*params))
264 if (!(params = malloc( size ))) return 0;
265 *params = *info;
268 if (params->lparam_size)
270 if (cs)
272 CREATESTRUCTW *params_cs = (CREATESTRUCTW *)(params + 1);
273 WCHAR *ptr = (WCHAR *)(params_cs + 1);
274 const void *inline_ptr = (void *)0xffffffff;
276 *params_cs = *cs;
277 if (!IS_INTRESOURCE( cs->lpszName ))
279 UINT len = wcslen( cs->lpszName ) + 1;
280 memcpy( ptr, cs->lpszName, len * sizeof(WCHAR) );
281 ptr += len;
282 params_cs->lpszName = inline_ptr;
284 if (!IS_INTRESOURCE( cs->lpszClass ))
286 wcscpy( ptr, cs->lpszClass );
287 params_cs->lpszClass = inline_ptr;
290 else
292 memcpy( params + 1, (const void *)params->lparam, params->lparam_size );
296 if (module && module[0])
297 wcscpy( (WCHAR *)((char *)(params + 1) + params->lparam_size), module );
300 * Windows protects from stack overflow in recursive hook calls. Different Windows
301 * allow different depths.
303 if (thread_info->hook_call_depth >= 25)
305 WARN("Too many hooks called recursively, skipping call.\n");
306 if (params != info) free( params );
307 return 0;
310 TRACE( "calling hook %p %s code %x wp %lx lp %lx module %s\n",
311 params->proc, hook_names[params->id-WH_MINHOOK], params->code, (long)params->wparam,
312 params->lparam, debugstr_w(module) );
314 thread_info->hook = params->handle;
315 thread_info->hook_unicode = params->next_unicode;
316 thread_info->hook_call_depth++;
317 ret = KeUserModeCallback( NtUserCallWindowsHook, params, size, &ret_ptr, &ret_len );
318 if (ret_len && ret_len == lparam_ret_size)
319 memcpy( (void *)params->lparam, ret_ptr, lparam_ret_size );
320 thread_info->hook = prev;
321 thread_info->hook_unicode = prev_unicode;
322 thread_info->hook_call_depth--;
324 if (params != info) free( params );
327 if (info->id == WH_KEYBOARD_LL || info->id == WH_MOUSE_LL)
328 InterlockedIncrement( &global_key_state_counter ); /* force refreshing the key state cache */
329 return ret;
332 /***********************************************************************
333 * NtUserCallNextHookEx (win32u.@)
335 LRESULT WINAPI NtUserCallNextHookEx( HHOOK hhook, INT code, WPARAM wparam, LPARAM lparam )
337 struct user_thread_info *thread_info = get_user_thread_info();
338 struct win_hook_params info;
339 WCHAR module[MAX_PATH];
341 memset( &info, 0, sizeof(info) );
343 SERVER_START_REQ( get_hook_info )
345 req->handle = wine_server_user_handle( thread_info->hook );
346 req->get_next = 1;
347 req->event = EVENT_MIN;
348 wine_server_set_reply( req, module, sizeof(module) - sizeof(WCHAR) );
349 if (!wine_server_call_err( req ))
351 module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0;
352 info.handle = wine_server_ptr_handle( reply->handle );
353 info.id = reply->id;
354 info.pid = reply->pid;
355 info.tid = reply->tid;
356 info.proc = wine_server_get_ptr( reply->proc );
357 info.next_unicode = reply->unicode;
360 SERVER_END_REQ;
362 info.code = code;
363 info.wparam = wparam;
364 info.lparam = lparam;
365 info.prev_unicode = thread_info->hook_unicode;
366 return call_hook( &info, module );
369 LRESULT call_current_hook( HHOOK hhook, INT code, WPARAM wparam, LPARAM lparam )
371 struct win_hook_params info;
372 WCHAR module[MAX_PATH];
374 memset( &info, 0, sizeof(info) );
376 SERVER_START_REQ( get_hook_info )
378 req->handle = wine_server_user_handle( hhook );
379 req->get_next = 0;
380 req->event = EVENT_MIN;
381 wine_server_set_reply( req, module, sizeof(module) );
382 if (!wine_server_call_err( req ))
384 module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0;
385 info.handle = wine_server_ptr_handle( reply->handle );
386 info.id = reply->id;
387 info.pid = reply->pid;
388 info.tid = reply->tid;
389 info.proc = wine_server_get_ptr( reply->proc );
390 info.next_unicode = reply->unicode;
393 SERVER_END_REQ;
395 info.code = code;
396 info.wparam = wparam;
397 info.lparam = lparam;
398 info.prev_unicode = TRUE; /* assume Unicode for this function */
399 return call_hook( &info, module );
402 LRESULT call_hooks( INT id, INT code, WPARAM wparam, LPARAM lparam, size_t lparam_size )
404 struct user_thread_info *thread_info = get_user_thread_info();
405 struct win_hook_params info;
406 WCHAR module[MAX_PATH];
407 DWORD_PTR ret;
409 user_check_not_lock();
411 if (!is_hooked( id ))
413 TRACE( "skipping hook %s mask %x\n", hook_names[id-WH_MINHOOK], thread_info->active_hooks );
414 return 0;
417 memset( &info, 0, sizeof(info) );
418 info.prev_unicode = TRUE;
419 info.id = id;
421 SERVER_START_REQ( start_hook_chain )
423 req->id = info.id;
424 req->event = EVENT_MIN;
425 wine_server_set_reply( req, module, sizeof(module)-sizeof(WCHAR) );
426 if (!wine_server_call( req ))
428 module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0;
429 info.handle = wine_server_ptr_handle( reply->handle );
430 info.pid = reply->pid;
431 info.tid = reply->tid;
432 info.proc = wine_server_get_ptr( reply->proc );
433 info.next_unicode = reply->unicode;
434 thread_info->active_hooks = reply->active_hooks;
437 SERVER_END_REQ;
438 if (!info.tid && !info.proc) return 0;
440 info.code = code;
441 info.wparam = wparam;
442 info.lparam = lparam;
443 info.lparam_size = lparam_size;
444 ret = call_hook( &info, module );
446 SERVER_START_REQ( finish_hook_chain )
448 req->id = id;
449 wine_server_call( req );
451 SERVER_END_REQ;
452 return ret;
455 /***********************************************************************
456 * NtUserSetWinEventHook (win32u.@)
458 HWINEVENTHOOK WINAPI NtUserSetWinEventHook( DWORD event_min, DWORD event_max, HMODULE inst,
459 UNICODE_STRING *module, WINEVENTPROC proc,
460 DWORD pid, DWORD tid, DWORD flags )
462 HWINEVENTHOOK handle = 0;
464 if ((flags & WINEVENT_INCONTEXT) && !inst)
466 RtlSetLastWin32Error(ERROR_HOOK_NEEDS_HMOD);
467 return 0;
470 if (event_min > event_max)
472 RtlSetLastWin32Error(ERROR_INVALID_HOOK_FILTER);
473 return 0;
476 /* FIXME: what if the tid or pid belongs to another process? */
477 if (tid) inst = 0; /* thread-local hook */
479 SERVER_START_REQ( set_hook )
481 req->id = WH_WINEVENT;
482 req->pid = pid;
483 req->tid = tid;
484 req->event_min = event_min;
485 req->event_max = event_max;
486 req->flags = flags;
487 req->unicode = 1;
488 if (inst) /* make proc relative to the module base */
490 req->proc = wine_server_client_ptr( (void *)((char *)proc - (char *)inst) );
491 wine_server_add_data( req, module->Buffer, module->Length );
493 else req->proc = wine_server_client_ptr( proc );
495 if (!wine_server_call_err( req ))
497 handle = wine_server_ptr_handle( reply->handle );
498 get_user_thread_info()->active_hooks = reply->active_hooks;
501 SERVER_END_REQ;
503 TRACE("-> %p\n", handle);
504 return handle;
507 /***********************************************************************
508 * NtUserUnhookWinEvent (win32u.@)
510 BOOL WINAPI NtUserUnhookWinEvent( HWINEVENTHOOK handle )
512 BOOL ret;
514 SERVER_START_REQ( remove_hook )
516 req->handle = wine_server_user_handle( handle );
517 req->id = WH_WINEVENT;
518 ret = !wine_server_call_err( req );
519 if (ret) get_user_thread_info()->active_hooks = reply->active_hooks;
521 SERVER_END_REQ;
522 return ret;
525 /***********************************************************************
526 * NtUserNotifyWinEvent (win32u.@)
528 void WINAPI NtUserNotifyWinEvent( DWORD event, HWND hwnd, LONG object_id, LONG child_id )
530 struct user_thread_info *thread_info = get_user_thread_info();
531 struct win_event_hook_params info;
532 void *ret_ptr;
533 ULONG ret_len;
534 BOOL ret;
536 TRACE( "%04x, %p, %d, %d\n", (int)event, hwnd, (int)object_id, (int)child_id );
538 user_check_not_lock();
540 if (!hwnd)
542 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
543 return;
546 if (!is_hooked( WH_WINEVENT ))
548 TRACE( "skipping hook mask %x\n", thread_info->active_hooks );
549 return;
552 info.event = event;
553 info.hwnd = hwnd;
554 info.object_id = object_id;
555 info.child_id = child_id;
556 info.tid = GetCurrentThreadId();
558 SERVER_START_REQ( start_hook_chain )
560 req->id = WH_WINEVENT;
561 req->event = event;
562 req->window = wine_server_user_handle( hwnd );
563 req->object_id = object_id;
564 req->child_id = child_id;
565 wine_server_set_reply( req, info.module, sizeof(info.module) - sizeof(WCHAR) );
566 ret = !wine_server_call( req ) && reply->proc;
567 if (ret)
569 info.module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0;
570 info.handle = wine_server_ptr_handle( reply->handle );
571 info.proc = wine_server_get_ptr( reply->proc );
572 thread_info->active_hooks = reply->active_hooks;
575 SERVER_END_REQ;
576 if (!ret) return;
580 TRACE( "calling WH_WINEVENT hook %p event %x hwnd %p %x %x module %s\n",
581 info.proc, (int)event, hwnd, (int)object_id, (int)child_id, debugstr_w(info.module) );
583 info.time = NtGetTickCount();
584 KeUserModeCallback( NtUserCallWinEventHook, &info,
585 FIELD_OFFSET( struct win_event_hook_params, module[lstrlenW(info.module) + 1] ),
586 &ret_ptr, &ret_len );
588 SERVER_START_REQ( get_hook_info )
590 req->handle = wine_server_user_handle( info.handle );
591 req->get_next = 1;
592 req->event = event;
593 req->window = wine_server_user_handle( hwnd );
594 req->object_id = object_id;
595 req->child_id = child_id;
596 wine_server_set_reply( req, info.module, sizeof(info.module) - sizeof(WCHAR) );
597 ret = !wine_server_call( req ) && reply->proc;
598 if (ret)
600 info.module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0;
601 info.handle = wine_server_ptr_handle( reply->handle );
602 info.proc = wine_server_get_ptr( reply->proc );
605 SERVER_END_REQ;
607 while (ret);
609 SERVER_START_REQ( finish_hook_chain )
611 req->id = WH_WINEVENT;
612 wine_server_call( req );
614 SERVER_END_REQ;