Rewrote hook support to store the hook chain in the server.
[wine] / dlls / user / hook.c
1 /*
2  * Windows hook functions
3  *
4  * Copyright 2002 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * NOTES:
21  *   Status of the various hooks:
22  *     WH_MSGFILTER                 OK
23  *     WH_JOURNALRECORD             Partially implemented
24  *     WH_JOURNALPLAYBACK           Partially implemented
25  *     WH_KEYBOARD                  OK
26  *     WH_GETMESSAGE                OK (FIXME: A/W mapping?)
27  *     WH_CALLWNDPROC               OK (FIXME: A/W mapping?)
28  *     WH_CBT
29  *       HCBT_MOVESIZE              OK
30  *       HCBT_MINMAX                OK
31  *       HCBT_QS                    OK
32  *       HCBT_CREATEWND             OK
33  *       HCBT_DESTROYWND            OK
34  *       HCBT_ACTIVATE              OK
35  *       HCBT_CLICKSKIPPED          OK
36  *       HCBT_KEYSKIPPED            OK
37  *       HCBT_SYSCOMMAND            Not implemented
38  *       HCBT_SETFOCUS              OK
39  *     WH_SYSMSGFILTER              OK
40  *     WH_MOUSE                     OK
41  *     WH_HARDWARE                  Not supported in Win32
42  *     WH_DEBUG                     Not implemented
43  *     WH_SHELL
44  *       HSHELL_WINDOWCREATED       OK
45  *       HSHELL_WINDOWDESTROYED     OK
46  *       HSHELL_ACTIVATESHELLWINDOW Not implemented
47  *       HSHELL_WINDOWACTIVATED     Not implemented
48  *       HSHELL_GETMINRECT          Not implemented
49  *       HSHELL_REDRAW              Not implemented
50  *       HSHELL_TASKMAN             Not implemented
51  *       HSHELL_LANGUAGE            Not implemented
52  *       HSHELL_SYSMENU             Not implemented
53  *       HSHELL_ENDTASK             Not implemented
54  *       HSHELL_ACCESSIBILITYSTATE  Not implemented
55  *       HSHELL_APPCOMMAND          Not implemented
56  *       HSHELL_WINDOWREPLACED      Not implemented
57  *       HSHELL_WINDOWREPLACING     Not implemented
58  *     WH_FOREGROUNDIDLE            Not implemented
59  *     WH_CALLWNDPROCRET            OK (FIXME: A/W mapping?)
60  *     WH_KEYBOARD_LL               Implemented but should use SendMessage instead
61  *     WH_MOUSE_LL                  Implemented but should use SendMessage instead
62  */
63
64 #include "windef.h"
65 #include "winbase.h"
66 #include "winuser.h"
67 #include "winerror.h"
68 #include "heap.h"
69 #include "queue.h"
70 #include "win.h"
71 #include "wine/server.h"
72 #include "wine/debug.h"
73
74 WINE_DEFAULT_DEBUG_CHANNEL(hook);
75 WINE_DECLARE_DEBUG_CHANNEL(relay);
76
77 static const char * const hook_names[WH_MAXHOOK - WH_MINHOOK + 1] =
78 {
79     "WH_MSGFILTER",
80     "WH_JOURNALRECORD",
81     "WH_JOURNALPLAYBACK",
82     "WH_KEYBOARD",
83     "WH_GETMESSAGE",
84     "WH_CALLWNDPROC",
85     "WH_CBT",
86     "WH_SYSMSGFILTER",
87     "WH_MOUSE",
88     "WH_HARDWARE",
89     "WH_DEBUG",
90     "WH_SHELL",
91     "WH_FOREGROUNDIDLE",
92     "WH_CALLWNDPROCRET",
93     "WH_KEYBOARD_LL",
94     "WH_MOUSE_LL"
95 };
96
97
98 /***********************************************************************
99  *              set_windows_hook
100  *
101  * Implementation of SetWindowsHookExA and SetWindowsHookExW.
102  */
103 static HHOOK set_windows_hook( INT id, HOOKPROC proc, HINSTANCE inst, DWORD tid, BOOL unicode )
104 {
105     HHOOK handle = 0;
106
107     if (tid)  /* thread-local hook */
108     {
109         if (id == WH_JOURNALRECORD ||
110             id == WH_JOURNALPLAYBACK ||
111             id == WH_KEYBOARD_LL ||
112             id == WH_MOUSE_LL ||
113             id == WH_SYSMSGFILTER)
114         {
115             /* these can only be global */
116             SetLastError( ERROR_INVALID_PARAMETER );
117             return 0;
118         }
119     }
120     else  /* system-global hook */
121     {
122         if (!inst)
123         {
124             SetLastError( ERROR_INVALID_PARAMETER );
125             return 0;
126         }
127         FIXME( "system hook %s won't work right\n", hook_names[id-WH_MINHOOK] );
128     }
129
130     SERVER_START_REQ( set_hook )
131     {
132         req->id      = id;
133         req->tid     = tid;
134         req->proc    = proc;
135         req->unicode = unicode;
136         if (!wine_server_call_err( req )) handle = reply->handle;
137     }
138     SERVER_END_REQ;
139
140     TRACE( "%s %p %lx -> %x\n", hook_names[id-WH_MINHOOK], proc, tid, handle );
141     return handle;
142 }
143
144
145 /***********************************************************************
146  *              call_hook_AtoW
147  */
148 static LRESULT call_hook_AtoW( HOOKPROC proc, INT id, INT code, WPARAM wparam, LPARAM lparam )
149 {
150     LRESULT ret;
151
152     if (id != WH_CBT || code != HCBT_CREATEWND) ret = proc( code, wparam, lparam );
153     else
154     {
155         CBT_CREATEWNDA *cbtcwA = (CBT_CREATEWNDA *)lparam;
156         CBT_CREATEWNDW cbtcwW;
157         CREATESTRUCTW csW;
158
159         cbtcwW.lpcs = &csW;
160         cbtcwW.hwndInsertAfter = cbtcwA->hwndInsertAfter;
161         csW = *(CREATESTRUCTW *)cbtcwA->lpcs;
162
163         if (HIWORD(cbtcwA->lpcs->lpszName))
164             csW.lpszName = HEAP_strdupAtoW( GetProcessHeap(), 0, cbtcwA->lpcs->lpszName );
165         if (HIWORD(cbtcwA->lpcs->lpszClass))
166             csW.lpszClass = HEAP_strdupAtoW( GetProcessHeap(), 0, cbtcwA->lpcs->lpszClass );
167         ret = proc( code, wparam, (LPARAM)&cbtcwW );
168         cbtcwA->hwndInsertAfter = cbtcwW.hwndInsertAfter;
169         if (HIWORD(csW.lpszName)) HeapFree( GetProcessHeap(), 0, (LPWSTR)csW.lpszName );
170         if (HIWORD(csW.lpszClass)) HeapFree( GetProcessHeap(), 0, (LPWSTR)csW.lpszClass );
171     }
172     return ret;
173 }
174
175
176 /***********************************************************************
177  *              call_hook_WtoA
178  */
179 static LRESULT call_hook_WtoA( HOOKPROC proc, INT id, INT code, WPARAM wparam, LPARAM lparam )
180 {
181     LRESULT ret;
182
183     if (id != WH_CBT || code != HCBT_CREATEWND) ret = proc( code, wparam, lparam );
184     else
185     {
186         CBT_CREATEWNDW *cbtcwW = (CBT_CREATEWNDW *)lparam;
187         CBT_CREATEWNDA cbtcwA;
188         CREATESTRUCTA csA;
189
190         cbtcwA.lpcs = &csA;
191         cbtcwA.hwndInsertAfter = cbtcwW->hwndInsertAfter;
192         csA = *(CREATESTRUCTA *)cbtcwW->lpcs;
193
194         if (HIWORD(cbtcwW->lpcs->lpszName))
195             csA.lpszName = HEAP_strdupWtoA( GetProcessHeap(), 0, cbtcwW->lpcs->lpszName );
196         if (HIWORD(cbtcwW->lpcs->lpszClass))
197             csA.lpszClass = HEAP_strdupWtoA( GetProcessHeap(), 0, cbtcwW->lpcs->lpszClass );
198         ret = proc( code, wparam, (LPARAM)&cbtcwA );
199         cbtcwW->hwndInsertAfter = cbtcwA.hwndInsertAfter;
200         if (HIWORD(csA.lpszName)) HeapFree( GetProcessHeap(), 0, (LPSTR)csA.lpszName );
201         if (HIWORD(csA.lpszClass)) HeapFree( GetProcessHeap(), 0, (LPSTR)csA.lpszClass );
202     }
203     return ret;
204 }
205
206
207 /***********************************************************************
208  *              call_hook
209  */
210 static LRESULT call_hook( HOOKPROC proc, INT id, INT code, WPARAM wparam, LPARAM lparam,
211                           BOOL prev_unicode, BOOL next_unicode )
212 {
213     LRESULT ret;
214
215     if (TRACE_ON(relay))
216         DPRINTF( "%08lx:Call hook proc %p (id=%s,code=%x,wp=%08x,lp=%08lx)\n",
217                  GetCurrentThreadId(), proc, hook_names[id-WH_MINHOOK], code, wparam, lparam );
218
219     if (!prev_unicode == !next_unicode) ret = proc( code, wparam, lparam );
220     else if (prev_unicode) ret = call_hook_WtoA( proc, id, code, wparam, lparam );
221     else ret = call_hook_AtoW( proc, id, code, wparam, lparam );
222
223     if (TRACE_ON(relay))
224         DPRINTF( "%08lx:Ret  hook proc %p (id=%s,code=%x,wp=%08x,lp=%08lx) retval=%08lx\n",
225                  GetCurrentThreadId(), proc, hook_names[id-WH_MINHOOK], code, wparam, lparam, ret );
226
227     return ret;
228 }
229
230
231 /***********************************************************************
232  *              HOOK_CallHooks
233  */
234 LRESULT HOOK_CallHooks( INT id, INT code, WPARAM wparam, LPARAM lparam, BOOL unicode )
235 {
236     MESSAGEQUEUE *queue = QUEUE_Current();
237     HOOKPROC proc = NULL;
238     HHOOK prev;
239     BOOL unicode_hook = FALSE;
240     LRESULT ret = 0;
241     int locks;
242
243     if (!queue) return 0;
244     prev = queue->hook;
245     SERVER_START_REQ( start_hook_chain )
246     {
247         req->id = id;
248         if (!wine_server_call_err( req ))
249         {
250             queue->hook = reply->handle;
251             proc = reply->proc;
252             unicode_hook = reply->unicode;
253         }
254     }
255     SERVER_END_REQ;
256
257     if (proc)
258     {
259         TRACE( "calling hook %p %s code %x wp %x lp %lx\n",
260                proc, hook_names[id-WH_MINHOOK], code, wparam, lparam );
261
262         locks = WIN_SuspendWndsLock();
263         ret = call_hook( proc, id, code, wparam, lparam, unicode, unicode_hook );
264         WIN_RestoreWndsLock( locks );
265
266         SERVER_START_REQ( finish_hook_chain )
267         {
268             req->id = id;
269             wine_server_call( req );
270         }
271         SERVER_END_REQ;
272     }
273
274     queue->hook = prev;
275     return ret;
276 }
277
278
279 /***********************************************************************
280  *           HOOK_IsHooked
281  */
282 BOOL HOOK_IsHooked( INT id )
283 {
284     return TRUE;  /* FIXME */
285 }
286
287
288 /***********************************************************************
289  *              SetWindowsHookA (USER32.@)
290  */
291 HHOOK WINAPI SetWindowsHookA( INT id, HOOKPROC proc )
292 {
293     return SetWindowsHookExA( id, proc, 0, GetCurrentThreadId() );
294 }
295
296
297 /***********************************************************************
298  *              SetWindowsHookW (USER32.@)
299  */
300 HHOOK WINAPI SetWindowsHookW( INT id, HOOKPROC proc )
301 {
302     return SetWindowsHookExW( id, proc, 0, GetCurrentThreadId() );
303 }
304
305
306 /***********************************************************************
307  *              SetWindowsHookExA (USER32.@)
308  */
309 HHOOK WINAPI SetWindowsHookExA( INT id, HOOKPROC proc, HINSTANCE inst, DWORD tid )
310 {
311     return set_windows_hook( id, proc, inst, tid, FALSE );
312 }
313
314 /***********************************************************************
315  *              SetWindowsHookExW (USER32.@)
316  */
317 HHOOK WINAPI SetWindowsHookExW( INT id, HOOKPROC proc, HINSTANCE inst, DWORD tid )
318 {
319     return set_windows_hook( id, proc, inst, tid, TRUE );
320 }
321
322
323 /***********************************************************************
324  *              UnhookWindowsHook (USER32.@)
325  */
326 BOOL WINAPI UnhookWindowsHook( INT id, HOOKPROC proc )
327 {
328     BOOL ret;
329
330     TRACE( "%s %p\n", hook_names[id-WH_MINHOOK], proc );
331
332     SERVER_START_REQ( remove_hook )
333     {
334         req->id   = id;
335         req->proc = proc;
336         ret = !wine_server_call_err( req );
337     }
338     SERVER_END_REQ;
339     return ret;
340 }
341
342
343
344 /***********************************************************************
345  *              UnhookWindowsHookEx (USER32.@)
346  */
347 BOOL WINAPI UnhookWindowsHookEx( HHOOK hhook )
348 {
349     BOOL ret;
350
351     TRACE( "%x\n", hhook );
352
353     SERVER_START_REQ( remove_hook )
354     {
355         req->handle = hhook;
356         ret = !wine_server_call_err( req );
357     }
358     SERVER_END_REQ;
359     return ret;
360 }
361
362
363 /***********************************************************************
364  *              CallNextHookEx (USER32.@)
365  */
366 LRESULT WINAPI CallNextHookEx( HHOOK hhook, INT code, WPARAM wparam, LPARAM lparam )
367 {
368     MESSAGEQUEUE *queue = QUEUE_Current();
369     HHOOK prev;
370     HOOKPROC proc = NULL;
371     INT id = 0;
372     BOOL prev_unicode = FALSE, next_unicode = FALSE;
373     LRESULT ret = 0;
374
375     if (!queue) return 0;
376     prev = queue->hook;
377     SERVER_START_REQ( get_next_hook )
378     {
379         req->handle = prev;
380         if (!wine_server_call_err( req ))
381         {
382             queue->hook  = reply->next;
383             id           = reply->id;
384             proc         = reply->proc;
385             prev_unicode = reply->prev_unicode;
386             next_unicode = reply->next_unicode;
387         }
388     }
389     SERVER_END_REQ;
390     if (proc)
391     {
392         TRACE( "calling hook %p %s code %x wp %x lp %lx\n",
393                proc, hook_names[id-WH_MINHOOK], code, wparam, lparam );
394
395         return call_hook( proc, id, code, wparam, lparam, prev_unicode, next_unicode );
396     }
397     queue->hook = prev;
398     return ret;
399 }
400
401
402 /***********************************************************************
403  *              CallMsgFilterA (USER32.@)
404  */
405 BOOL WINAPI CallMsgFilterA( LPMSG msg, INT code )
406 {
407     if (HOOK_CallHooks( WH_SYSMSGFILTER, code, 0, (LPARAM)msg, FALSE )) return TRUE;
408     return HOOK_CallHooks( WH_MSGFILTER, code, 0, (LPARAM)msg, FALSE );
409 }
410
411
412 /***********************************************************************
413  *              CallMsgFilterW (USER32.@)
414  */
415 BOOL WINAPI CallMsgFilterW( LPMSG msg, INT code )
416 {
417     if (HOOK_CallHooks( WH_SYSMSGFILTER, code, 0, (LPARAM)msg, TRUE )) return TRUE;
418     return HOOK_CallHooks( WH_MSGFILTER, code, 0, (LPARAM)msg, TRUE );
419 }
420
421
422 /***********************************************************************
423  *           SetWinEventHook                            [USER32.@]
424  *
425  * Set up an event hook for a set of events.
426  *
427  * PARAMS
428  *  dwMin     [I] Lowest event handled by pfnProc
429  *  dwMax     [I] Highest event handled by pfnProc
430  *  hModule   [I] DLL containing pfnProc
431  *  pfnProc   [I] Callback event hook function
432  *  dwProcess [I] Process to get events from, or 0 for all processes
433  *  dwThread  [I] Thread to get events from, or 0 for all threads
434  *  dwFlags   [I] Flags indicating the status of pfnProc
435  *
436  * RETURNS
437  *  Success: A handle representing the hook.
438  *  Failure: A NULL handle.
439  *
440  * BUGS
441  *  Not implemented.
442  */
443 HWINEVENTHOOK WINAPI SetWinEventHook(DWORD dwMin, DWORD dwMax, HMODULE hModule,
444                                      WINEVENTPROC pfnProc, DWORD dwProcess,
445                                      DWORD dwThread, DWORD dwFlags)
446 {
447     FIXME("(%ld,%ld,0x%08x,%p,%ld,%ld,0x%08lx)-stub!\n", dwMin, dwMax, hModule,
448           pfnProc, dwProcess, dwThread, dwFlags);
449     return 0;
450 }
451
452
453 /***********************************************************************
454  *           UnhookWinEvent                             [USER32.@]
455  *
456  * Remove an event hook for a set of events.
457  *
458  * PARAMS
459  *  hEventHook [I] Event hook to remove
460  *
461  * RETURNS
462  *  Success: TRUE. The event hook has been removed.
463  *  Failure: FALSE, if hEventHook is invalid.
464  *
465  * BUGS
466  *  Not implemented.
467  */
468 BOOL WINAPI UnhookWinEvent(HWINEVENTHOOK hEventHook)
469 {
470     FIXME("(%x)-stub!\n", hEventHook);
471
472     return (hEventHook != 0);
473 }
474
475
476 /***********************************************************************
477  *           NotifyWinEvent                             [USER32.@]
478  *
479  * Inform the OS that an event has occurred.
480  *
481  * PARAMS
482  *  dwEvent  [I] Id of the event
483  *  hWnd     [I] Window holding the object that created the event
484  *  nId      [I] Type of object that created the event
485  *  nChildId [I] Child object of nId, or CHILDID_SELF.
486  *
487  * RETURNS
488  *  Nothing.
489  *
490  * BUGS
491  *  Not implemented.
492  */
493 void WINAPI NotifyWinEvent(DWORD dwEvent, HWND hWnd, LONG nId, LONG nChildId)
494 {
495     FIXME("(%ld,0x%08x,%ld,%ld)-stub!\n", dwEvent, hWnd, nId, nChildId);
496 }
497
498
499 /***********************************************************************
500  *           IsWinEventHookInstalled                       [USER32.@]
501  *
502  * Determine if an event hook is installed for an event.
503  *
504  * PARAMS
505  *  dwEvent  [I] Id of the event
506  *
507  * RETURNS
508  *  TRUE,  If there are any hooks installed for the event.
509  *  FALSE, Otherwise.
510  *
511  * BUGS
512  *  Not implemented.
513  */
514 BOOL WINAPI IsWinEventHookInstalled(DWORD dwEvent)
515 {
516     FIXME("(%ld)-stub!\n", dwEvent);
517     return TRUE;
518 }