[Win32k]
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / hook.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Window hooks
5 * FILE: subsystems/win32/win32k/ntuser/hook.c
6 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * James Tabor (james.tabor@rectos.org)
8 *
9 * REVISION HISTORY:
10 * 06-06-2001 CSH Created
11 * NOTE: Most of this code was adapted from Wine,
12 * Copyright (C) 2002 Alexandre Julliard
13 */
14
15 #include <win32k.h>
16
17 #define NDEBUG
18 #include <debug.h>
19
20 typedef struct _HOOKPACK
21 {
22 PHOOK pHk;
23 LPARAM lParam;
24 } HOOKPACK, *PHOOKPACK;
25
26 /* PRIVATE FUNCTIONS *********************************************************/
27
28 static
29 LRESULT
30 FASTCALL
31 IntCallLowLevelHook( PHOOK Hook,
32 INT Code,
33 WPARAM wParam,
34 LPARAM lParam)
35 {
36 NTSTATUS Status;
37 PTHREADINFO pti;
38 PHOOKPACK pHP;
39 ULONG_PTR uResult = 0;
40
41 if (Hook->Thread)
42 pti = Hook->Thread->Tcb.Win32Thread;
43 else
44 pti = Hook->head.pti;
45
46 pHP = ExAllocatePoolWithTag(NonPagedPool, sizeof(HOOKPACK), TAG_HOOK);
47 if (!pHP) return 0;
48
49 pHP->pHk = Hook;
50 pHP->lParam = lParam;
51
52 /* FIXME should get timeout from
53 * HKEY_CURRENT_USER\Control Panel\Desktop\LowLevelHooksTimeout */
54 Status = co_MsqSendMessage( pti->MessageQueue,
55 IntToPtr(Code), // hWnd
56 Hook->HookId, // Msg
57 wParam,
58 (LPARAM)pHP,
59 5000,
60 TRUE,
61 MSQ_ISHOOK,
62 &uResult);
63 if (!NT_SUCCESS(Status))
64 {
65 DPRINT1("Error Hook Call SendMsg. %d Status: 0x%x\n", Hook->HookId, Status);
66 ExFreePoolWithTag(pHP, TAG_HOOK);
67 }
68 return NT_SUCCESS(Status) ? uResult : 0;
69 }
70
71 static
72 LRESULT
73 FASTCALL
74 co_HOOK_CallHookNext( PHOOK Hook,
75 INT Code,
76 WPARAM wParam,
77 LPARAM lParam)
78 {
79 if ( (Hook->Thread != PsGetCurrentThread()) && (Hook->Thread != NULL) )
80 {
81 DPRINT1("Calling Next HOOK from another Thread. %d\n", Hook->HookId);
82 return IntCallLowLevelHook(Hook, Code, wParam, lParam);
83 }
84
85 DPRINT("Calling Next HOOK %d\n", Hook->HookId);
86
87 return co_IntCallHookProc( Hook->HookId,
88 Code,
89 wParam,
90 lParam,
91 Hook->Proc,
92 Hook->Ansi,
93 &Hook->ModuleName);
94 }
95
96 //
97 // Dispatch MsgQueue Hook Call processor!
98 //
99 LRESULT
100 FASTCALL
101 co_CallHook( INT HookId,
102 INT Code,
103 WPARAM wParam,
104 LPARAM lParam)
105 {
106 LRESULT Result;
107 PHOOK phk;
108 PHOOKPACK pHP = (PHOOKPACK)lParam;
109
110 phk = pHP->pHk;
111 /* The odds are high for this to be a Global call. */
112 Result = co_IntCallHookProc( HookId,
113 Code,
114 wParam,
115 pHP->lParam,
116 phk->Proc,
117 phk->Ansi,
118 &phk->ModuleName);
119
120 ExFreePoolWithTag(pHP, TAG_HOOK);
121 return Result;
122 }
123
124 LRESULT
125 FASTCALL
126 IntCallDebugHook( PHOOK Hook,
127 int Code,
128 WPARAM wParam,
129 LPARAM lParam,
130 BOOL Ansi)
131 {
132 LRESULT lResult = 0;
133 ULONG Size;
134 DEBUGHOOKINFO Debug;
135 PVOID HooklParam = NULL;
136 BOOL BadChk = FALSE;
137
138 if (lParam)
139 {
140 _SEH2_TRY
141 {
142 ProbeForRead((PVOID)lParam,
143 sizeof(DEBUGHOOKINFO),
144 1);
145
146 RtlCopyMemory(&Debug,
147 (PVOID)lParam,
148 sizeof(DEBUGHOOKINFO));
149 }
150 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
151 {
152 BadChk = TRUE;
153 }
154 _SEH2_END;
155
156 if (BadChk)
157 {
158 DPRINT1("HOOK WH_DEBUG read from lParam ERROR!\n");
159 return lResult;
160 }
161 }
162 else
163 return lResult; /* Need lParam! */
164
165 switch (wParam)
166 {
167 case WH_CBT:
168 {
169 switch (Debug.code)
170 {
171 case HCBT_CLICKSKIPPED:
172 Size = sizeof(MOUSEHOOKSTRUCTEX);
173 break;
174
175 case HCBT_MOVESIZE:
176 Size = sizeof(RECT);
177 break;
178
179 case HCBT_ACTIVATE:
180 Size = sizeof(CBTACTIVATESTRUCT);
181 break;
182
183 case HCBT_CREATEWND: /* Handle Ansi? */
184 Size = sizeof(CBT_CREATEWND);
185 /* What shall we do? Size += sizeof(HOOKPROC_CBT_CREATEWND_EXTRA_ARGUMENTS); same as CREATESTRUCTEX */
186 break;
187
188 default:
189 Size = sizeof(LPARAM);
190 }
191 }
192 break;
193
194 case WH_MOUSE_LL:
195 Size = sizeof(MSLLHOOKSTRUCT);
196 break;
197
198 case WH_KEYBOARD_LL:
199 Size = sizeof(KBDLLHOOKSTRUCT);
200 break;
201
202 case WH_MSGFILTER:
203 case WH_SYSMSGFILTER:
204 case WH_GETMESSAGE:
205 Size = sizeof(MSG);
206 break;
207
208 case WH_JOURNALPLAYBACK:
209 case WH_JOURNALRECORD:
210 Size = sizeof(EVENTMSG);
211 break;
212
213 case WH_FOREGROUNDIDLE:
214 case WH_KEYBOARD:
215 case WH_SHELL:
216 default:
217 Size = sizeof(LPARAM);
218 }
219
220 if (Size > sizeof(LPARAM))
221 HooklParam = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_HOOK);
222
223 if (HooklParam)
224 {
225 _SEH2_TRY
226 {
227 ProbeForRead((PVOID)Debug.lParam,
228 Size,
229 1);
230
231 RtlCopyMemory(HooklParam,
232 (PVOID)Debug.lParam,
233 Size);
234 }
235 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
236 {
237 BadChk = TRUE;
238 }
239 _SEH2_END;
240
241 if (BadChk)
242 {
243 DPRINT1("HOOK WH_DEBUG read from Debug.lParam ERROR!\n");
244 ExFreePool(HooklParam);
245 return lResult;
246 }
247 }
248
249 if (HooklParam) Debug.lParam = (LPARAM)HooklParam;
250 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Debug);
251 if (HooklParam) ExFreePoolWithTag(HooklParam, TAG_HOOK);
252
253 return lResult;
254 }
255
256 LRESULT
257 FASTCALL
258 UserCallNextHookEx( PHOOK Hook,
259 int Code,
260 WPARAM wParam,
261 LPARAM lParam,
262 BOOL Ansi)
263 {
264 LRESULT lResult = 0;
265 BOOL BadChk = FALSE;
266
267 /* Handle this one first. */
268 if ((Hook->HookId == WH_MOUSE) ||
269 (Hook->HookId == WH_CBT && Code == HCBT_CLICKSKIPPED))
270 {
271 MOUSEHOOKSTRUCTEX Mouse;
272 if (lParam)
273 {
274 _SEH2_TRY
275 {
276 ProbeForRead((PVOID)lParam,
277 sizeof(MOUSEHOOKSTRUCTEX),
278 1);
279
280 RtlCopyMemory(&Mouse,
281 (PVOID)lParam,
282 sizeof(MOUSEHOOKSTRUCTEX));
283 }
284 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
285 {
286 BadChk = TRUE;
287 }
288 _SEH2_END;
289
290 if (BadChk)
291 {
292 DPRINT1("HOOK WH_MOUSE read from lParam ERROR!\n");
293 }
294 }
295
296 if (!BadChk)
297 {
298 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Mouse);
299 }
300
301 return lResult;
302 }
303
304 switch(Hook->HookId)
305 {
306 case WH_MOUSE_LL:
307 {
308 MSLLHOOKSTRUCT Mouse;
309
310 if (lParam)
311 {
312 _SEH2_TRY
313 {
314 ProbeForRead((PVOID)lParam,
315 sizeof(MSLLHOOKSTRUCT),
316 1);
317
318 RtlCopyMemory(&Mouse,
319 (PVOID)lParam,
320 sizeof(MSLLHOOKSTRUCT));
321 }
322 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
323 {
324 BadChk = TRUE;
325 }
326 _SEH2_END;
327
328 if (BadChk)
329 {
330 DPRINT1("HOOK WH_MOUSE_LL read from lParam ERROR!\n");
331 }
332 }
333
334 if (!BadChk)
335 {
336 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Mouse);
337 }
338 break;
339 }
340
341 case WH_KEYBOARD_LL:
342 {
343 KBDLLHOOKSTRUCT Keyboard;
344
345 if (lParam)
346 {
347 _SEH2_TRY
348 {
349 ProbeForRead((PVOID)lParam,
350 sizeof(KBDLLHOOKSTRUCT),
351 1);
352
353 RtlCopyMemory(&Keyboard,
354 (PVOID)lParam,
355 sizeof(KBDLLHOOKSTRUCT));
356 }
357 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
358 {
359 BadChk = TRUE;
360 }
361 _SEH2_END;
362
363 if (BadChk)
364 {
365 DPRINT1("HOOK WH_KEYBORD_LL read from lParam ERROR!\n");
366 }
367 }
368
369 if (!BadChk)
370 {
371 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Keyboard);
372 }
373 break;
374 }
375
376 case WH_MSGFILTER:
377 case WH_SYSMSGFILTER:
378 case WH_GETMESSAGE:
379 {
380 MSG Msg;
381
382 if (lParam)
383 {
384 _SEH2_TRY
385 {
386 ProbeForRead((PVOID)lParam,
387 sizeof(MSG),
388 1);
389
390 RtlCopyMemory(&Msg,
391 (PVOID)lParam,
392 sizeof(MSG));
393 }
394 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
395 {
396 BadChk = TRUE;
397 }
398 _SEH2_END;
399
400 if (BadChk)
401 {
402 DPRINT1("HOOK WH_XMESSAGEX read from lParam ERROR!\n");
403 }
404 }
405
406 if (!BadChk)
407 {
408 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Msg);
409
410 if (lParam && (Hook->HookId == WH_GETMESSAGE))
411 {
412 _SEH2_TRY
413 {
414 ProbeForWrite((PVOID)lParam,
415 sizeof(MSG),
416 1);
417
418 RtlCopyMemory((PVOID)lParam,
419 &Msg,
420 sizeof(MSG));
421 }
422 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
423 {
424 BadChk = TRUE;
425 }
426 _SEH2_END;
427
428 if (BadChk)
429 {
430 DPRINT1("HOOK WH_GETMESSAGE write to lParam ERROR!\n");
431 }
432 }
433 }
434 break;
435 }
436
437 case WH_CBT:
438 DPRINT("HOOK WH_CBT!\n");
439 switch (Code)
440 {
441 case HCBT_CREATEWND:
442 {
443 LPCBT_CREATEWNDW pcbtcww = (LPCBT_CREATEWNDW)lParam;
444
445 DPRINT("HOOK HCBT_CREATEWND\n");
446 _SEH2_TRY
447 {
448 if (Ansi)
449 {
450 ProbeForRead( pcbtcww,
451 sizeof(CBT_CREATEWNDA),
452 1);
453 ProbeForWrite(pcbtcww->lpcs,
454 sizeof(CREATESTRUCTA),
455 1);
456 ProbeForRead( pcbtcww->lpcs->lpszName,
457 sizeof(CHAR),
458 1);
459
460 if (!IS_ATOM(pcbtcww->lpcs->lpszClass))
461 {
462 ProbeForRead( pcbtcww->lpcs->lpszClass,
463 sizeof(CHAR),
464 1);
465 }
466 }
467 else
468 {
469 ProbeForRead( pcbtcww,
470 sizeof(CBT_CREATEWNDW),
471 1);
472 ProbeForWrite(pcbtcww->lpcs,
473 sizeof(CREATESTRUCTW),
474 1);
475 ProbeForRead( pcbtcww->lpcs->lpszName,
476 sizeof(WCHAR),
477 1);
478
479 if (!IS_ATOM(pcbtcww->lpcs->lpszClass))
480 {
481 ProbeForRead( pcbtcww->lpcs->lpszClass,
482 sizeof(WCHAR),
483 1);
484 }
485 }
486 }
487 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
488 {
489 BadChk = TRUE;
490 }
491 _SEH2_END;
492
493 if (BadChk)
494 {
495 DPRINT1("HOOK HCBT_CREATEWND write ERROR!\n");
496 }
497 /* The next call handles the structures. */
498 if (!BadChk && Hook->Proc)
499 {
500 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, lParam);
501 }
502 break;
503 }
504
505 case HCBT_MOVESIZE:
506 {
507 RECTL rt;
508
509 DPRINT("HOOK HCBT_MOVESIZE\n");
510
511 if (lParam)
512 {
513 _SEH2_TRY
514 {
515 ProbeForRead((PVOID)lParam,
516 sizeof(RECT),
517 1);
518
519 RtlCopyMemory(&rt,
520 (PVOID)lParam,
521 sizeof(RECT));
522 }
523 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
524 {
525 BadChk = TRUE;
526 }
527 _SEH2_END;
528
529 if (BadChk)
530 {
531 DPRINT1("HOOK HCBT_MOVESIZE read from lParam ERROR!\n");
532 }
533 }
534
535 if (!BadChk)
536 {
537 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&rt);
538 }
539 break;
540 }
541
542 case HCBT_ACTIVATE:
543 {
544 CBTACTIVATESTRUCT CbAs;
545
546 DPRINT("HOOK HCBT_ACTIVATE\n");
547 if (lParam)
548 {
549 _SEH2_TRY
550 {
551 ProbeForRead((PVOID)lParam,
552 sizeof(CBTACTIVATESTRUCT),
553 1);
554
555 RtlCopyMemory(&CbAs,
556 (PVOID)lParam,
557 sizeof(CBTACTIVATESTRUCT));
558 }
559 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
560 {
561 BadChk = TRUE;
562 }
563 _SEH2_END;
564
565 if (BadChk)
566 {
567 DPRINT1("HOOK HCBT_ACTIVATE read from lParam ERROR!\n");
568 }
569 }
570
571 if (!BadChk)
572 {
573 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&CbAs);
574 }
575 break;
576 }
577
578 /* The rest just use default. */
579 default:
580 DPRINT("HOOK HCBT_ %d\n",Code);
581 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, lParam);
582 break;
583 }
584 break;
585
586 case WH_JOURNALPLAYBACK:
587 case WH_JOURNALRECORD:
588 {
589 EVENTMSG EventMsg;
590
591 if (lParam)
592 {
593 _SEH2_TRY
594 {
595 ProbeForRead((PVOID)lParam,
596 sizeof(EVENTMSG),
597 1);
598
599 RtlCopyMemory(&EventMsg,
600 (PVOID)lParam,
601 sizeof(EVENTMSG));
602 }
603 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
604 {
605 BadChk = TRUE;
606 }
607 _SEH2_END;
608
609 if (BadChk)
610 {
611 DPRINT1("HOOK WH_JOURNAL read from lParam ERROR!\n");
612 }
613 }
614
615 if (!BadChk)
616 {
617 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)(lParam ? &EventMsg : NULL));
618
619 if (lParam)
620 {
621 _SEH2_TRY
622 {
623 ProbeForWrite((PVOID)lParam,
624 sizeof(EVENTMSG),
625 1);
626
627 RtlCopyMemory((PVOID)lParam,
628 &EventMsg,
629 sizeof(EVENTMSG));
630 }
631 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
632 {
633 BadChk = TRUE;
634 }
635 _SEH2_END;
636
637 if (BadChk)
638 {
639 DPRINT1("HOOK WH_JOURNAL write to lParam ERROR!\n");
640 }
641 }
642 }
643 break;
644 }
645
646 case WH_DEBUG:
647 lResult = IntCallDebugHook(Hook, Code, wParam, lParam, Ansi);
648 break;
649
650 /*
651 * Default the rest like, WH_FOREGROUNDIDLE, WH_KEYBOARD and WH_SHELL.
652 */
653 case WH_FOREGROUNDIDLE:
654 case WH_KEYBOARD:
655 case WH_SHELL:
656 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, lParam);
657 break;
658
659 default:
660 DPRINT1("Unsupported HOOK Id -> %d\n",Hook->HookId);
661 break;
662 }
663 if (Hook->HookId == WH_JOURNALPLAYBACK && lResult == 0)
664 lResult = -1;
665 return lResult;
666 }
667
668 PHOOK
669 FASTCALL
670 IntGetHookObject(HHOOK hHook)
671 {
672 PHOOK Hook;
673
674 if (!hHook)
675 {
676 SetLastWin32Error(ERROR_INVALID_HOOK_HANDLE);
677 return NULL;
678 }
679
680 Hook = (PHOOK)UserGetObject(gHandleTable, hHook, otHook);
681 if (!Hook)
682 {
683 SetLastWin32Error(ERROR_INVALID_HOOK_HANDLE);
684 return NULL;
685 }
686
687 UserReferenceObject(Hook);
688
689 return Hook;
690 }
691
692 /* get the first hook in the chain */
693 static
694 PHOOK
695 FASTCALL
696 IntGetFirstHook(PLIST_ENTRY Table)
697 {
698 PLIST_ENTRY Elem = Table->Flink;
699
700 if (IsListEmpty(Table)) return NULL;
701
702 return Elem == Table ? NULL : CONTAINING_RECORD(Elem, HOOK, Chain);
703 }
704
705 /* find the next hook in the chain */
706 PHOOK
707 FASTCALL
708 IntGetNextHook(PHOOK Hook)
709 {
710 int HookId = Hook->HookId;
711 PLIST_ENTRY Elem;
712 PTHREADINFO pti;
713
714 if (Hook->Thread)
715 {
716 pti = ((PTHREADINFO)Hook->Thread->Tcb.Win32Thread);
717
718 Elem = Hook->Chain.Flink;
719 if (Elem != &pti->aphkStart[HOOKID_TO_INDEX(HookId)])
720 return CONTAINING_RECORD(Elem, HOOK, Chain);
721 }
722 else
723 {
724 pti = PsGetCurrentThreadWin32Thread();
725
726 Elem = Hook->Chain.Flink;
727 if (Elem != &pti->pDeskInfo->aphkStart[HOOKID_TO_INDEX(HookId)])
728 return CONTAINING_RECORD(Elem, HOOK, Chain);
729 }
730 return NULL;
731 }
732
733 /* free a hook, removing it from its chain */
734 static
735 VOID
736 FASTCALL
737 IntFreeHook(PHOOK Hook)
738 {
739 RemoveEntryList(&Hook->Chain);
740 if (Hook->ModuleName.Buffer)
741 {
742 ExFreePoolWithTag(Hook->ModuleName.Buffer, TAG_HOOK);
743 Hook->ModuleName.Buffer = NULL;
744 }
745 /* Close handle */
746 UserDeleteObject(UserHMGetHandle(Hook), otHook);
747 }
748
749 /* remove a hook, freeing it from the chain */
750 static
751 BOOL
752 FASTCALL
753 IntRemoveHook(PHOOK Hook)
754 {
755 INT HookId;
756 PTHREADINFO pti;
757
758 HookId = Hook->HookId;
759
760 if (Hook->Thread) // Local
761 {
762 pti = ((PTHREADINFO)Hook->Thread->Tcb.Win32Thread);
763
764 IntFreeHook( Hook);
765
766 if ( IsListEmpty(&pti->aphkStart[HOOKID_TO_INDEX(HookId)]) )
767 {
768 pti->fsHooks &= ~HOOKID_TO_FLAG(HookId);
769 _SEH2_TRY
770 {
771 GetWin32ClientInfo()->fsHooks = pti->fsHooks;
772 }
773 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
774 {
775 }
776 _SEH2_END;
777 return TRUE;
778 }
779 }
780 else // Global
781 {
782 IntFreeHook( Hook);
783
784 pti = PsGetCurrentThreadWin32Thread();
785
786 if ( pti->pDeskInfo &&
787 IsListEmpty(&pti->pDeskInfo->aphkStart[HOOKID_TO_INDEX(HookId)]) )
788 {
789 pti->pDeskInfo->fsHooks &= ~HOOKID_TO_FLAG(HookId);
790 return TRUE;
791 }
792 }
793 return FALSE;
794 }
795
796 VOID
797 FASTCALL
798 HOOK_DestroyThreadHooks(PETHREAD Thread)
799 {
800 PTHREADINFO pti;
801 int HookId;
802 PHOOK HookObj;
803 PLIST_ENTRY pElem;
804
805 pti = Thread->Tcb.Win32Thread;
806 if (!pti || !pti->pDeskInfo) return;
807
808 // Local Thread cleanup.
809 if (pti->fsHooks)
810 {
811 for (HookId = WH_MINHOOK; HookId <= WH_MAXHOOK; HookId++)
812 {
813 PLIST_ENTRY pLLE = &pti->aphkStart[HOOKID_TO_INDEX(HookId)];
814
815 if (IsListEmpty(pLLE)) continue;
816
817 pElem = pLLE->Flink;
818 HookObj = CONTAINING_RECORD(pElem, HOOK, Chain);
819 do
820 {
821 if (!HookObj) break;
822 if (IntRemoveHook(HookObj)) break;
823 pElem = HookObj->Chain.Flink;
824 HookObj = CONTAINING_RECORD(pElem, HOOK, Chain);
825 }
826 while (pElem != pLLE);
827 }
828 pti->fsHooks = 0;
829 }
830 // Global search based on Thread and cleanup.
831 if (pti->pDeskInfo->fsHooks)
832 {
833 for (HookId = WH_MINHOOK; HookId <= WH_MAXHOOK; HookId++)
834 {
835 PLIST_ENTRY pGLE = &pti->pDeskInfo->aphkStart[HOOKID_TO_INDEX(HookId)];
836
837 if (IsListEmpty(pGLE)) continue;
838
839 pElem = pGLE->Flink;
840 HookObj = CONTAINING_RECORD(pElem, HOOK, Chain);
841 do
842 {
843 if (!HookObj) break;
844 if (HookObj->head.pti == pti)
845 {
846 if (IntRemoveHook(HookObj)) break;
847 }
848 pElem = HookObj->Chain.Flink;
849 HookObj = CONTAINING_RECORD(pElem, HOOK, Chain);
850 }
851 while (pElem != pGLE);
852 }
853 }
854 return;
855 }
856
857 /*
858 Win32k Kernel Space Hook Caller.
859 */
860 LRESULT
861 FASTCALL
862 co_HOOK_CallHooks( INT HookId,
863 INT Code,
864 WPARAM wParam,
865 LPARAM lParam)
866 {
867 PHOOK Hook, SaveHook;
868 PTHREADINFO pti;
869 PCLIENTINFO ClientInfo;
870 PLIST_ENTRY pLLE, pGLE;
871 BOOL Local = FALSE, Global = FALSE;
872 LRESULT Result = 0;
873
874 ASSERT(WH_MINHOOK <= HookId && HookId <= WH_MAXHOOK);
875
876 pti = PsGetCurrentThreadWin32Thread();
877 if (!pti || !pti->pDeskInfo)
878 goto Exit; // Must have a desktop running for hooks.
879
880 if ( pti->TIF_flags & TIF_INCLEANUP)
881 goto Exit;
882
883 if ( ISITHOOKED(HookId) )
884 {
885 DPRINT("Local Hooker %d\n", HookId);
886 Local = TRUE;
887 }
888
889 if ( pti->pDeskInfo->fsHooks & HOOKID_TO_FLAG(HookId) )
890 {
891 DPRINT("Global Hooker %d\n", HookId);
892 Global = TRUE;
893 }
894
895 if ( !Local && !Global ) goto Exit; // No work!
896
897 pLLE = &pti->aphkStart[HOOKID_TO_INDEX(HookId)];
898 pGLE = &pti->pDeskInfo->aphkStart[HOOKID_TO_INDEX(HookId)];
899 Hook = NULL;
900
901 /* SetWindowHookEx sorts out the Thread issue by placing the Hook to
902 the correct Thread if not NULL.
903 */
904 if ( Local )
905 {
906 Hook = IntGetFirstHook(pLLE);
907 if (!Hook)
908 {
909 DPRINT1("No Local Hook Found!\n");
910 goto Exit;
911 }
912 ObReferenceObject(Hook->Thread);
913
914 ClientInfo = pti->pClientInfo;
915 SaveHook = pti->sphkCurrent;
916
917 /* Load it for the next call. */
918 pti->sphkCurrent = Hook;
919 Hook->phkNext = IntGetNextHook(Hook);
920 if (ClientInfo)
921 {
922 _SEH2_TRY
923 {
924 ClientInfo->phkCurrent = Hook;
925 }
926 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
927 {
928 ClientInfo = NULL; // Don't bother next run.
929 }
930 _SEH2_END;
931 }
932 Result = co_IntCallHookProc( HookId,
933 Code,
934 wParam,
935 lParam,
936 Hook->Proc,
937 Hook->Ansi,
938 &Hook->ModuleName);
939 if (ClientInfo)
940 {
941 _SEH2_TRY
942 {
943 ClientInfo->phkCurrent = SaveHook;
944 }
945 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
946 {
947 }
948 _SEH2_END;
949 }
950 pti->sphkCurrent = SaveHook;
951 Hook->phkNext = NULL;
952 ObDereferenceObject(Hook->Thread);
953 }
954
955 if ( Global )
956 {
957 PTHREADINFO ptiHook;
958
959 Hook = IntGetFirstHook(pGLE);
960 if (!Hook)
961 {
962 DPRINT1("No Global Hook Found!\n");
963 goto Exit;
964 }
965 /* Performance goes down the drain. If more hooks are associated to this
966 * hook ID, this will have to post to each of the thread message queues
967 * or make a direct call.
968 */
969 do
970 {
971 /* Hook->Thread is null, we hax around this with Hook->head.pti. */
972 ptiHook = Hook->head.pti;
973
974 /* "Global hook monitors messages for all threads in the same desktop
975 * as the calling thread."
976 */
977 if ( ptiHook->TIF_flags & TIF_INCLEANUP ||
978 ptiHook->rpdesk != pti->rpdesk)
979 {
980 Hook = IntGetNextHook(Hook);
981 if (!Hook) break;
982 continue;
983 }
984 // Lockup the thread while this links through user world.
985 ObReferenceObject(ptiHook->pEThread);
986 if (ptiHook != pti )
987 {
988 DPRINT("\nGlobal Hook posting to another Thread! %d\n",HookId );
989 Result = IntCallLowLevelHook(Hook, Code, wParam, lParam);
990 }
991 else
992 { /* Make the direct call. */
993 Result = co_IntCallHookProc( HookId,
994 Code,
995 wParam,
996 lParam,
997 Hook->Proc,
998 Hook->Ansi,
999 &Hook->ModuleName);
1000 }
1001 ObDereferenceObject(ptiHook->pEThread);
1002 Hook = IntGetNextHook(Hook);
1003 }
1004 while ( Hook );
1005 DPRINT("Ret: Global HookId %d Result 0x%x\n", HookId,Result);
1006 }
1007 Exit:
1008 if (HookId == WH_JOURNALPLAYBACK && Result == 0)
1009 Result = -1;
1010 return Result;
1011 }
1012
1013 BOOL
1014 FASTCALL
1015 IntUnhookWindowsHook(int HookId, HOOKPROC pfnFilterProc)
1016 {
1017 PHOOK Hook;
1018 PLIST_ENTRY pLLE, pLE;
1019 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1020
1021 if (HookId < WH_MINHOOK || WH_MAXHOOK < HookId )
1022 {
1023 SetLastWin32Error(ERROR_INVALID_HOOK_FILTER);
1024 return FALSE;
1025 }
1026
1027 if (pti->fsHooks)
1028 {
1029 pLLE = &pti->aphkStart[HOOKID_TO_INDEX(HookId)];
1030
1031 if (IsListEmpty(pLLE)) return FALSE;
1032
1033 pLE = pLLE->Flink;
1034 Hook = CONTAINING_RECORD(pLE, HOOK, Chain);
1035 do
1036 {
1037 if (!Hook) break;
1038 if (Hook->Proc == pfnFilterProc)
1039 {
1040 if (Hook->head.pti == pti)
1041 {
1042 IntRemoveHook(Hook);
1043 UserDereferenceObject(Hook);
1044 return TRUE;
1045 }
1046 else
1047 {
1048 SetLastWin32Error(ERROR_ACCESS_DENIED);
1049 return FALSE;
1050 }
1051 }
1052 pLE = Hook->Chain.Flink;
1053 Hook = CONTAINING_RECORD(pLE, HOOK, Chain);
1054 }
1055 while (pLE != pLLE);
1056 }
1057 return FALSE;
1058 }
1059
1060 /*
1061 * Support for compatibility only? Global hooks are processed in kernel space.
1062 * This is very thread specific! Never seeing applications with more than one
1063 * hook per thread installed. Most of the applications are Global hookers and
1064 * associated with just one hook Id. Maybe it's for diagnostic testing or a
1065 * throw back to 3.11?
1066 */
1067 LRESULT
1068 APIENTRY
1069 NtUserCallNextHookEx( int Code,
1070 WPARAM wParam,
1071 LPARAM lParam,
1072 BOOL Ansi)
1073 {
1074 PTHREADINFO pti;
1075 PHOOK HookObj, NextObj;
1076 PCLIENTINFO ClientInfo;
1077 LRESULT lResult = 0;
1078 DECLARE_RETURN(LRESULT);
1079
1080 DPRINT("Enter NtUserCallNextHookEx\n");
1081 UserEnterExclusive();
1082
1083 pti = GetW32ThreadInfo();
1084
1085 HookObj = pti->sphkCurrent;
1086
1087 if (!HookObj) RETURN( 0);
1088
1089 NextObj = HookObj->phkNext;
1090
1091 pti->sphkCurrent = NextObj;
1092 ClientInfo = pti->pClientInfo;
1093 _SEH2_TRY
1094 {
1095 ClientInfo->phkCurrent = NextObj;
1096 }
1097 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1098 {
1099 ClientInfo = NULL;
1100 }
1101 _SEH2_END;
1102
1103 /* Now in List run down. */
1104 if (ClientInfo && NextObj)
1105 {
1106 NextObj->phkNext = IntGetNextHook(NextObj);
1107 lResult = UserCallNextHookEx( NextObj, Code, wParam, lParam, NextObj->Ansi);
1108 }
1109 RETURN( lResult);
1110
1111 CLEANUP:
1112 DPRINT("Leave NtUserCallNextHookEx, ret=%i\n",_ret_);
1113 UserLeave();
1114 END_CLEANUP;
1115 }
1116
1117 HHOOK
1118 APIENTRY
1119 NtUserSetWindowsHookAW( int idHook,
1120 HOOKPROC lpfn,
1121 BOOL Ansi)
1122 {
1123 DWORD ThreadId;
1124 UNICODE_STRING USModuleName;
1125
1126 RtlInitUnicodeString(&USModuleName, NULL);
1127 ThreadId = PtrToUint(NtCurrentTeb()->ClientId.UniqueThread);
1128
1129 return NtUserSetWindowsHookEx( NULL,
1130 &USModuleName,
1131 ThreadId,
1132 idHook,
1133 lpfn,
1134 Ansi);
1135 }
1136
1137 HHOOK
1138 APIENTRY
1139 NtUserSetWindowsHookEx( HINSTANCE Mod,
1140 PUNICODE_STRING UnsafeModuleName,
1141 DWORD ThreadId,
1142 int HookId,
1143 HOOKPROC HookProc,
1144 BOOL Ansi)
1145 {
1146 PWINSTATION_OBJECT WinStaObj;
1147 PHOOK Hook;
1148 UNICODE_STRING ModuleName;
1149 NTSTATUS Status;
1150 HHOOK Handle;
1151 PETHREAD Thread = NULL;
1152 PTHREADINFO ptiCurrent, pti = NULL;
1153 BOOL Hit = FALSE;
1154 DECLARE_RETURN(HHOOK);
1155
1156 DPRINT("Enter NtUserSetWindowsHookEx\n");
1157 UserEnterExclusive();
1158
1159 ptiCurrent = GetW32ThreadInfo();
1160
1161 if (HookId < WH_MINHOOK || WH_MAXHOOK < HookId )
1162 {
1163 SetLastWin32Error(ERROR_INVALID_HOOK_FILTER);
1164 RETURN( NULL);
1165 }
1166
1167 if (!HookProc)
1168 {
1169 SetLastWin32Error(ERROR_INVALID_FILTER_PROC);
1170 RETURN( NULL);
1171 }
1172
1173 if (ThreadId) /* thread-local hook */
1174 {
1175 if ( HookId == WH_JOURNALRECORD ||
1176 HookId == WH_JOURNALPLAYBACK ||
1177 HookId == WH_KEYBOARD_LL ||
1178 HookId == WH_MOUSE_LL ||
1179 HookId == WH_SYSMSGFILTER)
1180 {
1181 /* these can only be global */
1182 SetLastWin32Error(ERROR_GLOBAL_ONLY_HOOK);
1183 RETURN( NULL);
1184 }
1185
1186 if (!NT_SUCCESS(PsLookupThreadByThreadId((HANDLE)(DWORD_PTR) ThreadId, &Thread)))
1187 {
1188 DPRINT1("Invalid thread id 0x%x\n", ThreadId);
1189 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1190 RETURN( NULL);
1191 }
1192
1193 pti = Thread->Tcb.Win32Thread;
1194
1195 ObDereferenceObject(Thread);
1196
1197 if ( pti->rpdesk != ptiCurrent->rpdesk) // gptiCurrent->rpdesk)
1198 {
1199 SetLastWin32Error(ERROR_ACCESS_DENIED);
1200 RETURN( NULL);
1201 }
1202
1203 if (Thread->ThreadsProcess != PsGetCurrentProcess())
1204 {
1205 if ( !Mod &&
1206 (HookId == WH_GETMESSAGE ||
1207 HookId == WH_CALLWNDPROC ||
1208 HookId == WH_CBT ||
1209 HookId == WH_HARDWARE ||
1210 HookId == WH_DEBUG ||
1211 HookId == WH_SHELL ||
1212 HookId == WH_FOREGROUNDIDLE ||
1213 HookId == WH_CALLWNDPROCRET) )
1214 {
1215 SetLastWin32Error(ERROR_HOOK_NEEDS_HMOD);
1216 RETURN( NULL);
1217 }
1218
1219 if ( (pti->TIF_flags & (TIF_CSRSSTHREAD|TIF_SYSTEMTHREAD)) &&
1220 (HookId == WH_GETMESSAGE ||
1221 HookId == WH_CALLWNDPROC ||
1222 HookId == WH_CBT ||
1223 HookId == WH_HARDWARE ||
1224 HookId == WH_DEBUG ||
1225 HookId == WH_SHELL ||
1226 HookId == WH_FOREGROUNDIDLE ||
1227 HookId == WH_CALLWNDPROCRET) )
1228 {
1229 SetLastWin32Error(ERROR_HOOK_TYPE_NOT_ALLOWED);
1230 RETURN( NULL);
1231 }
1232 }
1233 }
1234 else /* system-global hook */
1235 {
1236 pti = ptiCurrent; // gptiCurrent;
1237 if ( !Mod &&
1238 (HookId == WH_GETMESSAGE ||
1239 HookId == WH_CALLWNDPROC ||
1240 HookId == WH_CBT ||
1241 HookId == WH_SYSMSGFILTER ||
1242 HookId == WH_HARDWARE ||
1243 HookId == WH_DEBUG ||
1244 HookId == WH_SHELL ||
1245 HookId == WH_FOREGROUNDIDLE ||
1246 HookId == WH_CALLWNDPROCRET) )
1247 {
1248 SetLastWin32Error(ERROR_HOOK_NEEDS_HMOD);
1249 RETURN( NULL);
1250 }
1251 }
1252
1253 Status = IntValidateWindowStationHandle( PsGetCurrentProcess()->Win32WindowStation,
1254 KernelMode,
1255 0,
1256 &WinStaObj);
1257
1258 if (!NT_SUCCESS(Status))
1259 {
1260 SetLastNtError(Status);
1261 RETURN( NULL);
1262 }
1263 ObDereferenceObject(WinStaObj);
1264
1265 Hook = UserCreateObject(gHandleTable, NULL, &Handle, otHook, sizeof(HOOK));
1266
1267 if (!Hook)
1268 {
1269 RETURN( NULL);
1270 }
1271
1272 Hook->ihmod = (INT)Mod; // Module Index from atom table, Do this for now.
1273 Hook->Thread = Thread; /* Set Thread, Null is Global. */
1274 Hook->HookId = HookId;
1275 Hook->rpdesk = pti->rpdesk;
1276 Hook->phkNext = NULL; /* Dont use as a chain! Use link lists for chaining. */
1277 Hook->Proc = HookProc;
1278 Hook->Ansi = Ansi;
1279
1280 if (ThreadId) /* thread-local hook */
1281 {
1282 InsertHeadList(&pti->aphkStart[HOOKID_TO_INDEX(HookId)], &Hook->Chain);
1283 pti->sphkCurrent = NULL;
1284 Hook->ptiHooked = pti;
1285 pti->fsHooks |= HOOKID_TO_FLAG(HookId);
1286
1287 if (pti->pClientInfo)
1288 {
1289 if ( pti->ppi == ptiCurrent->ppi) /* gptiCurrent->ppi) */
1290 {
1291 _SEH2_TRY
1292 {
1293 pti->pClientInfo->fsHooks = pti->fsHooks;
1294 pti->pClientInfo->phkCurrent = 0;
1295 }
1296 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1297 {
1298 Hit = TRUE;
1299 }
1300 _SEH2_END;
1301 if (Hit)
1302 {
1303 DPRINT1("Problem writing to Local ClientInfo!\n");
1304 }
1305 }
1306 else
1307 {
1308 KeAttachProcess(&pti->ppi->peProcess->Pcb);
1309 _SEH2_TRY
1310 {
1311 pti->pClientInfo->fsHooks = pti->fsHooks;
1312 pti->pClientInfo->phkCurrent = 0;
1313 }
1314 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1315 {
1316 Hit = TRUE;
1317 }
1318 _SEH2_END;
1319 KeDetachProcess();
1320 if (Hit)
1321 {
1322 DPRINT1("Problem writing to Remote ClientInfo!\n");
1323 }
1324 }
1325 }
1326 }
1327 else
1328 {
1329 InsertHeadList(&pti->pDeskInfo->aphkStart[HOOKID_TO_INDEX(HookId)], &Hook->Chain);
1330 Hook->ptiHooked = NULL;
1331 //gptiCurrent->pDeskInfo->fsHooks |= HOOKID_TO_FLAG(HookId);
1332 pti->pDeskInfo->fsHooks |= HOOKID_TO_FLAG(HookId);
1333 }
1334
1335 RtlInitUnicodeString(&Hook->ModuleName, NULL);
1336
1337 if (Mod)
1338 {
1339 Status = MmCopyFromCaller(&ModuleName,
1340 UnsafeModuleName,
1341 sizeof(UNICODE_STRING));
1342 if (!NT_SUCCESS(Status))
1343 {
1344 IntRemoveHook(Hook);
1345 SetLastNtError(Status);
1346 RETURN( NULL);
1347 }
1348
1349 Hook->ModuleName.Buffer = ExAllocatePoolWithTag( PagedPool,
1350 ModuleName.MaximumLength,
1351 TAG_HOOK);
1352 if (NULL == Hook->ModuleName.Buffer)
1353 {
1354 IntRemoveHook(Hook);
1355 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
1356 RETURN( NULL);
1357 }
1358
1359 Hook->ModuleName.MaximumLength = ModuleName.MaximumLength;
1360 Status = MmCopyFromCaller( Hook->ModuleName.Buffer,
1361 ModuleName.Buffer,
1362 ModuleName.MaximumLength);
1363 if (!NT_SUCCESS(Status))
1364 {
1365 ExFreePoolWithTag(Hook->ModuleName.Buffer, TAG_HOOK);
1366 Hook->ModuleName.Buffer = NULL;
1367 IntRemoveHook(Hook);
1368 SetLastNtError(Status);
1369 RETURN( NULL);
1370 }
1371
1372 Hook->ModuleName.Length = ModuleName.Length;
1373 /* make proc relative to the module base */
1374 Hook->offPfn = (ULONG_PTR)((char *)HookProc - (char *)Mod);
1375 }
1376 else
1377 Hook->offPfn = 0;
1378
1379 RETURN( Handle);
1380
1381 CLEANUP:
1382 DPRINT("Leave NtUserSetWindowsHookEx, ret=%i\n",_ret_);
1383 UserLeave();
1384 END_CLEANUP;
1385 }
1386
1387 BOOL
1388 APIENTRY
1389 NtUserUnhookWindowsHookEx(HHOOK Hook)
1390 {
1391 PHOOK HookObj;
1392 DECLARE_RETURN(BOOL);
1393
1394 DPRINT("Enter NtUserUnhookWindowsHookEx\n");
1395 UserEnterExclusive();
1396
1397 if (!(HookObj = IntGetHookObject(Hook)))
1398 {
1399 DPRINT1("Invalid handle passed to NtUserUnhookWindowsHookEx\n");
1400 /* SetLastNtError(Status); */
1401 RETURN( FALSE);
1402 }
1403
1404 ASSERT(Hook == UserHMGetHandle(HookObj));
1405
1406 IntRemoveHook(HookObj);
1407
1408 UserDereferenceObject(HookObj);
1409
1410 RETURN( TRUE);
1411
1412 CLEANUP:
1413 DPRINT("Leave NtUserUnhookWindowsHookEx, ret=%i\n",_ret_);
1414 UserLeave();
1415 END_CLEANUP;
1416 }
1417
1418 /* EOF */