comctl32/tests: Use CRT allocation functions.
[wine.git] / dlls / user32 / hook.c
blobb2e213f42feaf95ebc31eb0e0d1b400d32166197
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
21 * NOTES:
22 * Status of the various hooks:
23 * WH_MSGFILTER OK
24 * WH_JOURNALRECORD Partially implemented
25 * WH_JOURNALPLAYBACK Partially implemented
26 * WH_KEYBOARD OK
27 * WH_GETMESSAGE OK (FIXME: A/W mapping?)
28 * WH_CALLWNDPROC OK (FIXME: A/W mapping?)
29 * WH_CBT
30 * HCBT_MOVESIZE OK
31 * HCBT_MINMAX OK
32 * HCBT_QS OK
33 * HCBT_CREATEWND OK
34 * HCBT_DESTROYWND OK
35 * HCBT_ACTIVATE OK
36 * HCBT_CLICKSKIPPED OK
37 * HCBT_KEYSKIPPED OK
38 * HCBT_SYSCOMMAND OK
39 * HCBT_SETFOCUS OK
40 * WH_SYSMSGFILTER OK
41 * WH_MOUSE OK
42 * WH_HARDWARE Not supported in Win32
43 * WH_DEBUG Not implemented
44 * WH_SHELL
45 * HSHELL_WINDOWCREATED OK
46 * HSHELL_WINDOWDESTROYED OK
47 * HSHELL_ACTIVATESHELLWINDOW Not implemented
48 * HSHELL_WINDOWACTIVATED Not implemented
49 * HSHELL_GETMINRECT Not implemented
50 * HSHELL_REDRAW Not implemented
51 * HSHELL_TASKMAN Not implemented
52 * HSHELL_LANGUAGE Not implemented
53 * HSHELL_SYSMENU Not implemented
54 * HSHELL_ENDTASK Not implemented
55 * HSHELL_ACCESSIBILITYSTATE Not implemented
56 * HSHELL_APPCOMMAND Not implemented
57 * HSHELL_WINDOWREPLACED Not implemented
58 * HSHELL_WINDOWREPLACING Not implemented
59 * WH_FOREGROUNDIDLE Not implemented
60 * WH_CALLWNDPROCRET OK (FIXME: A/W mapping?)
61 * WH_KEYBOARD_LL Implemented but should use SendMessage instead
62 * WH_MOUSE_LL Implemented but should use SendMessage instead
65 #include "user_private.h"
66 #include "wine/asm.h"
67 #include "wine/debug.h"
69 WINE_DEFAULT_DEBUG_CHANNEL(hook);
70 WINE_DECLARE_DEBUG_CHANNEL(relay);
72 static struct user_api_hook original_user_api =
74 USER_DefDlgProc,
75 USER_NonClientButtonDraw,
76 USER_ScrollBarDraw,
77 USER_ScrollBarProc,
79 static struct user_api_hook hooked_user_api;
80 struct user_api_hook *user_api = &original_user_api;
82 struct hook_info
84 INT id;
85 void *proc;
86 void *handle;
87 DWORD pid, tid;
88 BOOL prev_unicode, next_unicode;
89 WCHAR module[MAX_PATH];
92 static CRITICAL_SECTION api_hook_cs;
93 static CRITICAL_SECTION_DEBUG critsect_debug =
95 0, 0, &api_hook_cs,
96 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
97 0, 0, { (DWORD_PTR)(__FILE__ ": api_hook_cs") }
99 static CRITICAL_SECTION api_hook_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
102 #define WH_WINEVENT (WH_MAXHOOK+1)
104 static const char * const hook_names[WH_WINEVENT - WH_MINHOOK + 1] =
106 "WH_MSGFILTER",
107 "WH_JOURNALRECORD",
108 "WH_JOURNALPLAYBACK",
109 "WH_KEYBOARD",
110 "WH_GETMESSAGE",
111 "WH_CALLWNDPROC",
112 "WH_CBT",
113 "WH_SYSMSGFILTER",
114 "WH_MOUSE",
115 "WH_HARDWARE",
116 "WH_DEBUG",
117 "WH_SHELL",
118 "WH_FOREGROUNDIDLE",
119 "WH_CALLWNDPROCRET",
120 "WH_KEYBOARD_LL",
121 "WH_MOUSE_LL",
122 "WH_WINEVENT"
126 /***********************************************************************
127 * set_windows_hook
129 * Implementation of SetWindowsHookExA and SetWindowsHookExW.
131 static HHOOK set_windows_hook( INT id, HOOKPROC proc, HINSTANCE inst, DWORD tid, BOOL ansi )
133 WCHAR module[MAX_PATH];
134 UNICODE_STRING str;
136 if (!inst)
138 RtlInitUnicodeString( &str, NULL );
140 else
142 size_t len = GetModuleFileNameW( inst, module, ARRAYSIZE(module) );
143 if (!len || len >= ARRAYSIZE(module))
145 SetLastError( ERROR_INVALID_PARAMETER );
146 return 0;
148 str.Buffer = module;
149 str.MaximumLength = str.Length = len * sizeof(WCHAR);
152 return NtUserSetWindowsHookEx( inst, &str, tid, id, proc, ansi );
155 #ifdef __i386__
156 /* Some apps pass a non-stdcall proc to SetWindowsHookExA,
157 * so we need a small assembly wrapper to call the proc.
159 extern LRESULT HOOKPROC_wrapper( HOOKPROC proc,
160 INT code, WPARAM wParam, LPARAM lParam );
161 __ASM_GLOBAL_FUNC( HOOKPROC_wrapper,
162 "pushl %ebp\n\t"
163 __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
164 __ASM_CFI(".cfi_rel_offset %ebp,0\n\t")
165 "movl %esp,%ebp\n\t"
166 __ASM_CFI(".cfi_def_cfa_register %ebp\n\t")
167 "pushl %edi\n\t"
168 __ASM_CFI(".cfi_rel_offset %edi,-4\n\t")
169 "pushl %esi\n\t"
170 __ASM_CFI(".cfi_rel_offset %esi,-8\n\t")
171 "pushl %ebx\n\t"
172 __ASM_CFI(".cfi_rel_offset %ebx,-12\n\t")
173 "pushl 20(%ebp)\n\t"
174 "pushl 16(%ebp)\n\t"
175 "pushl 12(%ebp)\n\t"
176 "movl 8(%ebp),%eax\n\t"
177 "call *%eax\n\t"
178 "leal -12(%ebp),%esp\n\t"
179 "popl %ebx\n\t"
180 __ASM_CFI(".cfi_same_value %ebx\n\t")
181 "popl %esi\n\t"
182 __ASM_CFI(".cfi_same_value %esi\n\t")
183 "popl %edi\n\t"
184 __ASM_CFI(".cfi_same_value %edi\n\t")
185 "leave\n\t"
186 __ASM_CFI(".cfi_def_cfa %esp,4\n\t")
187 __ASM_CFI(".cfi_same_value %ebp\n\t")
188 "ret" )
189 #else
190 static inline LRESULT HOOKPROC_wrapper( HOOKPROC proc,
191 INT code, WPARAM wParam, LPARAM lParam )
193 return proc( code, wParam, lParam );
195 #endif /* __i386__ */
198 /***********************************************************************
199 * call_hook_AtoW
201 static LRESULT call_hook_AtoW( HOOKPROC proc, INT id, INT code, WPARAM wparam, LPARAM lparam )
203 LRESULT ret;
204 UNICODE_STRING usBuffer;
205 if (id != WH_CBT || code != HCBT_CREATEWND)
206 ret = HOOKPROC_wrapper( proc, code, wparam, lparam );
207 else
209 CBT_CREATEWNDA *cbtcwA = (CBT_CREATEWNDA *)lparam;
210 CBT_CREATEWNDW cbtcwW;
211 CREATESTRUCTW csW;
212 LPWSTR nameW = NULL;
213 LPWSTR classW = NULL;
214 WCHAR name_buf[3];
216 cbtcwW.lpcs = &csW;
217 cbtcwW.hwndInsertAfter = cbtcwA->hwndInsertAfter;
218 csW = *(CREATESTRUCTW *)cbtcwA->lpcs;
220 if (!IS_INTRESOURCE(cbtcwA->lpcs->lpszName))
222 if (cbtcwA->lpcs->lpszName[0] != '\xff')
224 RtlCreateUnicodeStringFromAsciiz( &usBuffer, cbtcwA->lpcs->lpszName );
225 csW.lpszName = nameW = usBuffer.Buffer;
227 else
229 name_buf[0] = 0xffff;
230 name_buf[1] = MAKEWORD( cbtcwA->lpcs->lpszName[1], cbtcwA->lpcs->lpszName[2] );
231 name_buf[2] = 0;
232 csW.lpszName = name_buf;
235 if (!IS_INTRESOURCE(cbtcwA->lpcs->lpszClass))
237 RtlCreateUnicodeStringFromAsciiz(&usBuffer,cbtcwA->lpcs->lpszClass);
238 csW.lpszClass = classW = usBuffer.Buffer;
240 ret = HOOKPROC_wrapper( proc, code, wparam, (LPARAM)&cbtcwW );
241 cbtcwA->hwndInsertAfter = cbtcwW.hwndInsertAfter;
242 HeapFree( GetProcessHeap(), 0, nameW );
243 HeapFree( GetProcessHeap(), 0, classW );
245 return ret;
249 /***********************************************************************
250 * call_hook_WtoA
252 static LRESULT call_hook_WtoA( HOOKPROC proc, INT id, INT code, WPARAM wparam, LPARAM lparam )
254 LRESULT ret;
256 if (id != WH_CBT || code != HCBT_CREATEWND)
257 ret = HOOKPROC_wrapper( proc, code, wparam, lparam );
258 else
260 CBT_CREATEWNDW *cbtcwW = (CBT_CREATEWNDW *)lparam;
261 CBT_CREATEWNDA cbtcwA;
262 CREATESTRUCTA csA;
263 int len;
264 LPSTR nameA = NULL;
265 LPSTR classA = NULL;
266 char name_buf[4];
268 cbtcwA.lpcs = &csA;
269 cbtcwA.hwndInsertAfter = cbtcwW->hwndInsertAfter;
270 csA = *(CREATESTRUCTA *)cbtcwW->lpcs;
272 if (!IS_INTRESOURCE(cbtcwW->lpcs->lpszName))
274 if (cbtcwW->lpcs->lpszName[0] != 0xffff)
276 len = WideCharToMultiByte( CP_ACP, 0, cbtcwW->lpcs->lpszName, -1, NULL, 0, NULL, NULL );
277 nameA = HeapAlloc( GetProcessHeap(), 0, len*sizeof(CHAR) );
278 WideCharToMultiByte( CP_ACP, 0, cbtcwW->lpcs->lpszName, -1, nameA, len, NULL, NULL );
279 csA.lpszName = nameA;
281 else
283 name_buf[0] = '\xff';
284 name_buf[1] = cbtcwW->lpcs->lpszName[1];
285 name_buf[2] = cbtcwW->lpcs->lpszName[1] >> 8;
286 name_buf[3] = 0;
287 csA.lpszName = name_buf;
291 if (!IS_INTRESOURCE(cbtcwW->lpcs->lpszClass)) {
292 len = WideCharToMultiByte( CP_ACP, 0, cbtcwW->lpcs->lpszClass, -1, NULL, 0, NULL, NULL );
293 classA = HeapAlloc( GetProcessHeap(), 0, len*sizeof(CHAR) );
294 WideCharToMultiByte( CP_ACP, 0, cbtcwW->lpcs->lpszClass, -1, classA, len, NULL, NULL );
295 csA.lpszClass = classA;
298 ret = HOOKPROC_wrapper( proc, code, wparam, (LPARAM)&cbtcwA );
299 cbtcwW->hwndInsertAfter = cbtcwA.hwndInsertAfter;
300 HeapFree( GetProcessHeap(), 0, nameA );
301 HeapFree( GetProcessHeap(), 0, classA );
303 return ret;
307 /***********************************************************************
308 * call_hook_proc
310 static LRESULT call_hook_proc( HOOKPROC proc, INT id, INT code, WPARAM wparam, LPARAM lparam,
311 BOOL prev_unicode, BOOL next_unicode )
313 LRESULT ret;
315 TRACE_(relay)( "\1Call hook proc %p (id=%s,code=%x,wp=%08Ix,lp=%08Ix)\n",
316 proc, hook_names[id-WH_MINHOOK], code, wparam, lparam );
318 if (!prev_unicode == !next_unicode) ret = proc( code, wparam, lparam );
319 else if (prev_unicode) ret = call_hook_WtoA( proc, id, code, wparam, lparam );
320 else ret = call_hook_AtoW( proc, id, code, wparam, lparam );
322 TRACE_(relay)( "\1Ret hook proc %p (id=%s,code=%x,wp=%08Ix,lp=%08Ix) retval=%08Ix\n",
323 proc, hook_names[id-WH_MINHOOK], code, wparam, lparam, ret );
325 return ret;
329 /***********************************************************************
330 * get_hook_proc
332 * Retrieve the hook procedure real value for a module-relative proc
334 void *get_hook_proc( void *proc, const WCHAR *module, HMODULE *free_module )
336 HMODULE mod;
338 GetModuleHandleExW( 0, module, &mod );
339 *free_module = mod;
340 if (!mod)
342 TRACE( "loading %s\n", debugstr_w(module) );
343 /* FIXME: the library will never be freed */
344 if (!(mod = LoadLibraryExW(module, NULL, LOAD_WITH_ALTERED_SEARCH_PATH))) return NULL;
346 return (char *)mod + (ULONG_PTR)proc;
350 /***********************************************************************
351 * SetWindowsHookA (USER32.@)
353 HHOOK WINAPI SetWindowsHookA( INT id, HOOKPROC proc )
355 return SetWindowsHookExA( id, proc, 0, GetCurrentThreadId() );
359 /***********************************************************************
360 * SetWindowsHookW (USER32.@)
362 HHOOK WINAPI SetWindowsHookW( INT id, HOOKPROC proc )
364 return SetWindowsHookExW( id, proc, 0, GetCurrentThreadId() );
368 /***********************************************************************
369 * SetWindowsHookExA (USER32.@)
371 HHOOK WINAPI SetWindowsHookExA( INT id, HOOKPROC proc, HINSTANCE inst, DWORD tid )
373 return set_windows_hook( id, proc, inst, tid, TRUE );
376 /***********************************************************************
377 * SetWindowsHookExW (USER32.@)
379 HHOOK WINAPI SetWindowsHookExW( INT id, HOOKPROC proc, HINSTANCE inst, DWORD tid )
381 return set_windows_hook( id, proc, inst, tid, FALSE );
385 /***********************************************************************
386 * UnhookWindowsHook (USER32.@)
388 BOOL WINAPI UnhookWindowsHook( INT id, HOOKPROC proc )
390 return NtUserUnhookWindowsHook( id, proc );
394 /***********************************************************************
395 * SetWinEventHook [USER32.@]
397 * Set up an event hook for a set of events.
399 * PARAMS
400 * event_min [I] Lowest event handled by pfnProc
401 * event_max [I] Highest event handled by pfnProc
402 * inst [I] DLL containing pfnProc
403 * proc [I] Callback event hook function
404 * pid [I] Process to get events from, or 0 for all processes
405 * tid [I] Thread to get events from, or 0 for all threads
406 * flags [I] Flags indicating the status of pfnProc
408 * RETURNS
409 * Success: A handle representing the hook.
410 * Failure: A NULL handle.
412 HWINEVENTHOOK WINAPI SetWinEventHook(DWORD event_min, DWORD event_max,
413 HMODULE inst, WINEVENTPROC proc,
414 DWORD pid, DWORD tid, DWORD flags)
416 WCHAR module[MAX_PATH];
417 UNICODE_STRING str;
418 DWORD len = 0;
420 TRACE("%ld,%ld,%p,%p,%08lx,%04lx,%08lx\n", event_min, event_max, inst,
421 proc, pid, tid, flags);
423 if (inst && (!(len = GetModuleFileNameW( inst, module, MAX_PATH )) || len >= MAX_PATH))
425 inst = 0;
426 len = 0;
428 str.Buffer = module;
429 str.Length = str.MaximumLength = len * sizeof(WCHAR);
430 return NtUserSetWinEventHook( event_min, event_max, inst, &str, proc, pid, tid, flags );
433 BOOL WINAPI User32CallWinEventHook( const struct win_event_hook_params *params, ULONG size )
435 WINEVENTPROC proc = params->proc;
436 HMODULE free_module = 0;
438 if (params->module[0] && !(proc = get_hook_proc( proc, params->module, &free_module ))) return FALSE;
440 TRACE_(relay)( "\1Call winevent hook proc %p (hhook=%p,event=%lx,hwnd=%p,object_id=%lx,child_id=%lx,tid=%04lx,time=%lx)\n",
441 proc, params->handle, params->event, params->hwnd, params->object_id,
442 params->child_id, params->tid, params->time );
444 proc( params->handle, params->event, params->hwnd, params->object_id, params->child_id,
445 params->tid, params->time );
447 TRACE_(relay)( "\1Ret winevent hook proc %p (hhook=%p,event=%lx,hwnd=%p,object_id=%lx,child_id=%lx,tid=%04lx,time=%lx)\n",
448 proc, params->handle, params->event, params->hwnd, params->object_id,
449 params->child_id, params->tid, params->time );
451 if (free_module) FreeLibrary( free_module );
452 return TRUE;
455 BOOL WINAPI User32CallWindowsHook( struct win_hook_params *params, ULONG size )
457 HOOKPROC proc = params->proc;
458 HMODULE free_module = 0;
459 void *ret_ptr = NULL;
460 CBT_CREATEWNDW cbtc;
461 UINT ret_size = 0;
462 size_t lparam_offset;
463 LRESULT ret;
465 lparam_offset = FIELD_OFFSET( struct win_hook_params, module[wcslen( params->module ) + 1]);
467 if (lparam_offset < size)
469 lparam_offset = (lparam_offset + 15) & ~15; /* align */
470 ret_size = size - lparam_offset;
471 ret_ptr = (char *)params + lparam_offset;
472 params->lparam = (LPARAM)ret_ptr;
474 switch (params->id)
476 case WH_CBT:
477 if (params->code == HCBT_CREATEWND)
479 cbtc.hwndInsertAfter = HWND_TOP;
480 unpack_message( (HWND)params->wparam, WM_CREATE, NULL, (LPARAM *)&cbtc.lpcs,
481 ret_ptr, ret_size, FALSE );
482 params->lparam = (LPARAM)&cbtc;
483 ret_size = sizeof(*cbtc.lpcs);
485 break;
486 case WH_CALLWNDPROC:
487 if (ret_size > sizeof(CWPSTRUCT))
489 CWPSTRUCT *cwp = (CWPSTRUCT *)params->lparam;
490 size_t offset = (lparam_offset + sizeof(*cwp) + 15) & ~15;
492 unpack_message( cwp->hwnd, cwp->message, &cwp->wParam, &cwp->lParam,
493 (char *)params + offset, size - offset, !params->prev_unicode );
494 ret_size = 0;
495 break;
497 case WH_CALLWNDPROCRET:
498 if (ret_size > sizeof(CWPRETSTRUCT))
500 CWPRETSTRUCT *cwpret = (CWPRETSTRUCT *)params->lparam;
501 size_t offset = (lparam_offset + sizeof(*cwpret) + 15) & ~15;
503 unpack_message( cwpret->hwnd, cwpret->message, &cwpret->wParam, &cwpret->lParam,
504 (char *)params + offset, size - offset, !params->prev_unicode );
505 ret_size = 0;
506 break;
510 if (params->module[0] && !(proc = get_hook_proc( proc, params->module, &free_module )))
511 return FALSE;
513 ret = call_hook_proc( proc, params->id, params->code, params->wparam, params->lparam,
514 params->prev_unicode, params->next_unicode );
516 if (free_module) FreeLibrary( free_module );
517 return NtCallbackReturn( ret_ptr, ret_size, ret );
520 /***********************************************************************
521 * IsWinEventHookInstalled [USER32.@]
523 * Determine if an event hook is installed for an event.
525 * PARAMS
526 * dwEvent [I] Id of the event
528 * RETURNS
529 * TRUE, If there are any hooks installed for the event.
530 * FALSE, Otherwise.
532 * BUGS
533 * Not implemented.
535 BOOL WINAPI IsWinEventHookInstalled(DWORD dwEvent)
537 /* FIXME: Needed by Office 2007 installer */
538 WARN("(%ld)-stub!\n", dwEvent);
539 return TRUE;
542 /* Undocumented RegisterUserApiHook() */
543 BOOL WINAPI RegisterUserApiHook(const struct user_api_hook *new_hook, struct user_api_hook *old_hook)
545 if (!new_hook)
546 return FALSE;
548 EnterCriticalSection( &api_hook_cs );
549 hooked_user_api = *new_hook;
550 user_api = &hooked_user_api;
551 if (old_hook)
552 *old_hook = original_user_api;
553 LeaveCriticalSection( &api_hook_cs );
554 return TRUE;
557 /* Undocumented UnregisterUserApiHook() */
558 void WINAPI UnregisterUserApiHook(void)
560 InterlockedExchangePointer((void **)&user_api, &original_user_api);