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