* Sync to trunk HEAD (r53473).
[reactos.git] / dll / win32 / user32 / windows / hook.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /*
20 *
21 * PROJECT: ReactOS user32.dll
22 * FILE: dll/win32/user32/windows/hook.c
23 * PURPOSE: Hooks
24 * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
25 * UPDATE HISTORY:
26 * 09-05-2001 CSH Created
27 */
28
29 /* INCLUDES ******************************************************************/
30
31 #include <user32.h>
32
33 #include <wine/debug.h>
34
35 WINE_DEFAULT_DEBUG_CHANNEL(user32);
36
37 typedef struct _NOTIFYEVENT
38 {
39 DWORD event;
40 LONG idObject;
41 LONG idChild;
42 DWORD flags;
43 } NOTIFYEVENT, *PNOTIFYEVENT;
44
45 /* PRIVATE FUNCTIONS *********************************************************/
46
47 static
48 DWORD
49 FASTCALL
50 GetMaskFromEvent(DWORD Event)
51 {
52 DWORD Ret = 0;
53
54 if ( Event > EVENT_OBJECT_STATECHANGE )
55 {
56 if ( Event == EVENT_OBJECT_LOCATIONCHANGE ) return SRV_EVENT_LOCATIONCHANGE;
57 if ( Event == EVENT_OBJECT_NAMECHANGE ) return SRV_EVENT_NAMECHANGE;
58 if ( Event == EVENT_OBJECT_VALUECHANGE ) return SRV_EVENT_VALUECHANGE;
59 return SRV_EVENT_CREATE;
60 }
61
62 if ( Event == EVENT_OBJECT_STATECHANGE ) return SRV_EVENT_STATECHANGE;
63
64 Ret = SRV_EVENT_RUNNING;
65
66 if ( Event < EVENT_SYSTEM_MENUSTART ) return SRV_EVENT_CREATE;
67
68 if ( Event <= EVENT_SYSTEM_MENUPOPUPEND )
69 {
70 Ret = SRV_EVENT_MENU;
71 }
72 else
73 {
74 if ( Event <= EVENT_CONSOLE_CARET-1 ) return SRV_EVENT_CREATE;
75 if ( Event <= EVENT_CONSOLE_END_APPLICATION ) return SRV_EVENT_END_APPLICATION;
76 if ( Event != EVENT_OBJECT_FOCUS ) return SRV_EVENT_CREATE;
77 }
78 return Ret;
79 }
80
81 static
82 HHOOK
83 FASTCALL
84 IntSetWindowsHook(
85 int idHook,
86 HOOKPROC lpfn,
87 HINSTANCE hMod,
88 DWORD dwThreadId,
89 BOOL bAnsi)
90 {
91 WCHAR ModuleName[MAX_PATH];
92 UNICODE_STRING USModuleName;
93
94 if (NULL != hMod)
95 {
96 if (0 == GetModuleFileNameW(hMod, ModuleName, MAX_PATH))
97 {
98 return NULL;
99 }
100 RtlInitUnicodeString(&USModuleName, ModuleName);
101 }
102 else
103 {
104 RtlInitUnicodeString(&USModuleName, NULL);
105 }
106
107 return NtUserSetWindowsHookEx(hMod, &USModuleName, dwThreadId, idHook, lpfn, bAnsi);
108 }
109
110 /*
111 Since ReactOS uses User32 as the main message source this was needed.
112 Base on the funny rules from the wine tests it left it with this option.
113 8^(
114 */
115 VOID
116 FASTCALL
117 IntNotifyWinEvent(
118 DWORD event,
119 HWND hwnd,
120 LONG idObject,
121 LONG idChild,
122 DWORD flags
123 )
124 {
125 NOTIFYEVENT ne;
126 ne.event = event;
127 ne.idObject = idObject;
128 ne.idChild = idChild;
129 ne.flags = flags;
130 if (gpsi->dwInstalledEventHooks & GetMaskFromEvent(event))
131 NtUserxNotifyWinEvent(hwnd, &ne);
132 }
133
134 /* FUNCTIONS *****************************************************************/
135
136 #if 0
137 BOOL
138 WINAPI
139 CallMsgFilter(
140 LPMSG lpMsg,
141 int nCode)
142 {
143 UNIMPLEMENTED;
144 return FALSE;
145 }
146 #endif
147
148 /*
149 * @implemented
150 */
151 BOOL
152 WINAPI
153 CallMsgFilterA(
154 LPMSG lpMsg,
155 int nCode)
156 {
157 MSG Msg;
158 if ( NtCurrentTeb()->Win32ThreadInfo &&
159 (ISITHOOKED(WH_MSGFILTER) || ISITHOOKED(WH_SYSMSGFILTER)) )
160 {
161 if ( lpMsg->message & ~WM_MAXIMUM )
162 {
163 SetLastError(ERROR_INVALID_PARAMETER);
164 return FALSE;
165 }
166 RtlCopyMemory(&Msg, lpMsg, sizeof(MSG));
167 return NtUserCallMsgFilter( &Msg, nCode);
168 }
169 return FALSE;
170 }
171
172
173 /*
174 * @implemented
175 */
176 BOOL
177 WINAPI
178 CallMsgFilterW(
179 LPMSG lpMsg,
180 int nCode)
181 {
182 MSG Msg;
183 if ( NtCurrentTeb()->Win32ThreadInfo &&
184 (ISITHOOKED(WH_MSGFILTER) || ISITHOOKED(WH_SYSMSGFILTER)) )
185 {
186 if ( lpMsg->message & ~WM_MAXIMUM )
187 {
188 SetLastError(ERROR_INVALID_PARAMETER);
189 return FALSE;
190 }
191 RtlCopyMemory(&Msg, lpMsg, sizeof(MSG));
192 return NtUserCallMsgFilter( &Msg, nCode);
193 }
194 return FALSE;
195 }
196
197
198 /*
199 * @implemented
200 */
201 LRESULT
202 WINAPI
203 CallNextHookEx(
204 HHOOK Hook, // Windows NT/XP/2003: Ignored.
205 int Code,
206 WPARAM wParam,
207 LPARAM lParam)
208 {
209 PCLIENTINFO ClientInfo;
210 DWORD Flags, Save;
211 PHOOK pHook, phkNext;
212 LRESULT lResult = 0;
213
214 GetConnected();
215
216 ClientInfo = GetWin32ClientInfo();
217
218 if (!ClientInfo->phkCurrent) return 0;
219
220 pHook = DesktopPtrToUser(ClientInfo->phkCurrent);
221
222 if (!pHook->phkNext) return 0; // Nothing to do....
223
224 phkNext = DesktopPtrToUser(pHook->phkNext);
225
226 if ( phkNext->HookId == WH_CALLWNDPROC ||
227 phkNext->HookId == WH_CALLWNDPROCRET)
228 {
229 Save = ClientInfo->dwHookData;
230 Flags = ClientInfo->CI_flags & CI_CURTHPRHOOK;
231 // wParam: If the message was sent by the current thread/process, it is
232 // nonzero; otherwise, it is zero.
233 if (wParam) ClientInfo->CI_flags |= CI_CURTHPRHOOK;
234 else ClientInfo->CI_flags &= ~CI_CURTHPRHOOK;
235
236 if (phkNext->HookId == WH_CALLWNDPROC)
237 {
238 PCWPSTRUCT pCWP = (PCWPSTRUCT)lParam;
239
240 NtUserMessageCall( pCWP->hwnd,
241 pCWP->message,
242 pCWP->wParam,
243 pCWP->lParam,
244 (ULONG_PTR)&lResult,
245 FNID_CALLWNDPROC,
246 phkNext->Ansi);
247 }
248 else
249 {
250 PCWPRETSTRUCT pCWPR = (PCWPRETSTRUCT)lParam;
251
252 ClientInfo->dwHookData = pCWPR->lResult;
253
254 NtUserMessageCall( pCWPR->hwnd,
255 pCWPR->message,
256 pCWPR->wParam,
257 pCWPR->lParam,
258 (ULONG_PTR)&lResult,
259 FNID_CALLWNDPROCRET,
260 phkNext->Ansi);
261 }
262 ClientInfo->CI_flags ^= ((ClientInfo->CI_flags ^ Flags) & CI_CURTHPRHOOK);
263 ClientInfo->dwHookData = Save;
264 }
265 else
266 lResult = NtUserCallNextHookEx(Code, wParam, lParam, pHook->Ansi);
267
268 return lResult;
269 }
270
271
272 /*
273 * @implemented
274 */
275 HHOOK
276 WINAPI
277 SetWindowsHookW(int idHook, HOOKPROC lpfn)
278 {
279 DWORD ThreadId = PtrToUint(NtCurrentTeb()->ClientId.UniqueThread);
280 return IntSetWindowsHook(idHook, lpfn, NULL, ThreadId, FALSE);
281 // return NtUserSetWindowsHookAW(idHook, lpfn, FALSE);
282 }
283
284 /*
285 * @implemented
286 */
287 HHOOK
288 WINAPI
289 SetWindowsHookA(int idHook, HOOKPROC lpfn)
290 {
291 DWORD ThreadId = PtrToUint(NtCurrentTeb()->ClientId.UniqueThread);
292 return IntSetWindowsHook(idHook, lpfn, NULL, ThreadId, TRUE);
293 // return NtUserSetWindowsHookAW(idHook, lpfn, TRUE);
294 }
295
296 /*
297 * @unimplemented
298 */
299 BOOL
300 WINAPI
301 DeregisterShellHookWindow(HWND hWnd)
302 {
303 return NtUserxDeregisterShellHookWindow(hWnd);
304 }
305
306 /*
307 * @unimplemented
308 */
309 BOOL
310 WINAPI
311 RegisterShellHookWindow(HWND hWnd)
312 {
313 return NtUserxRegisterShellHookWindow(hWnd);
314 }
315
316 /*
317 * @implemented
318 */
319 BOOL
320 WINAPI
321 UnhookWindowsHook ( int nCode, HOOKPROC pfnFilterProc )
322 {
323 return NtUserxUnhookWindowsHook(nCode, pfnFilterProc);
324 }
325
326 /*
327 * @implemented
328 */
329 VOID
330 WINAPI
331 NotifyWinEvent(
332 DWORD event,
333 HWND hwnd,
334 LONG idObject,
335 LONG idChild
336 )
337 {
338 // "Servers call NotifyWinEvent to announce the event to the system after the
339 // event has occurred; they must never notify the system of an event before
340 // the event has occurred." msdn on NotifyWinEvent.
341 if (gpsi->dwInstalledEventHooks & GetMaskFromEvent(event)) // Check to see.
342 NtUserNotifyWinEvent(event, hwnd, idObject, idChild);
343 }
344
345 /*
346 * @implemented
347 */
348 HWINEVENTHOOK
349 WINAPI
350 SetWinEventHook(
351 UINT eventMin,
352 UINT eventMax,
353 HMODULE hmodWinEventProc,
354 WINEVENTPROC pfnWinEventProc,
355 DWORD idProcess,
356 DWORD idThread,
357 UINT dwFlags
358 )
359 {
360 WCHAR ModuleName[MAX_PATH];
361 UNICODE_STRING USModuleName;
362
363 if ((hmodWinEventProc != NULL) && (dwFlags & WINEVENT_INCONTEXT))
364 {
365 if (0 == GetModuleFileNameW(hmodWinEventProc, ModuleName, MAX_PATH))
366 {
367 return NULL;
368 }
369 RtlInitUnicodeString(&USModuleName, ModuleName);
370 }
371 else
372 {
373 RtlInitUnicodeString(&USModuleName, NULL);
374 }
375
376 return NtUserSetWinEventHook(eventMin,
377 eventMax,
378 hmodWinEventProc,
379 &USModuleName,
380 pfnWinEventProc,
381 idProcess,
382 idThread,
383 dwFlags);
384 }
385
386 /*
387 * @implemented
388 */
389 BOOL
390 WINAPI
391 IsWinEventHookInstalled(
392 DWORD event)
393 {
394 if ((PTHREADINFO)NtCurrentTeb()->Win32ThreadInfo)
395 {
396 return (gpsi->dwInstalledEventHooks & GetMaskFromEvent(event)) != 0;
397 }
398 return FALSE;
399 }
400
401 /*
402 * @implemented
403 */
404 HHOOK
405 WINAPI
406 SetWindowsHookExA(
407 int idHook,
408 HOOKPROC lpfn,
409 HINSTANCE hMod,
410 DWORD dwThreadId)
411 {
412 return IntSetWindowsHook(idHook, lpfn, hMod, dwThreadId, TRUE);
413 }
414
415
416 /*
417 * @implemented
418 */
419 HHOOK
420 WINAPI
421 SetWindowsHookExW(
422 int idHook,
423 HOOKPROC lpfn,
424 HINSTANCE hMod,
425 DWORD dwThreadId)
426 {
427 return IntSetWindowsHook(idHook, lpfn, hMod, dwThreadId, FALSE);
428 }
429
430 NTSTATUS WINAPI
431 User32CallHookProcFromKernel(PVOID Arguments, ULONG ArgumentLength)
432 {
433 PHOOKPROC_CALLBACK_ARGUMENTS Common;
434 CREATESTRUCTW Csw;
435 CBT_CREATEWNDW CbtCreatewndw;
436 PHOOKPROC_CBT_CREATEWND_EXTRA_ARGUMENTS CbtCreatewndExtra = NULL;
437 KBDLLHOOKSTRUCT KeyboardLlData, *pKeyboardLlData;
438 MSLLHOOKSTRUCT MouseLlData, *pMouseLlData;
439 MSG *pcMsg, *pMsg;
440 PMOUSEHOOKSTRUCT pMHook;
441 CWPSTRUCT CWP, *pCWP;
442 CWPRETSTRUCT CWPR, *pCWPR;
443 PRECTL prl;
444 LPCBTACTIVATESTRUCT pcbtas;
445 WPARAM wParam = 0;
446 LPARAM lParam = 0;
447 LRESULT Result = 0;
448 BOOL Hit = FALSE;
449
450 Common = (PHOOKPROC_CALLBACK_ARGUMENTS) Arguments;
451
452 switch(Common->HookId)
453 {
454 case WH_CBT:
455 {
456 //ERR("WH_CBT: Code %d\n", Common->Code);
457 switch(Common->Code)
458 {
459 case HCBT_CREATEWND:
460 CbtCreatewndExtra = (PHOOKPROC_CBT_CREATEWND_EXTRA_ARGUMENTS)
461 ((PCHAR) Common + Common->lParam);
462 RtlCopyMemory(&Csw, &CbtCreatewndExtra->Cs, sizeof(CREATESTRUCTW));
463 CbtCreatewndw.lpcs = &Csw;
464 CbtCreatewndw.hwndInsertAfter = CbtCreatewndExtra->WndInsertAfter;
465 wParam = Common->wParam;
466 lParam = (LPARAM) &CbtCreatewndw;
467 //ERR("HCBT_CREATEWND: hWnd 0x%x Name 0x%x Class 0x%x\n", Common->wParam, Csw.lpszName, Csw.lpszClass);
468 break;
469 case HCBT_CLICKSKIPPED:
470 pMHook = (PMOUSEHOOKSTRUCT)((PCHAR) Common + Common->lParam);
471 lParam = (LPARAM) pMHook;
472 break;
473 case HCBT_MOVESIZE:
474 prl = (PRECTL)((PCHAR) Common + Common->lParam);
475 lParam = (LPARAM) prl;
476 break;
477 case HCBT_ACTIVATE:
478 pcbtas = (LPCBTACTIVATESTRUCT)((PCHAR) Common + Common->lParam);
479 lParam = (LPARAM) pcbtas;
480 break;
481 case HCBT_KEYSKIPPED: /* The rest SEH support */
482 case HCBT_MINMAX:
483 case HCBT_SETFOCUS:
484 case HCBT_SYSCOMMAND:
485 case HCBT_DESTROYWND:
486 case HCBT_QS:
487 wParam = Common->wParam;
488 lParam = Common->lParam;
489 break;
490 default:
491 ERR("HCBT_ not supported = %d\n", Common->Code);
492 return ZwCallbackReturn(NULL, 0, STATUS_NOT_SUPPORTED);
493 }
494
495 if (Common->Proc)
496 {
497 _SEH2_TRY
498 {
499 Result = Common->Proc(Common->Code, wParam, lParam);
500 }
501 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
502 {
503 Hit = TRUE;
504 }
505 _SEH2_END;
506 }
507 else
508 {
509 ERR("Null Proc! Common = 0x%x, Proc = 0x%x\n",Common,Common->Proc);
510 }
511 switch(Common->Code)
512 {
513 case HCBT_CREATEWND:
514 CbtCreatewndExtra->WndInsertAfter = CbtCreatewndw.hwndInsertAfter;
515 CbtCreatewndExtra->Cs.x = CbtCreatewndw.lpcs->x;
516 CbtCreatewndExtra->Cs.y = CbtCreatewndw.lpcs->y;
517 CbtCreatewndExtra->Cs.cx = CbtCreatewndw.lpcs->cx;
518 CbtCreatewndExtra->Cs.cy = CbtCreatewndw.lpcs->cy;
519 break;
520 }
521 break;
522 }
523 case WH_KEYBOARD_LL:
524 //ERR("WH_KEYBOARD_LL: Code %d, wParam %d\n",Common->Code,Common->wParam);
525 pKeyboardLlData = (PKBDLLHOOKSTRUCT)((PCHAR) Common + Common->lParam);
526 RtlCopyMemory(&KeyboardLlData, pKeyboardLlData, sizeof(KBDLLHOOKSTRUCT));
527 Result = Common->Proc(Common->Code, Common->wParam, (LPARAM) &KeyboardLlData);
528 break;
529 case WH_MOUSE_LL:
530 //ERR("WH_MOUSE_LL: Code %d, wParam %d\n",Common->Code,Common->wParam);
531 pMouseLlData = (PMSLLHOOKSTRUCT)((PCHAR) Common + Common->lParam);
532 RtlCopyMemory(&MouseLlData, pMouseLlData, sizeof(MSLLHOOKSTRUCT));
533 Result = Common->Proc(Common->Code, Common->wParam, (LPARAM) &MouseLlData);
534 break;
535 case WH_MOUSE: /* SEH support */
536 pMHook = (PMOUSEHOOKSTRUCT)((PCHAR) Common + Common->lParam);
537 _SEH2_TRY
538 {
539 Result = Common->Proc(Common->Code, Common->wParam, (LPARAM) pMHook);
540 }
541 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
542 {
543 Hit = TRUE;
544 }
545 _SEH2_END;
546 break;
547 case WH_CALLWNDPROC:
548 // ERR("WH_CALLWNDPROC: Code %d, wParam %d\n",Common->Code,Common->wParam);
549 pCWP = (PCWPSTRUCT)((PCHAR) Common + Common->lParam);
550 RtlCopyMemory(&CWP, pCWP, sizeof(CWPSTRUCT));
551 Result = Common->Proc(Common->Code, Common->wParam, (LPARAM) &CWP);
552 break;
553 case WH_CALLWNDPROCRET:
554 pCWPR = (PCWPRETSTRUCT)((PCHAR) Common + Common->lParam);
555 RtlCopyMemory(&CWPR, pCWPR, sizeof(CWPRETSTRUCT));
556 Result = Common->Proc(Common->Code, Common->wParam, (LPARAM) &CWPR);
557 break;
558 case WH_MSGFILTER: /* All SEH support */
559 case WH_SYSMSGFILTER:
560 case WH_GETMESSAGE:
561 pMsg = (PMSG)((PCHAR) Common + Common->lParam);
562 pcMsg = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MSG));
563 RtlCopyMemory(pcMsg, pMsg, sizeof(MSG));
564 // ERR("pMsg %d pcMsg %d\n",pMsg->message, pcMsg->message);
565 _SEH2_TRY
566 {
567 Result = Common->Proc(Common->Code, Common->wParam, (LPARAM) pcMsg);
568 }
569 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
570 {
571 Hit = TRUE;
572 }
573 _SEH2_END;
574 if (!Hit && Common->HookId == WH_GETMESSAGE)
575 RtlCopyMemory(pMsg, pcMsg, sizeof(MSG));
576 HeapFree( GetProcessHeap(), 0, pcMsg );
577 break;
578 case WH_KEYBOARD:
579 case WH_SHELL:
580 Result = Common->Proc(Common->Code, Common->wParam, Common->lParam);
581 break;
582 case WH_FOREGROUNDIDLE: /* <-- SEH support */
583 _SEH2_TRY
584 {
585 Result = Common->Proc(Common->Code, Common->wParam, Common->lParam);
586 }
587 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
588 {
589 Hit = TRUE;
590 }
591 _SEH2_END;
592 break;
593 default:
594 return ZwCallbackReturn(NULL, 0, STATUS_NOT_SUPPORTED);
595 }
596 if (Hit)
597 {
598 ERR("Hook Exception! Id: %d, Code %d, Proc 0x%x\n",Common->HookId,Common->Code,Common->Proc);
599 }
600 return ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS);
601 }
602
603 NTSTATUS WINAPI
604 User32CallEventProcFromKernel(PVOID Arguments, ULONG ArgumentLength)
605 {
606 PEVENTPROC_CALLBACK_ARGUMENTS Common;
607
608 Common = (PEVENTPROC_CALLBACK_ARGUMENTS) Arguments;
609
610 Common->Proc(Common->hook,
611 Common->event,
612 Common->hwnd,
613 Common->idObject,
614 Common->idChild,
615 Common->dwEventThread,
616 Common->dwmsEventTime);
617
618 return ZwCallbackReturn(NULL, 0, STATUS_SUCCESS);
619 }
620
621
622