Release 1.5.29.
[wine] / dlls / user32 / hook.c
1 /*
2  * Windows hook functions
3  *
4  * Copyright 2002 Alexandre Julliard
5  * Copyright 2005 Dmitry Timoshkov
6  *
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.
11  *
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.
16  *
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
20  *
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
63  */
64
65 #include "config.h"
66 #include "wine/port.h"
67
68 #include <stdarg.h>
69 #include <assert.h>
70
71 #include "windef.h"
72 #include "winbase.h"
73 #include "wingdi.h"
74 #include "winuser.h"
75 #include "winerror.h"
76 #include "win.h"
77 #include "user_private.h"
78 #include "wine/server.h"
79 #include "wine/unicode.h"
80 #include "wine/debug.h"
81 #include "winternl.h"
82
83 WINE_DEFAULT_DEBUG_CHANNEL(hook);
84 WINE_DECLARE_DEBUG_CHANNEL(relay);
85
86 struct hook_info
87 {
88     INT id;
89     void *proc;
90     void *handle;
91     DWORD pid, tid;
92     BOOL prev_unicode, next_unicode;
93     WCHAR module[MAX_PATH];
94 };
95
96 #define WH_WINEVENT (WH_MAXHOOK+1)
97
98 static const char * const hook_names[WH_WINEVENT - WH_MINHOOK + 1] =
99 {
100     "WH_MSGFILTER",
101     "WH_JOURNALRECORD",
102     "WH_JOURNALPLAYBACK",
103     "WH_KEYBOARD",
104     "WH_GETMESSAGE",
105     "WH_CALLWNDPROC",
106     "WH_CBT",
107     "WH_SYSMSGFILTER",
108     "WH_MOUSE",
109     "WH_HARDWARE",
110     "WH_DEBUG",
111     "WH_SHELL",
112     "WH_FOREGROUNDIDLE",
113     "WH_CALLWNDPROCRET",
114     "WH_KEYBOARD_LL",
115     "WH_MOUSE_LL",
116     "WH_WINEVENT"
117 };
118
119
120 /***********************************************************************
121  *              get_ll_hook_timeout
122  *
123  */
124 static UINT get_ll_hook_timeout(void)
125 {
126     /* FIXME: should retrieve LowLevelHooksTimeout in HKEY_CURRENT_USER\Control Panel\Desktop */
127     return 2000;
128 }
129
130
131 /***********************************************************************
132  *              set_windows_hook
133  *
134  * Implementation of SetWindowsHookExA and SetWindowsHookExW.
135  */
136 static HHOOK set_windows_hook( INT id, HOOKPROC proc, HINSTANCE inst, DWORD tid, BOOL unicode )
137 {
138     HHOOK handle = 0;
139     WCHAR module[MAX_PATH];
140     DWORD len;
141
142     if (!proc)
143     {
144         SetLastError( ERROR_INVALID_FILTER_PROC );
145         return 0;
146     }
147
148     if (tid)  /* thread-local hook */
149     {
150         if (id == WH_JOURNALRECORD ||
151             id == WH_JOURNALPLAYBACK ||
152             id == WH_KEYBOARD_LL ||
153             id == WH_MOUSE_LL ||
154             id == WH_SYSMSGFILTER)
155         {
156             /* these can only be global */
157             SetLastError( ERROR_INVALID_PARAMETER );
158             return 0;
159         }
160     }
161     else  /* system-global hook */
162     {
163         if (id == WH_KEYBOARD_LL || id == WH_MOUSE_LL) inst = 0;
164         else if (!inst)
165         {
166             SetLastError( ERROR_HOOK_NEEDS_HMOD );
167             return 0;
168         }
169     }
170
171     if (inst && (!(len = GetModuleFileNameW( inst, module, MAX_PATH )) || len >= MAX_PATH))
172     {
173         SetLastError( ERROR_INVALID_PARAMETER );
174         return 0;
175     }
176
177     SERVER_START_REQ( set_hook )
178     {
179         req->id        = id;
180         req->pid       = 0;
181         req->tid       = tid;
182         req->event_min = EVENT_MIN;
183         req->event_max = EVENT_MAX;
184         req->flags     = WINEVENT_INCONTEXT;
185         req->unicode   = unicode;
186         if (inst) /* make proc relative to the module base */
187         {
188             req->proc = wine_server_client_ptr( (void *)((char *)proc - (char *)inst) );
189             wine_server_add_data( req, module, strlenW(module) * sizeof(WCHAR) );
190         }
191         else req->proc = wine_server_client_ptr( proc );
192
193         if (!wine_server_call_err( req ))
194         {
195             handle = wine_server_ptr_handle( reply->handle );
196             get_user_thread_info()->active_hooks = reply->active_hooks;
197         }
198     }
199     SERVER_END_REQ;
200
201     TRACE( "%s %p %x -> %p\n", hook_names[id-WH_MINHOOK], proc, tid, handle );
202     return handle;
203 }
204
205 #ifdef __i386__
206 /* Some apps pass a non-stdcall proc to SetWindowsHookExA,
207  * so we need a small assembly wrapper to call the proc.
208  */
209 extern LRESULT HOOKPROC_wrapper( HOOKPROC proc,
210                                  INT code, WPARAM wParam, LPARAM lParam );
211 __ASM_GLOBAL_FUNC( HOOKPROC_wrapper,
212                    "pushl %ebp\n\t"
213                    __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
214                    __ASM_CFI(".cfi_rel_offset %ebp,0\n\t")
215                    "movl %esp,%ebp\n\t"
216                    __ASM_CFI(".cfi_def_cfa_register %ebp\n\t")
217                    "pushl %edi\n\t"
218                    __ASM_CFI(".cfi_rel_offset %edi,-4\n\t")
219                    "pushl %esi\n\t"
220                    __ASM_CFI(".cfi_rel_offset %esi,-8\n\t")
221                    "pushl %ebx\n\t"
222                    __ASM_CFI(".cfi_rel_offset %ebx,-12\n\t")
223                    "pushl 20(%ebp)\n\t"
224                    "pushl 16(%ebp)\n\t"
225                    "pushl 12(%ebp)\n\t"
226                    "movl 8(%ebp),%eax\n\t"
227                    "call *%eax\n\t"
228                    "leal -12(%ebp),%esp\n\t"
229                    "popl %ebx\n\t"
230                    __ASM_CFI(".cfi_same_value %ebx\n\t")
231                    "popl %esi\n\t"
232                    __ASM_CFI(".cfi_same_value %esi\n\t")
233                    "popl %edi\n\t"
234                    __ASM_CFI(".cfi_same_value %edi\n\t")
235                    "leave\n\t"
236                    __ASM_CFI(".cfi_def_cfa %esp,4\n\t")
237                    __ASM_CFI(".cfi_same_value %ebp\n\t")
238                    "ret" )
239 #else
240 static inline LRESULT HOOKPROC_wrapper( HOOKPROC proc,
241                                  INT code, WPARAM wParam, LPARAM lParam )
242 {
243     return proc( code, wParam, lParam );
244 }
245 #endif  /* __i386__ */
246
247
248 /***********************************************************************
249  *              call_hook_AtoW
250  */
251 static LRESULT call_hook_AtoW( HOOKPROC proc, INT id, INT code, WPARAM wparam, LPARAM lparam )
252 {
253     LRESULT ret;
254     UNICODE_STRING usBuffer;
255     if (id != WH_CBT || code != HCBT_CREATEWND)
256         ret = HOOKPROC_wrapper( proc, code, wparam, lparam );
257     else
258     {
259         CBT_CREATEWNDA *cbtcwA = (CBT_CREATEWNDA *)lparam;
260         CBT_CREATEWNDW cbtcwW;
261         CREATESTRUCTW csW;
262         LPWSTR nameW = NULL;
263         LPWSTR classW = NULL;
264
265         cbtcwW.lpcs = &csW;
266         cbtcwW.hwndInsertAfter = cbtcwA->hwndInsertAfter;
267         csW = *(CREATESTRUCTW *)cbtcwA->lpcs;
268
269         if (!IS_INTRESOURCE(cbtcwA->lpcs->lpszName))
270         {
271             RtlCreateUnicodeStringFromAsciiz(&usBuffer,cbtcwA->lpcs->lpszName);
272             csW.lpszName = nameW = usBuffer.Buffer;
273         }
274         if (!IS_INTRESOURCE(cbtcwA->lpcs->lpszClass))
275         {
276             RtlCreateUnicodeStringFromAsciiz(&usBuffer,cbtcwA->lpcs->lpszClass);
277             csW.lpszClass = classW = usBuffer.Buffer;
278         }
279         ret = HOOKPROC_wrapper( proc, code, wparam, (LPARAM)&cbtcwW );
280         cbtcwA->hwndInsertAfter = cbtcwW.hwndInsertAfter;
281         HeapFree( GetProcessHeap(), 0, nameW );
282         HeapFree( GetProcessHeap(), 0, classW );
283     }
284     return ret;
285 }
286
287
288 /***********************************************************************
289  *              call_hook_WtoA
290  */
291 static LRESULT call_hook_WtoA( HOOKPROC proc, INT id, INT code, WPARAM wparam, LPARAM lparam )
292 {
293     LRESULT ret;
294
295     if (id != WH_CBT || code != HCBT_CREATEWND)
296         ret = HOOKPROC_wrapper( proc, code, wparam, lparam );
297     else
298     {
299         CBT_CREATEWNDW *cbtcwW = (CBT_CREATEWNDW *)lparam;
300         CBT_CREATEWNDA cbtcwA;
301         CREATESTRUCTA csA;
302         int len;
303         LPSTR nameA = NULL;
304         LPSTR classA = NULL;
305
306         cbtcwA.lpcs = &csA;
307         cbtcwA.hwndInsertAfter = cbtcwW->hwndInsertAfter;
308         csA = *(CREATESTRUCTA *)cbtcwW->lpcs;
309
310         if (!IS_INTRESOURCE(cbtcwW->lpcs->lpszName)) {
311             len = WideCharToMultiByte( CP_ACP, 0, cbtcwW->lpcs->lpszName, -1, NULL, 0, NULL, NULL );
312             nameA = HeapAlloc( GetProcessHeap(), 0, len*sizeof(CHAR) );
313             WideCharToMultiByte( CP_ACP, 0, cbtcwW->lpcs->lpszName, -1, nameA, len, NULL, NULL );
314             csA.lpszName = nameA;
315         }
316
317         if (!IS_INTRESOURCE(cbtcwW->lpcs->lpszClass)) {
318             len = WideCharToMultiByte( CP_ACP, 0, cbtcwW->lpcs->lpszClass, -1, NULL, 0, NULL, NULL );
319             classA = HeapAlloc( GetProcessHeap(), 0, len*sizeof(CHAR) );
320             WideCharToMultiByte( CP_ACP, 0, cbtcwW->lpcs->lpszClass, -1, classA, len, NULL, NULL );
321             csA.lpszClass = classA;
322         }
323
324         ret = HOOKPROC_wrapper( proc, code, wparam, (LPARAM)&cbtcwA );
325         cbtcwW->hwndInsertAfter = cbtcwA.hwndInsertAfter;
326         HeapFree( GetProcessHeap(), 0, nameA );
327         HeapFree( GetProcessHeap(), 0, classA );
328     }
329     return ret;
330 }
331
332
333 /***********************************************************************
334  *              call_hook_proc
335  */
336 static LRESULT call_hook_proc( HOOKPROC proc, INT id, INT code, WPARAM wparam, LPARAM lparam,
337                                BOOL prev_unicode, BOOL next_unicode )
338 {
339     LRESULT ret;
340
341     if (TRACE_ON(relay))
342         DPRINTF( "%04x:Call hook proc %p (id=%s,code=%x,wp=%08lx,lp=%08lx)\n",
343                  GetCurrentThreadId(), proc, hook_names[id-WH_MINHOOK], code, wparam, lparam );
344
345     if (!prev_unicode == !next_unicode) ret = proc( code, wparam, lparam );
346     else if (prev_unicode) ret = call_hook_WtoA( proc, id, code, wparam, lparam );
347     else ret = call_hook_AtoW( proc, id, code, wparam, lparam );
348
349     if (TRACE_ON(relay))
350         DPRINTF( "%04x:Ret  hook proc %p (id=%s,code=%x,wp=%08lx,lp=%08lx) retval=%08lx\n",
351                  GetCurrentThreadId(), proc, hook_names[id-WH_MINHOOK], code, wparam, lparam, ret );
352
353     return ret;
354 }
355
356
357 /***********************************************************************
358  *              get_hook_proc
359  *
360  * Retrieve the hook procedure real value for a module-relative proc
361  */
362 void *get_hook_proc( void *proc, const WCHAR *module )
363 {
364     HMODULE mod;
365
366     if (!(mod = GetModuleHandleW(module)))
367     {
368         TRACE( "loading %s\n", debugstr_w(module) );
369         /* FIXME: the library will never be freed */
370         if (!(mod = LoadLibraryExW(module, NULL, LOAD_WITH_ALTERED_SEARCH_PATH))) return NULL;
371     }
372     return (char *)mod + (ULONG_PTR)proc;
373 }
374
375 /***********************************************************************
376  *              call_hook
377  *
378  * Call hook either in current thread or send message to the destination
379  * thread.
380  */
381 static LRESULT call_hook( struct hook_info *info, INT code, WPARAM wparam, LPARAM lparam )
382 {
383     DWORD_PTR ret = 0;
384
385     if (info->tid)
386     {
387         struct hook_extra_info h_extra;
388         h_extra.handle = info->handle;
389         h_extra.lparam = lparam;
390
391         TRACE( "calling hook in thread %04x %s code %x wp %lx lp %lx\n",
392                info->tid, hook_names[info->id-WH_MINHOOK], code, wparam, lparam );
393
394         switch(info->id)
395         {
396         case WH_KEYBOARD_LL:
397             MSG_SendInternalMessageTimeout( info->pid, info->tid, WM_WINE_KEYBOARD_LL_HOOK,
398                                             wparam, (LPARAM)&h_extra, SMTO_ABORTIFHUNG,
399                                             get_ll_hook_timeout(), &ret );
400             break;
401         case WH_MOUSE_LL:
402             MSG_SendInternalMessageTimeout( info->pid, info->tid, WM_WINE_MOUSE_LL_HOOK,
403                                             wparam, (LPARAM)&h_extra, SMTO_ABORTIFHUNG,
404                                             get_ll_hook_timeout(), &ret );
405             break;
406         default:
407             ERR("Unknown hook id %d\n", info->id);
408             assert(0);
409             break;
410         }
411     }
412     else if (info->proc)
413     {
414         TRACE( "calling hook %p %s code %x wp %lx lp %lx module %s\n",
415                info->proc, hook_names[info->id-WH_MINHOOK], code, wparam,
416                lparam, debugstr_w(info->module) );
417
418         if (!info->module[0] ||
419             (info->proc = get_hook_proc( info->proc, info->module )) != NULL)
420         {
421             struct user_thread_info *thread_info = get_user_thread_info();
422             HHOOK prev = thread_info->hook;
423             BOOL prev_unicode = thread_info->hook_unicode;
424
425             thread_info->hook = info->handle;
426             thread_info->hook_unicode = info->next_unicode;
427             ret = call_hook_proc( info->proc, info->id, code, wparam, lparam,
428                                   info->prev_unicode, info->next_unicode );
429             thread_info->hook = prev;
430             thread_info->hook_unicode = prev_unicode;
431         }
432     }
433
434     if (info->id == WH_KEYBOARD_LL || info->id == WH_MOUSE_LL)
435         get_user_thread_info()->key_state_time = 0;  /* force refreshing the key state cache */
436
437     return ret;
438 }
439
440
441 /***********************************************************************
442  *           HOOK_IsHooked
443  */
444 static BOOL HOOK_IsHooked( INT id )
445 {
446     struct user_thread_info *thread_info = get_user_thread_info();
447
448     if (!thread_info->active_hooks) return TRUE;
449     return (thread_info->active_hooks & (1 << (id - WH_MINHOOK))) != 0;
450 }
451
452
453 /***********************************************************************
454  *              HOOK_CallHooks
455  */
456 LRESULT HOOK_CallHooks( INT id, INT code, WPARAM wparam, LPARAM lparam, BOOL unicode )
457 {
458     struct user_thread_info *thread_info = get_user_thread_info();
459     struct hook_info info;
460     DWORD_PTR ret = 0;
461
462     USER_CheckNotLock();
463
464     if (!HOOK_IsHooked( id ))
465     {
466         TRACE( "skipping hook %s mask %x\n", hook_names[id-WH_MINHOOK], thread_info->active_hooks );
467         return 0;
468     }
469
470     ZeroMemory( &info, sizeof(info) - sizeof(info.module) );
471     info.prev_unicode = unicode;
472     info.id = id;
473
474     SERVER_START_REQ( start_hook_chain )
475     {
476         req->id = info.id;
477         req->event = EVENT_MIN;
478         wine_server_set_reply( req, info.module, sizeof(info.module)-sizeof(WCHAR) );
479         if (!wine_server_call( req ))
480         {
481             info.module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0;
482             info.handle       = wine_server_ptr_handle( reply->handle );
483             info.pid          = reply->pid;
484             info.tid          = reply->tid;
485             info.proc         = wine_server_get_ptr( reply->proc );
486             info.next_unicode = reply->unicode;
487             thread_info->active_hooks = reply->active_hooks;
488         }
489     }
490     SERVER_END_REQ;
491
492     if (!info.tid && !info.proc) return 0;
493     ret = call_hook( &info, code, wparam, lparam );
494
495     SERVER_START_REQ( finish_hook_chain )
496     {
497         req->id = id;
498         wine_server_call( req );
499     }
500     SERVER_END_REQ;
501     return ret;
502 }
503
504
505 /***********************************************************************
506  *              SetWindowsHookA (USER32.@)
507  */
508 HHOOK WINAPI SetWindowsHookA( INT id, HOOKPROC proc )
509 {
510     return SetWindowsHookExA( id, proc, 0, GetCurrentThreadId() );
511 }
512
513
514 /***********************************************************************
515  *              SetWindowsHookW (USER32.@)
516  */
517 HHOOK WINAPI SetWindowsHookW( INT id, HOOKPROC proc )
518 {
519     return SetWindowsHookExW( id, proc, 0, GetCurrentThreadId() );
520 }
521
522
523 /***********************************************************************
524  *              SetWindowsHookExA (USER32.@)
525  */
526 HHOOK WINAPI SetWindowsHookExA( INT id, HOOKPROC proc, HINSTANCE inst, DWORD tid )
527 {
528     return set_windows_hook( id, proc, inst, tid, FALSE );
529 }
530
531 /***********************************************************************
532  *              SetWindowsHookExW (USER32.@)
533  */
534 HHOOK WINAPI SetWindowsHookExW( INT id, HOOKPROC proc, HINSTANCE inst, DWORD tid )
535 {
536     return set_windows_hook( id, proc, inst, tid, TRUE );
537 }
538
539
540 /***********************************************************************
541  *              UnhookWindowsHook (USER32.@)
542  */
543 BOOL WINAPI UnhookWindowsHook( INT id, HOOKPROC proc )
544 {
545     BOOL ret;
546
547     TRACE( "%s %p\n", hook_names[id-WH_MINHOOK], proc );
548
549     SERVER_START_REQ( remove_hook )
550     {
551         req->handle = 0;
552         req->id   = id;
553         req->proc = wine_server_client_ptr( proc );
554         ret = !wine_server_call_err( req );
555         if (ret) get_user_thread_info()->active_hooks = reply->active_hooks;
556     }
557     SERVER_END_REQ;
558     if (!ret && GetLastError() == ERROR_INVALID_HANDLE) SetLastError( ERROR_INVALID_HOOK_HANDLE );
559     return ret;
560 }
561
562
563
564 /***********************************************************************
565  *              UnhookWindowsHookEx (USER32.@)
566  */
567 BOOL WINAPI UnhookWindowsHookEx( HHOOK hhook )
568 {
569     BOOL ret;
570
571     SERVER_START_REQ( remove_hook )
572     {
573         req->handle = wine_server_user_handle( hhook );
574         req->id     = 0;
575         ret = !wine_server_call_err( req );
576         if (ret) get_user_thread_info()->active_hooks = reply->active_hooks;
577     }
578     SERVER_END_REQ;
579     if (!ret && GetLastError() == ERROR_INVALID_HANDLE) SetLastError( ERROR_INVALID_HOOK_HANDLE );
580     return ret;
581 }
582
583
584 /***********************************************************************
585  *              CallNextHookEx (USER32.@)
586  */
587 LRESULT WINAPI CallNextHookEx( HHOOK hhook, INT code, WPARAM wparam, LPARAM lparam )
588 {
589     struct user_thread_info *thread_info = get_user_thread_info();
590     struct hook_info info;
591
592     ZeroMemory( &info, sizeof(info) - sizeof(info.module) );
593
594     SERVER_START_REQ( get_hook_info )
595     {
596         req->handle = wine_server_user_handle( thread_info->hook );
597         req->get_next = 1;
598         req->event = EVENT_MIN;
599         wine_server_set_reply( req, info.module, sizeof(info.module)-sizeof(WCHAR) );
600         if (!wine_server_call_err( req ))
601         {
602             info.module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0;
603             info.handle       = wine_server_ptr_handle( reply->handle );
604             info.id           = reply->id;
605             info.pid          = reply->pid;
606             info.tid          = reply->tid;
607             info.proc         = wine_server_get_ptr( reply->proc );
608             info.next_unicode = reply->unicode;
609         }
610     }
611     SERVER_END_REQ;
612
613     info.prev_unicode = thread_info->hook_unicode;
614     return call_hook( &info, code, wparam, lparam );
615 }
616
617
618 LRESULT call_current_hook( HHOOK hhook, INT code, WPARAM wparam, LPARAM lparam )
619 {
620     struct hook_info info;
621
622     ZeroMemory( &info, sizeof(info) - sizeof(info.module) );
623
624     SERVER_START_REQ( get_hook_info )
625     {
626         req->handle = wine_server_user_handle( hhook );
627         req->get_next = 0;
628         req->event = EVENT_MIN;
629         wine_server_set_reply( req, info.module, sizeof(info.module)-sizeof(WCHAR) );
630         if (!wine_server_call_err( req ))
631         {
632             info.module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0;
633             info.handle       = wine_server_ptr_handle( reply->handle );
634             info.id           = reply->id;
635             info.pid          = reply->pid;
636             info.tid          = reply->tid;
637             info.proc         = wine_server_get_ptr( reply->proc );
638             info.next_unicode = reply->unicode;
639         }
640     }
641     SERVER_END_REQ;
642
643     info.prev_unicode = TRUE;  /* assume Unicode for this function */
644     return call_hook( &info, code, wparam, lparam );
645 }
646
647 /***********************************************************************
648  *              CallMsgFilterA (USER32.@)
649  */
650 BOOL WINAPI CallMsgFilterA( LPMSG msg, INT code )
651 {
652     if (HOOK_CallHooks( WH_SYSMSGFILTER, code, 0, (LPARAM)msg, FALSE )) return TRUE;
653     return HOOK_CallHooks( WH_MSGFILTER, code, 0, (LPARAM)msg, FALSE );
654 }
655
656
657 /***********************************************************************
658  *              CallMsgFilterW (USER32.@)
659  */
660 BOOL WINAPI CallMsgFilterW( LPMSG msg, INT code )
661 {
662     if (HOOK_CallHooks( WH_SYSMSGFILTER, code, 0, (LPARAM)msg, TRUE )) return TRUE;
663     return HOOK_CallHooks( WH_MSGFILTER, code, 0, (LPARAM)msg, TRUE );
664 }
665
666
667 /***********************************************************************
668  *           SetWinEventHook                            [USER32.@]
669  *
670  * Set up an event hook for a set of events.
671  *
672  * PARAMS
673  *  event_min [I] Lowest event handled by pfnProc
674  *  event_max [I] Highest event handled by pfnProc
675  *  inst      [I] DLL containing pfnProc
676  *  proc      [I] Callback event hook function
677  *  pid       [I] Process to get events from, or 0 for all processes
678  *  tid       [I] Thread to get events from, or 0 for all threads
679  *  flags     [I] Flags indicating the status of pfnProc
680  *
681  * RETURNS
682  *  Success: A handle representing the hook.
683  *  Failure: A NULL handle.
684  */
685 HWINEVENTHOOK WINAPI SetWinEventHook(DWORD event_min, DWORD event_max,
686                                      HMODULE inst, WINEVENTPROC proc,
687                                      DWORD pid, DWORD tid, DWORD flags)
688 {
689     HWINEVENTHOOK handle = 0;
690     WCHAR module[MAX_PATH];
691     DWORD len;
692
693     TRACE("%d,%d,%p,%p,%08x,%04x,%08x\n", event_min, event_max, inst,
694           proc, pid, tid, flags);
695
696     if (inst)
697     {
698         if (!(len = GetModuleFileNameW(inst, module, MAX_PATH)) || len >= MAX_PATH)
699             inst = 0;
700     }
701
702     if ((flags & WINEVENT_INCONTEXT) && !inst)
703     {
704         SetLastError(ERROR_HOOK_NEEDS_HMOD);
705         return 0;
706     }
707
708     if (event_min > event_max)
709     {
710         SetLastError(ERROR_INVALID_HOOK_FILTER);
711         return 0;
712     }
713
714     /* FIXME: what if the tid or pid belongs to another process? */
715     if (tid)  /* thread-local hook */
716         inst = 0;
717
718     SERVER_START_REQ( set_hook )
719     {
720         req->id        = WH_WINEVENT;
721         req->pid       = pid;
722         req->tid       = tid;
723         req->event_min = event_min;
724         req->event_max = event_max;
725         req->flags     = flags;
726         req->unicode   = 1;
727         if (inst) /* make proc relative to the module base */
728         {
729             req->proc = wine_server_client_ptr( (void *)((char *)proc - (char *)inst) );
730             wine_server_add_data( req, module, strlenW(module) * sizeof(WCHAR) );
731         }
732         else req->proc = wine_server_client_ptr( proc );
733
734         if (!wine_server_call_err( req ))
735         {
736             handle = wine_server_ptr_handle( reply->handle );
737             get_user_thread_info()->active_hooks = reply->active_hooks;
738         }
739     }
740     SERVER_END_REQ;
741
742     TRACE("-> %p\n", handle);
743     return handle;
744 }
745
746
747 /***********************************************************************
748  *           UnhookWinEvent                             [USER32.@]
749  *
750  * Remove an event hook for a set of events.
751  *
752  * PARAMS
753  *  hEventHook [I] Event hook to remove
754  *
755  * RETURNS
756  *  Success: TRUE. The event hook has been removed.
757  *  Failure: FALSE, if hEventHook is invalid.
758  */
759 BOOL WINAPI UnhookWinEvent(HWINEVENTHOOK hEventHook)
760 {
761     BOOL ret;
762
763     SERVER_START_REQ( remove_hook )
764     {
765         req->handle = wine_server_user_handle( hEventHook );
766         req->id     = WH_WINEVENT;
767         ret = !wine_server_call_err( req );
768         if (ret) get_user_thread_info()->active_hooks = reply->active_hooks;
769     }
770     SERVER_END_REQ;
771     return ret;
772 }
773
774 static inline BOOL find_first_hook(DWORD id, DWORD event, HWND hwnd, LONG object_id,
775                                    LONG child_id, struct hook_info *info)
776 {
777     struct user_thread_info *thread_info = get_user_thread_info();
778     BOOL ret;
779
780     if (!HOOK_IsHooked( id ))
781     {
782         TRACE( "skipping hook %s mask %x\n", hook_names[id-WH_MINHOOK], thread_info->active_hooks );
783         return FALSE;
784     }
785
786     SERVER_START_REQ( start_hook_chain )
787     {
788         req->id = id;
789         req->event = event;
790         req->window = wine_server_user_handle( hwnd );
791         req->object_id = object_id;
792         req->child_id = child_id;
793         wine_server_set_reply( req, info->module, sizeof(info->module)-sizeof(WCHAR) );
794         ret = !wine_server_call( req );
795         if (ret)
796         {
797             info->module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0;
798             info->handle    = wine_server_ptr_handle( reply->handle );
799             info->proc      = wine_server_get_ptr( reply->proc );
800             info->tid       = reply->tid;
801             thread_info->active_hooks = reply->active_hooks;
802         }
803     }
804     SERVER_END_REQ;
805     return ret && (info->tid || info->proc);
806 }
807
808 static inline BOOL find_next_hook(DWORD event, HWND hwnd, LONG object_id,
809                                   LONG child_id, struct hook_info *info)
810 {
811     BOOL ret;
812
813     SERVER_START_REQ( get_hook_info )
814     {
815         req->handle = wine_server_user_handle( info->handle );
816         req->get_next = 1;
817         req->event = event;
818         req->window = wine_server_user_handle( hwnd );
819         req->object_id = object_id;
820         req->child_id = child_id;
821         wine_server_set_reply( req, info->module, sizeof(info->module)-sizeof(WCHAR) );
822         ret = !wine_server_call( req );
823         if (ret)
824         {
825             info->module[wine_server_reply_size(req) / sizeof(WCHAR)] = 0;
826             info->handle    = wine_server_ptr_handle( reply->handle );
827             info->proc      = wine_server_get_ptr( reply->proc );
828             info->tid       = reply->tid;
829         }
830     }
831     SERVER_END_REQ;
832     return ret;
833 }
834
835 static inline void find_hook_close(DWORD id)
836 {
837     SERVER_START_REQ( finish_hook_chain )
838     {
839         req->id = id;
840         wine_server_call( req );
841     }
842     SERVER_END_REQ;
843 }
844
845 /***********************************************************************
846  *           NotifyWinEvent                             [USER32.@]
847  *
848  * Inform the OS that an event has occurred.
849  *
850  * PARAMS
851  *  event     [I] Id of the event
852  *  hwnd      [I] Window holding the object that created the event
853  *  object_id [I] Type of object that created the event
854  *  child_id  [I] Child object of nId, or CHILDID_SELF.
855  *
856  * RETURNS
857  *  Nothing.
858  */
859 void WINAPI NotifyWinEvent(DWORD event, HWND hwnd, LONG object_id, LONG child_id)
860 {
861     struct hook_info info;
862
863     TRACE("%04x,%p,%d,%d\n", event, hwnd, object_id, child_id);
864
865     if (!hwnd)
866     {
867         SetLastError(ERROR_INVALID_WINDOW_HANDLE);
868         return;
869     }
870
871     USER_CheckNotLock();
872
873 #if 0
874     if (event & 0x80000000)
875     {
876         /* FIXME: on 64-bit platforms we need to invent some other way for
877          * passing parameters, nId and nChildId can't hold full [W|L]PARAM.
878          * struct call_hook *hook = (LRESULT *)hWnd;
879          * wparam = hook->wparam;
880          * lparam = hook->lparam;
881          */
882         LRESULT *ret = (LRESULT *)hwnd;
883         INT id, code, unicode;
884
885         id = (dwEvent & 0x7fff0000) >> 16;
886         code = event & 0x7fff;
887         unicode = event & 0x8000;
888         *ret = HOOK_CallHooks(id, code, object_id, child_id, unicode);
889         return;
890     }
891 #endif
892
893     if (!find_first_hook(WH_WINEVENT, event, hwnd, object_id, child_id, &info)) return;
894
895     do
896     {
897         WINEVENTPROC proc = info.proc;
898         if (proc)
899         {
900             TRACE( "calling WH_WINEVENT hook %p event %x hwnd %p %x %x module %s\n",
901                    proc, event, hwnd, object_id, child_id, debugstr_w(info.module) );
902
903             if (!info.module[0] || (proc = get_hook_proc( proc, info.module )) != NULL)
904             {
905                 if (TRACE_ON(relay))
906                     DPRINTF( "%04x:Call winevent hook proc %p (hhook=%p,event=%x,hwnd=%p,object_id=%x,child_id=%x,tid=%04x,time=%x)\n",
907                              GetCurrentThreadId(), proc, info.handle, event, hwnd, object_id,
908                              child_id, GetCurrentThreadId(), GetCurrentTime());
909
910                 proc( info.handle, event, hwnd, object_id, child_id,
911                       GetCurrentThreadId(), GetCurrentTime());
912
913                 if (TRACE_ON(relay))
914                     DPRINTF( "%04x:Ret  winevent hook proc %p (hhook=%p,event=%x,hwnd=%p,object_id=%x,child_id=%x,tid=%04x,time=%x)\n",
915                              GetCurrentThreadId(), proc, info.handle, event, hwnd, object_id,
916                              child_id, GetCurrentThreadId(), GetCurrentTime());
917             }
918         }
919         else
920             break;
921     }
922     while (find_next_hook(event, hwnd, object_id, child_id, &info));
923
924     find_hook_close(WH_WINEVENT);
925 }
926
927
928 /***********************************************************************
929  *           IsWinEventHookInstalled                       [USER32.@]
930  *
931  * Determine if an event hook is installed for an event.
932  *
933  * PARAMS
934  *  dwEvent  [I] Id of the event
935  *
936  * RETURNS
937  *  TRUE,  If there are any hooks installed for the event.
938  *  FALSE, Otherwise.
939  *
940  * BUGS
941  *  Not implemented.
942  */
943 BOOL WINAPI IsWinEventHookInstalled(DWORD dwEvent)
944 {
945     /* FIXME: Needed by Office 2007 installer */
946     WARN("(%d)-stub!\n", dwEvent);
947     return TRUE;
948 }