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