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