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 * Status of the various hooks:
24 * WH_JOURNALRECORD Partially implemented
25 * WH_JOURNALPLAYBACK Partially implemented
27 * WH_GETMESSAGE OK (FIXME: A/W mapping?)
28 * WH_CALLWNDPROC OK (FIXME: A/W mapping?)
36 * HCBT_CLICKSKIPPED OK
42 * WH_HARDWARE Not supported in Win32
43 * WH_DEBUG Not implemented
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
66 #define WIN32_NO_STATUS
67 #include "user_private.h"
69 #include "wine/debug.h"
71 WINE_DEFAULT_DEBUG_CHANNEL(hook
);
72 WINE_DECLARE_DEBUG_CHANNEL(relay
);
74 static struct user_api_hook original_user_api
=
77 USER_NonClientButtonDraw
,
81 static struct user_api_hook hooked_user_api
;
82 struct user_api_hook
*user_api
= &original_user_api
;
90 BOOL prev_unicode
, next_unicode
;
91 WCHAR module
[MAX_PATH
];
94 static CRITICAL_SECTION api_hook_cs
;
95 static CRITICAL_SECTION_DEBUG critsect_debug
=
98 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
99 0, 0, { (DWORD_PTR
)(__FILE__
": api_hook_cs") }
101 static CRITICAL_SECTION api_hook_cs
= { &critsect_debug
, -1, 0, 0, 0, 0 };
104 #define WH_WINEVENT (WH_MAXHOOK+1)
106 static const char * const hook_names
[WH_WINEVENT
- WH_MINHOOK
+ 1] =
110 "WH_JOURNALPLAYBACK",
128 /***********************************************************************
131 * Implementation of SetWindowsHookExA and SetWindowsHookExW.
133 static HHOOK
set_windows_hook( INT id
, HOOKPROC proc
, HINSTANCE inst
, DWORD tid
, BOOL ansi
)
135 WCHAR module
[MAX_PATH
];
140 RtlInitUnicodeString( &str
, NULL
);
144 size_t len
= GetModuleFileNameW( inst
, module
, ARRAYSIZE(module
) );
145 if (!len
|| len
>= ARRAYSIZE(module
))
147 SetLastError( ERROR_INVALID_PARAMETER
);
151 str
.MaximumLength
= str
.Length
= len
* sizeof(WCHAR
);
154 return NtUserSetWindowsHookEx( inst
, &str
, tid
, id
, proc
, ansi
);
158 /* Some apps pass a non-stdcall proc to SetWindowsHookExA,
159 * so we need a small assembly wrapper to call the proc.
161 extern LRESULT
HOOKPROC_wrapper( HOOKPROC proc
,
162 INT code
, WPARAM wParam
, LPARAM lParam
);
163 __ASM_GLOBAL_FUNC( HOOKPROC_wrapper
,
165 __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
166 __ASM_CFI(".cfi_rel_offset %ebp,0\n\t")
168 __ASM_CFI(".cfi_def_cfa_register %ebp\n\t")
170 __ASM_CFI(".cfi_rel_offset %edi,-4\n\t")
172 __ASM_CFI(".cfi_rel_offset %esi,-8\n\t")
174 __ASM_CFI(".cfi_rel_offset %ebx,-12\n\t")
178 "movl 8(%ebp),%eax\n\t"
180 "leal -12(%ebp),%esp\n\t"
182 __ASM_CFI(".cfi_same_value %ebx\n\t")
184 __ASM_CFI(".cfi_same_value %esi\n\t")
186 __ASM_CFI(".cfi_same_value %edi\n\t")
188 __ASM_CFI(".cfi_def_cfa %esp,4\n\t")
189 __ASM_CFI(".cfi_same_value %ebp\n\t")
192 static inline LRESULT
HOOKPROC_wrapper( HOOKPROC proc
,
193 INT code
, WPARAM wParam
, LPARAM lParam
)
195 return proc( code
, wParam
, lParam
);
197 #endif /* __i386__ */
200 /***********************************************************************
203 static LRESULT
call_hook_AtoW( HOOKPROC proc
, INT id
, INT code
, WPARAM wparam
, LPARAM lparam
)
206 UNICODE_STRING usBuffer
;
207 if (id
!= WH_CBT
|| code
!= HCBT_CREATEWND
)
208 ret
= HOOKPROC_wrapper( proc
, code
, wparam
, lparam
);
211 CBT_CREATEWNDA
*cbtcwA
= (CBT_CREATEWNDA
*)lparam
;
212 CBT_CREATEWNDW cbtcwW
;
215 LPWSTR classW
= NULL
;
219 cbtcwW
.hwndInsertAfter
= cbtcwA
->hwndInsertAfter
;
220 csW
= *(CREATESTRUCTW
*)cbtcwA
->lpcs
;
222 if (!IS_INTRESOURCE(cbtcwA
->lpcs
->lpszName
))
224 if (cbtcwA
->lpcs
->lpszName
[0] != '\xff')
226 RtlCreateUnicodeStringFromAsciiz( &usBuffer
, cbtcwA
->lpcs
->lpszName
);
227 csW
.lpszName
= nameW
= usBuffer
.Buffer
;
231 name_buf
[0] = 0xffff;
232 name_buf
[1] = MAKEWORD( cbtcwA
->lpcs
->lpszName
[1], cbtcwA
->lpcs
->lpszName
[2] );
234 csW
.lpszName
= name_buf
;
237 if (!IS_INTRESOURCE(cbtcwA
->lpcs
->lpszClass
))
239 RtlCreateUnicodeStringFromAsciiz(&usBuffer
,cbtcwA
->lpcs
->lpszClass
);
240 csW
.lpszClass
= classW
= usBuffer
.Buffer
;
242 ret
= HOOKPROC_wrapper( proc
, code
, wparam
, (LPARAM
)&cbtcwW
);
243 cbtcwA
->hwndInsertAfter
= cbtcwW
.hwndInsertAfter
;
244 HeapFree( GetProcessHeap(), 0, nameW
);
245 HeapFree( GetProcessHeap(), 0, classW
);
251 /***********************************************************************
254 static LRESULT
call_hook_WtoA( HOOKPROC proc
, INT id
, INT code
, WPARAM wparam
, LPARAM lparam
)
258 if (id
!= WH_CBT
|| code
!= HCBT_CREATEWND
)
259 ret
= HOOKPROC_wrapper( proc
, code
, wparam
, lparam
);
262 CBT_CREATEWNDW
*cbtcwW
= (CBT_CREATEWNDW
*)lparam
;
263 CBT_CREATEWNDA cbtcwA
;
271 cbtcwA
.hwndInsertAfter
= cbtcwW
->hwndInsertAfter
;
272 csA
= *(CREATESTRUCTA
*)cbtcwW
->lpcs
;
274 if (!IS_INTRESOURCE(cbtcwW
->lpcs
->lpszName
))
276 if (cbtcwW
->lpcs
->lpszName
[0] != 0xffff)
278 len
= WideCharToMultiByte( CP_ACP
, 0, cbtcwW
->lpcs
->lpszName
, -1, NULL
, 0, NULL
, NULL
);
279 nameA
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(CHAR
) );
280 WideCharToMultiByte( CP_ACP
, 0, cbtcwW
->lpcs
->lpszName
, -1, nameA
, len
, NULL
, NULL
);
281 csA
.lpszName
= nameA
;
285 name_buf
[0] = '\xff';
286 name_buf
[1] = cbtcwW
->lpcs
->lpszName
[1];
287 name_buf
[2] = cbtcwW
->lpcs
->lpszName
[1] >> 8;
289 csA
.lpszName
= name_buf
;
293 if (!IS_INTRESOURCE(cbtcwW
->lpcs
->lpszClass
)) {
294 len
= WideCharToMultiByte( CP_ACP
, 0, cbtcwW
->lpcs
->lpszClass
, -1, NULL
, 0, NULL
, NULL
);
295 classA
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(CHAR
) );
296 WideCharToMultiByte( CP_ACP
, 0, cbtcwW
->lpcs
->lpszClass
, -1, classA
, len
, NULL
, NULL
);
297 csA
.lpszClass
= classA
;
300 ret
= HOOKPROC_wrapper( proc
, code
, wparam
, (LPARAM
)&cbtcwA
);
301 cbtcwW
->hwndInsertAfter
= cbtcwA
.hwndInsertAfter
;
302 HeapFree( GetProcessHeap(), 0, nameA
);
303 HeapFree( GetProcessHeap(), 0, classA
);
309 /***********************************************************************
312 static LRESULT
call_hook_proc( HOOKPROC proc
, INT id
, INT code
, WPARAM wparam
, LPARAM lparam
,
313 BOOL prev_unicode
, BOOL next_unicode
)
317 TRACE_(relay
)( "\1Call hook proc %p (id=%s,code=%x,wp=%08Ix,lp=%08Ix)\n",
318 proc
, hook_names
[id
-WH_MINHOOK
], code
, wparam
, lparam
);
320 if (!prev_unicode
== !next_unicode
) ret
= proc( code
, wparam
, lparam
);
321 else if (prev_unicode
) ret
= call_hook_WtoA( proc
, id
, code
, wparam
, lparam
);
322 else ret
= call_hook_AtoW( proc
, id
, code
, wparam
, lparam
);
324 TRACE_(relay
)( "\1Ret hook proc %p (id=%s,code=%x,wp=%08Ix,lp=%08Ix) retval=%08Ix\n",
325 proc
, hook_names
[id
-WH_MINHOOK
], code
, wparam
, lparam
, ret
);
331 /***********************************************************************
334 * Retrieve the hook procedure real value for a module-relative proc
336 void *get_hook_proc( void *proc
, const WCHAR
*module
, HMODULE
*free_module
)
340 GetModuleHandleExW( 0, module
, &mod
);
344 TRACE( "loading %s\n", debugstr_w(module
) );
345 /* FIXME: the library will never be freed */
346 if (!(mod
= LoadLibraryExW(module
, NULL
, LOAD_WITH_ALTERED_SEARCH_PATH
))) return NULL
;
348 return (char *)mod
+ (ULONG_PTR
)proc
;
352 /***********************************************************************
353 * SetWindowsHookA (USER32.@)
355 HHOOK WINAPI
SetWindowsHookA( INT id
, HOOKPROC proc
)
357 return SetWindowsHookExA( id
, proc
, 0, GetCurrentThreadId() );
361 /***********************************************************************
362 * SetWindowsHookW (USER32.@)
364 HHOOK WINAPI
SetWindowsHookW( INT id
, HOOKPROC proc
)
366 return SetWindowsHookExW( id
, proc
, 0, GetCurrentThreadId() );
370 /***********************************************************************
371 * SetWindowsHookExA (USER32.@)
373 HHOOK WINAPI
SetWindowsHookExA( INT id
, HOOKPROC proc
, HINSTANCE inst
, DWORD tid
)
375 return set_windows_hook( id
, proc
, inst
, tid
, TRUE
);
378 /***********************************************************************
379 * SetWindowsHookExW (USER32.@)
381 HHOOK WINAPI
SetWindowsHookExW( INT id
, HOOKPROC proc
, HINSTANCE inst
, DWORD tid
)
383 return set_windows_hook( id
, proc
, inst
, tid
, FALSE
);
387 /***********************************************************************
388 * UnhookWindowsHook (USER32.@)
390 BOOL WINAPI
UnhookWindowsHook( INT id
, HOOKPROC proc
)
392 return NtUserUnhookWindowsHook( id
, proc
);
396 /***********************************************************************
397 * SetWinEventHook [USER32.@]
399 * Set up an event hook for a set of events.
402 * event_min [I] Lowest event handled by pfnProc
403 * event_max [I] Highest event handled by pfnProc
404 * inst [I] DLL containing pfnProc
405 * proc [I] Callback event hook function
406 * pid [I] Process to get events from, or 0 for all processes
407 * tid [I] Thread to get events from, or 0 for all threads
408 * flags [I] Flags indicating the status of pfnProc
411 * Success: A handle representing the hook.
412 * Failure: A NULL handle.
414 HWINEVENTHOOK WINAPI
SetWinEventHook(DWORD event_min
, DWORD event_max
,
415 HMODULE inst
, WINEVENTPROC proc
,
416 DWORD pid
, DWORD tid
, DWORD flags
)
418 WCHAR module
[MAX_PATH
];
422 TRACE("%ld,%ld,%p,%p,%08lx,%04lx,%08lx\n", event_min
, event_max
, inst
,
423 proc
, pid
, tid
, flags
);
425 if (inst
&& (!(len
= GetModuleFileNameW( inst
, module
, MAX_PATH
)) || len
>= MAX_PATH
))
431 str
.Length
= str
.MaximumLength
= len
* sizeof(WCHAR
);
432 return NtUserSetWinEventHook( event_min
, event_max
, inst
, &str
, proc
, pid
, tid
, flags
);
435 NTSTATUS WINAPI
User32CallWinEventHook( void *args
, ULONG size
)
437 const struct win_event_hook_params
*params
= args
;
438 WINEVENTPROC proc
= params
->proc
;
439 HMODULE free_module
= 0;
441 if (params
->module
[0] && !(proc
= get_hook_proc( proc
, params
->module
, &free_module
)))
442 return STATUS_INVALID_PARAMETER
;
444 TRACE_(relay
)( "\1Call winevent hook proc %p (hhook=%p,event=%lx,hwnd=%p,object_id=%lx,child_id=%lx,tid=%04lx,time=%lx)\n",
445 proc
, params
->handle
, params
->event
, params
->hwnd
, params
->object_id
,
446 params
->child_id
, params
->tid
, params
->time
);
448 proc( params
->handle
, params
->event
, params
->hwnd
, params
->object_id
, params
->child_id
,
449 params
->tid
, params
->time
);
451 TRACE_(relay
)( "\1Ret winevent hook proc %p (hhook=%p,event=%lx,hwnd=%p,object_id=%lx,child_id=%lx,tid=%04lx,time=%lx)\n",
452 proc
, params
->handle
, params
->event
, params
->hwnd
, params
->object_id
,
453 params
->child_id
, params
->tid
, params
->time
);
455 if (free_module
) FreeLibrary( free_module
);
456 return STATUS_SUCCESS
;
459 NTSTATUS WINAPI
User32CallWindowsHook( void *args
, ULONG size
)
461 struct win_hook_params
*params
= args
;
462 HOOKPROC proc
= params
->proc
;
463 HMODULE free_module
= 0;
464 void *ret_ptr
= NULL
;
467 size_t lparam_offset
;
470 lparam_offset
= FIELD_OFFSET( struct win_hook_params
, module
[wcslen( params
->module
) + 1]);
472 if (lparam_offset
< size
)
474 lparam_offset
= (lparam_offset
+ 15) & ~15; /* align */
475 ret_size
= size
- lparam_offset
;
476 ret_ptr
= (char *)params
+ lparam_offset
;
477 params
->lparam
= (LPARAM
)ret_ptr
;
482 if (params
->code
== HCBT_CREATEWND
)
484 cbtc
.hwndInsertAfter
= HWND_TOP
;
485 unpack_message( (HWND
)params
->wparam
, WM_CREATE
, NULL
, (LPARAM
*)&cbtc
.lpcs
,
487 params
->lparam
= (LPARAM
)&cbtc
;
488 ret_size
= sizeof(*cbtc
.lpcs
);
492 if (ret_size
> sizeof(CWPSTRUCT
))
494 CWPSTRUCT
*cwp
= (CWPSTRUCT
*)params
->lparam
;
495 size_t offset
= (lparam_offset
+ sizeof(*cwp
) + 15) & ~15;
497 unpack_message( cwp
->hwnd
, cwp
->message
, &cwp
->wParam
, &cwp
->lParam
,
498 (char *)params
+ offset
, !params
->prev_unicode
);
502 case WH_CALLWNDPROCRET
:
503 if (ret_size
> sizeof(CWPRETSTRUCT
))
505 CWPRETSTRUCT
*cwpret
= (CWPRETSTRUCT
*)params
->lparam
;
506 size_t offset
= (lparam_offset
+ sizeof(*cwpret
) + 15) & ~15;
508 unpack_message( cwpret
->hwnd
, cwpret
->message
, &cwpret
->wParam
, &cwpret
->lParam
,
509 (char *)params
+ offset
, !params
->prev_unicode
);
515 if (params
->module
[0] && !(proc
= get_hook_proc( proc
, params
->module
, &free_module
)))
518 ret
= call_hook_proc( proc
, params
->id
, params
->code
, params
->wparam
, params
->lparam
,
519 params
->prev_unicode
, params
->next_unicode
);
521 if (free_module
) FreeLibrary( free_module
);
525 LRESULT
*result_ptr
= (LRESULT
*)ret_ptr
- 1;
527 return NtCallbackReturn( result_ptr
, sizeof(*result_ptr
) + ret_size
, STATUS_SUCCESS
);
529 return NtCallbackReturn( &ret
, sizeof(ret
), STATUS_SUCCESS
);
532 /***********************************************************************
533 * IsWinEventHookInstalled [USER32.@]
535 * Determine if an event hook is installed for an event.
538 * dwEvent [I] Id of the event
541 * TRUE, If there are any hooks installed for the event.
547 BOOL WINAPI
IsWinEventHookInstalled(DWORD dwEvent
)
549 /* FIXME: Needed by Office 2007 installer */
550 WARN("(%ld)-stub!\n", dwEvent
);
554 /* Undocumented RegisterUserApiHook() */
555 BOOL WINAPI
RegisterUserApiHook(const struct user_api_hook
*new_hook
, struct user_api_hook
*old_hook
)
560 EnterCriticalSection( &api_hook_cs
);
561 hooked_user_api
= *new_hook
;
562 user_api
= &hooked_user_api
;
564 *old_hook
= original_user_api
;
565 LeaveCriticalSection( &api_hook_cs
);
569 /* Undocumented UnregisterUserApiHook() */
570 void WINAPI
UnregisterUserApiHook(void)
572 InterlockedExchangePointer((void **)&user_api
, &original_user_api
);