ws2_32: Return a valid value for WSAIoctl SIO_IDEAL_SEND_BACKLOG_QUERY.
[wine.git] / dlls / win32u / hook.c
blob666e2a844df164aeadc0e40488d00bc324d2a8e7
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, 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 info->tid, hook_names[info->id-WH_MINHOOK], info->code, info->wparam, info->lparam );
211 switch(info->id)
213 case WH_KEYBOARD_LL:
214 send_internal_message_timeout( info->pid, info->tid, WM_WINE_KEYBOARD_LL_HOOK,
215 info->wparam, (LPARAM)&h_extra, SMTO_ABORTIFHUNG,
216 get_ll_hook_timeout(), &ret );
217 break;
218 case WH_MOUSE_LL:
219 send_internal_message_timeout( info->pid, info->tid, WM_WINE_MOUSE_LL_HOOK,
220 info->wparam, (LPARAM)&h_extra, SMTO_ABORTIFHUNG,
221 get_ll_hook_timeout(), &ret );
222 break;
223 default:
224 ERR("Unknown hook id %d\n", info->id);
225 assert(0);
226 break;
229 else if (info->proc)
231 struct user_thread_info *thread_info = get_user_thread_info();
232 HHOOK prev = thread_info->hook;
233 BOOL prev_unicode = thread_info->hook_unicode;
234 struct win_hook_params *params = info;
235 ULONG size = sizeof(*params);
236 ULONG lparam_ret_size = params->lparam_size;
237 CREATESTRUCTW *cs = NULL;
238 void *ret_ptr;
239 ULONG ret_len;
241 if (params->lparam_size)
243 lparam_ret_size = params->lparam_size;
245 if (params->id == WH_CBT && params->code == HCBT_CREATEWND)
247 cs = ((CBT_CREATEWNDW *)params->lparam)->lpcs;
248 params->lparam = 0;
249 lparam_ret_size = 0;
250 params->lparam_size = sizeof(*cs);
251 if (!IS_INTRESOURCE( cs->lpszName ))
252 params->lparam_size += (wcslen( cs->lpszName ) + 1) * sizeof(WCHAR);
253 if (!IS_INTRESOURCE( cs->lpszClass ))
254 params->lparam_size += (wcslen( cs->lpszClass ) + 1) * sizeof(WCHAR);
257 size += params->lparam_size;
260 if (module && module[0]) size += (lstrlenW( module ) + 1) * sizeof(WCHAR);
261 if (size != sizeof(*params))
263 if (!(params = malloc( size ))) return 0;
264 *params = *info;
267 if (params->lparam_size)
269 if (cs)
271 CREATESTRUCTW *params_cs = (CREATESTRUCTW *)(params + 1);
272 WCHAR *ptr = (WCHAR *)(params_cs + 1);
273 const void *inline_ptr = (void *)0xffffffff;
275 *params_cs = *cs;
276 if (!IS_INTRESOURCE( cs->lpszName ))
278 UINT len = wcslen( cs->lpszName ) + 1;
279 memcpy( ptr, cs->lpszName, len * sizeof(WCHAR) );
280 ptr += len;
281 params_cs->lpszName = inline_ptr;
283 if (!IS_INTRESOURCE( cs->lpszClass ))
285 wcscpy( ptr, cs->lpszClass );
286 params_cs->lpszClass = inline_ptr;
289 else
291 memcpy( params + 1, (const void *)params->lparam, params->lparam_size );
295 if (module && module[0])
296 wcscpy( (WCHAR *)((char *)(params + 1) + params->lparam_size), module );
299 * Windows protects from stack overflow in recursive hook calls. Different Windows
300 * allow different depths.
302 if (thread_info->hook_call_depth >= 25)
304 WARN("Too many hooks called recursively, skipping call.\n");
305 if (params != info) free( params );
306 return 0;
309 TRACE( "calling hook %p %s code %x wp %lx lp %lx module %s\n",
310 params->proc, hook_names[params->id-WH_MINHOOK], params->code, params->wparam,
311 params->lparam, debugstr_w(module) );
313 thread_info->hook = params->handle;
314 thread_info->hook_unicode = params->next_unicode;
315 thread_info->hook_call_depth++;
316 ret = KeUserModeCallback( NtUserCallWindowsHook, params, size, &ret_ptr, &ret_len );
317 if (ret_len && ret_len == lparam_ret_size)
318 memcpy( (void *)params->lparam, ret_ptr, lparam_ret_size );
319 thread_info->hook = prev;
320 thread_info->hook_unicode = prev_unicode;
321 thread_info->hook_call_depth--;
323 if (params != info) free( params );
326 if (info->id == WH_KEYBOARD_LL || info->id == WH_MOUSE_LL)
327 InterlockedIncrement( &global_key_state_counter ); /* force refreshing the key state cache */
328 return ret;
331 /***********************************************************************
332 * NtUserCallNextHookEx (win32u.@)
334 LRESULT WINAPI NtUserCallNextHookEx( HHOOK hhook, INT code, WPARAM wparam, LPARAM lparam )
336 struct user_thread_info *thread_info = get_user_thread_info();
337 struct win_hook_params info;
338 WCHAR module[MAX_PATH];
340 memset( &info, 0, sizeof(info) );
342 SERVER_START_REQ( get_hook_info )
344 req->handle = wine_server_user_handle( thread_info->hook );
345 req->get_next = 1;
346 req->event = EVENT_MIN;
347 wine_server_set_reply( req, module, sizeof(module) - sizeof(WCHAR) );
348 if (!wine_server_call_err( req ))
350 module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0;
351 info.handle = wine_server_ptr_handle( reply->handle );
352 info.id = reply->id;
353 info.pid = reply->pid;
354 info.tid = reply->tid;
355 info.proc = wine_server_get_ptr( reply->proc );
356 info.next_unicode = reply->unicode;
359 SERVER_END_REQ;
361 info.code = code;
362 info.wparam = wparam;
363 info.lparam = lparam;
364 info.prev_unicode = thread_info->hook_unicode;
365 return call_hook( &info, module );
368 LRESULT call_current_hook( HHOOK hhook, INT code, WPARAM wparam, LPARAM lparam )
370 struct win_hook_params info;
371 WCHAR module[MAX_PATH];
373 memset( &info, 0, sizeof(info) );
375 SERVER_START_REQ( get_hook_info )
377 req->handle = wine_server_user_handle( hhook );
378 req->get_next = 0;
379 req->event = EVENT_MIN;
380 wine_server_set_reply( req, module, sizeof(module) );
381 if (!wine_server_call_err( req ))
383 module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0;
384 info.handle = wine_server_ptr_handle( reply->handle );
385 info.id = reply->id;
386 info.pid = reply->pid;
387 info.tid = reply->tid;
388 info.proc = wine_server_get_ptr( reply->proc );
389 info.next_unicode = reply->unicode;
392 SERVER_END_REQ;
394 info.code = code;
395 info.wparam = wparam;
396 info.lparam = lparam;
397 info.prev_unicode = TRUE; /* assume Unicode for this function */
398 return call_hook( &info, module );
401 LRESULT call_hooks( INT id, INT code, WPARAM wparam, LPARAM lparam, size_t lparam_size )
403 struct user_thread_info *thread_info = get_user_thread_info();
404 struct win_hook_params info;
405 WCHAR module[MAX_PATH];
406 DWORD_PTR ret;
408 user_check_not_lock();
410 if (!is_hooked( id ))
412 TRACE( "skipping hook %s mask %x\n", hook_names[id-WH_MINHOOK], thread_info->active_hooks );
413 return 0;
416 memset( &info, 0, sizeof(info) );
417 info.prev_unicode = TRUE;
418 info.id = id;
420 SERVER_START_REQ( start_hook_chain )
422 req->id = info.id;
423 req->event = EVENT_MIN;
424 wine_server_set_reply( req, module, sizeof(module)-sizeof(WCHAR) );
425 if (!wine_server_call( req ))
427 module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0;
428 info.handle = wine_server_ptr_handle( reply->handle );
429 info.pid = reply->pid;
430 info.tid = reply->tid;
431 info.proc = wine_server_get_ptr( reply->proc );
432 info.next_unicode = reply->unicode;
433 thread_info->active_hooks = reply->active_hooks;
436 SERVER_END_REQ;
437 if (!info.tid && !info.proc) return 0;
439 info.code = code;
440 info.wparam = wparam;
441 info.lparam = lparam;
442 info.lparam_size = lparam_size;
443 ret = call_hook( &info, module );
445 SERVER_START_REQ( finish_hook_chain )
447 req->id = id;
448 wine_server_call( req );
450 SERVER_END_REQ;
451 return ret;
454 /***********************************************************************
455 * NtUserSetWinEventHook (win32u.@)
457 HWINEVENTHOOK WINAPI NtUserSetWinEventHook( DWORD event_min, DWORD event_max, HMODULE inst,
458 UNICODE_STRING *module, WINEVENTPROC proc,
459 DWORD pid, DWORD tid, DWORD flags )
461 HWINEVENTHOOK handle = 0;
463 if ((flags & WINEVENT_INCONTEXT) && !inst)
465 RtlSetLastWin32Error(ERROR_HOOK_NEEDS_HMOD);
466 return 0;
469 if (event_min > event_max)
471 RtlSetLastWin32Error(ERROR_INVALID_HOOK_FILTER);
472 return 0;
475 /* FIXME: what if the tid or pid belongs to another process? */
476 if (tid) inst = 0; /* thread-local hook */
478 SERVER_START_REQ( set_hook )
480 req->id = WH_WINEVENT;
481 req->pid = pid;
482 req->tid = tid;
483 req->event_min = event_min;
484 req->event_max = event_max;
485 req->flags = flags;
486 req->unicode = 1;
487 if (inst) /* make proc relative to the module base */
489 req->proc = wine_server_client_ptr( (void *)((char *)proc - (char *)inst) );
490 wine_server_add_data( req, module->Buffer, module->Length );
492 else req->proc = wine_server_client_ptr( proc );
494 if (!wine_server_call_err( req ))
496 handle = wine_server_ptr_handle( reply->handle );
497 get_user_thread_info()->active_hooks = reply->active_hooks;
500 SERVER_END_REQ;
502 TRACE("-> %p\n", handle);
503 return handle;
506 /***********************************************************************
507 * NtUserUnhookWinEvent (win32u.@)
509 BOOL WINAPI NtUserUnhookWinEvent( HWINEVENTHOOK handle )
511 BOOL ret;
513 SERVER_START_REQ( remove_hook )
515 req->handle = wine_server_user_handle( handle );
516 req->id = WH_WINEVENT;
517 ret = !wine_server_call_err( req );
518 if (ret) get_user_thread_info()->active_hooks = reply->active_hooks;
520 SERVER_END_REQ;
521 return ret;
524 /***********************************************************************
525 * NtUserNotifyWinEvent (win32u.@)
527 void WINAPI NtUserNotifyWinEvent( DWORD event, HWND hwnd, LONG object_id, LONG child_id )
529 struct user_thread_info *thread_info = get_user_thread_info();
530 struct win_event_hook_params info;
531 void *ret_ptr;
532 ULONG ret_len;
533 BOOL ret;
535 TRACE( "%04x, %p, %d, %d\n", event, hwnd, object_id, child_id );
537 user_check_not_lock();
539 if (!hwnd)
541 RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
542 return;
545 if (!is_hooked( WH_WINEVENT ))
547 TRACE( "skipping hook mask %x\n", thread_info->active_hooks );
548 return;
551 info.event = event;
552 info.hwnd = hwnd;
553 info.object_id = object_id;
554 info.child_id = child_id;
555 info.tid = GetCurrentThreadId();
557 SERVER_START_REQ( start_hook_chain )
559 req->id = WH_WINEVENT;
560 req->event = event;
561 req->window = wine_server_user_handle( hwnd );
562 req->object_id = object_id;
563 req->child_id = child_id;
564 wine_server_set_reply( req, info.module, sizeof(info.module) - sizeof(WCHAR) );
565 ret = !wine_server_call( req ) && reply->proc;
566 if (ret)
568 info.module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0;
569 info.handle = wine_server_ptr_handle( reply->handle );
570 info.proc = wine_server_get_ptr( reply->proc );
571 thread_info->active_hooks = reply->active_hooks;
574 SERVER_END_REQ;
575 if (!ret) return;
579 TRACE( "calling WH_WINEVENT hook %p event %x hwnd %p %x %x module %s\n",
580 info.proc, event, hwnd, object_id, child_id, debugstr_w(info.module) );
582 info.time = NtGetTickCount();
583 KeUserModeCallback( NtUserCallWinEventHook, &info,
584 FIELD_OFFSET( struct win_event_hook_params, module[lstrlenW(info.module) + 1] ),
585 &ret_ptr, &ret_len );
587 SERVER_START_REQ( get_hook_info )
589 req->handle = wine_server_user_handle( info.handle );
590 req->get_next = 1;
591 req->event = event;
592 req->window = wine_server_user_handle( hwnd );
593 req->object_id = object_id;
594 req->child_id = child_id;
595 wine_server_set_reply( req, info.module, sizeof(info.module) - sizeof(WCHAR) );
596 ret = !wine_server_call( req ) && reply->proc;
597 if (ret)
599 info.module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0;
600 info.handle = wine_server_ptr_handle( reply->handle );
601 info.proc = wine_server_get_ptr( reply->proc );
604 SERVER_END_REQ;
606 while (ret);
608 SERVER_START_REQ( finish_hook_chain )
610 req->id = WH_WINEVENT;
611 wine_server_call( req );
613 SERVER_END_REQ;