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