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
65 #include "user_private.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
=
75 USER_NonClientButtonDraw
,
79 static struct user_api_hook hooked_user_api
;
80 struct user_api_hook
*user_api
= &original_user_api
;
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
=
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] =
108 "WH_JOURNALPLAYBACK",
126 /***********************************************************************
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
];
138 RtlInitUnicodeString( &str
, NULL
);
142 size_t len
= GetModuleFileNameW( inst
, module
, ARRAYSIZE(module
) );
143 if (!len
|| len
>= ARRAYSIZE(module
))
145 SetLastError( ERROR_INVALID_PARAMETER
);
149 str
.MaximumLength
= str
.Length
= len
* sizeof(WCHAR
);
152 return NtUserSetWindowsHookEx( inst
, &str
, tid
, id
, proc
, ansi
);
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
,
163 __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
164 __ASM_CFI(".cfi_rel_offset %ebp,0\n\t")
166 __ASM_CFI(".cfi_def_cfa_register %ebp\n\t")
168 __ASM_CFI(".cfi_rel_offset %edi,-4\n\t")
170 __ASM_CFI(".cfi_rel_offset %esi,-8\n\t")
172 __ASM_CFI(".cfi_rel_offset %ebx,-12\n\t")
176 "movl 8(%ebp),%eax\n\t"
178 "leal -12(%ebp),%esp\n\t"
180 __ASM_CFI(".cfi_same_value %ebx\n\t")
182 __ASM_CFI(".cfi_same_value %esi\n\t")
184 __ASM_CFI(".cfi_same_value %edi\n\t")
186 __ASM_CFI(".cfi_def_cfa %esp,4\n\t")
187 __ASM_CFI(".cfi_same_value %ebp\n\t")
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 /***********************************************************************
201 static LRESULT
call_hook_AtoW( HOOKPROC proc
, INT id
, INT code
, WPARAM wparam
, LPARAM lparam
)
204 UNICODE_STRING usBuffer
;
205 if (id
!= WH_CBT
|| code
!= HCBT_CREATEWND
)
206 ret
= HOOKPROC_wrapper( proc
, code
, wparam
, lparam
);
209 CBT_CREATEWNDA
*cbtcwA
= (CBT_CREATEWNDA
*)lparam
;
210 CBT_CREATEWNDW cbtcwW
;
213 LPWSTR classW
= NULL
;
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
;
229 name_buf
[0] = 0xffff;
230 name_buf
[1] = MAKEWORD( cbtcwA
->lpcs
->lpszName
[1], cbtcwA
->lpcs
->lpszName
[2] );
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
);
249 /***********************************************************************
252 static LRESULT
call_hook_WtoA( HOOKPROC proc
, INT id
, INT code
, WPARAM wparam
, LPARAM lparam
)
256 if (id
!= WH_CBT
|| code
!= HCBT_CREATEWND
)
257 ret
= HOOKPROC_wrapper( proc
, code
, wparam
, lparam
);
260 CBT_CREATEWNDW
*cbtcwW
= (CBT_CREATEWNDW
*)lparam
;
261 CBT_CREATEWNDA cbtcwA
;
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
;
283 name_buf
[0] = '\xff';
284 name_buf
[1] = cbtcwW
->lpcs
->lpszName
[1];
285 name_buf
[2] = cbtcwW
->lpcs
->lpszName
[1] >> 8;
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
);
307 /***********************************************************************
310 static LRESULT
call_hook_proc( HOOKPROC proc
, INT id
, INT code
, WPARAM wparam
, LPARAM lparam
,
311 BOOL prev_unicode
, BOOL next_unicode
)
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
);
329 /***********************************************************************
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
)
338 GetModuleHandleExW( 0, module
, &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.
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
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
];
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
))
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
);
455 BOOL WINAPI
User32CallWindowsHook( struct win_hook_params
*params
, ULONG size
)
457 HOOKPROC proc
= params
->proc
;
458 const WCHAR
*module
= NULL
;
459 HMODULE free_module
= 0;
460 void *ret_lparam
= NULL
;
462 UINT ret_lparam_size
= 0;
465 if (size
> sizeof(*params
) + params
->lparam_size
)
466 module
= (const WCHAR
*)((const char *)(params
+ 1) + params
->lparam_size
);
468 if (params
->lparam_size
)
470 ret_lparam
= (void *)params
->lparam
;
471 ret_lparam_size
= params
->lparam_size
;
472 params
->lparam
= (LPARAM
)(params
+ 1);
474 if (params
->id
== WH_CBT
&& params
->code
== HCBT_CREATEWND
)
476 CREATESTRUCTW
*cs
= (CREATESTRUCTW
*)params
->lparam
;
477 const WCHAR
*ptr
= (const WCHAR
*)(cs
+ 1);
479 if (!IS_INTRESOURCE(cs
->lpszName
))
482 ptr
+= wcslen( ptr
) + 1;
484 if (!IS_INTRESOURCE(cs
->lpszClass
))
487 cbtc
.hwndInsertAfter
= HWND_TOP
;
489 params
->lparam
= (LPARAM
)&cbtc
;
490 ret_lparam_size
= sizeof(*cs
);
493 if (module
&& !(proc
= get_hook_proc( proc
, module
, &free_module
))) return FALSE
;
495 ret
= call_hook_proc( proc
, params
->id
, params
->code
, params
->wparam
, params
->lparam
,
496 params
->prev_unicode
, params
->next_unicode
);
498 if (free_module
) FreeLibrary( free_module
);
499 if (ret_lparam
) memcpy( ret_lparam
, params
+ 1, ret_lparam_size
);
500 else if (ret_lparam_size
) NtCallbackReturn( params
+ 1, ret_lparam_size
, ret
);
504 /***********************************************************************
505 * IsWinEventHookInstalled [USER32.@]
507 * Determine if an event hook is installed for an event.
510 * dwEvent [I] Id of the event
513 * TRUE, If there are any hooks installed for the event.
519 BOOL WINAPI
IsWinEventHookInstalled(DWORD dwEvent
)
521 /* FIXME: Needed by Office 2007 installer */
522 WARN("(%ld)-stub!\n", dwEvent
);
526 /* Undocumented RegisterUserApiHook() */
527 BOOL WINAPI
RegisterUserApiHook(const struct user_api_hook
*new_hook
, struct user_api_hook
*old_hook
)
532 EnterCriticalSection( &api_hook_cs
);
533 hooked_user_api
= *new_hook
;
534 user_api
= &hooked_user_api
;
536 *old_hook
= original_user_api
;
537 LeaveCriticalSection( &api_hook_cs
);
541 /* Undocumented UnregisterUserApiHook() */
542 void WINAPI
UnregisterUserApiHook(void)
544 InterlockedExchangePointer((void **)&user_api
, &original_user_api
);