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