c14c2b5d03617f1eb6a02f76e9f4f14c0f9d5148
[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: subsystem/win32/win32k/ntuser/hook.c
6 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * REVISION HISTORY:
8 * 06-06-2001 CSH Created
9 * NOTE: Most of this code was adapted from Wine,
10 * Copyright (C) 2002 Alexandre Julliard
11 */
12
13 #include <w32k.h>
14
15 #define NDEBUG
16 #include <debug.h>
17
18 static PHOOKTABLE GlobalHooks;
19
20
21 /* PRIVATE FUNCTIONS *********************************************************/
22
23
24 /* create a new hook table */
25 static PHOOKTABLE
26 IntAllocHookTable(void)
27 {
28 PHOOKTABLE Table;
29 UINT i;
30
31 Table = ExAllocatePoolWithTag(PagedPool, sizeof(HOOKTABLE), TAG_HOOK);
32 if (NULL != Table)
33 {
34 for (i = 0; i < NB_HOOKS; i++)
35 {
36 InitializeListHead(&Table->Hooks[i]);
37 Table->Counts[i] = 0;
38 }
39 }
40
41 return Table;
42 }
43
44
45 PHOOK FASTCALL IntGetHookObject(HHOOK hHook)
46 {
47 PHOOK Hook;
48
49 if (!hHook)
50 {
51 SetLastWin32Error(ERROR_INVALID_HOOK_HANDLE);
52 return NULL;
53 }
54
55 Hook = (PHOOK)UserGetObject(gHandleTable, hHook, otHook);
56 if (!Hook)
57 {
58 SetLastWin32Error(ERROR_INVALID_HOOK_HANDLE);
59 return NULL;
60 }
61
62 ASSERT(USER_BODY_TO_HEADER(Hook)->RefCount >= 0);
63
64 USER_BODY_TO_HEADER(Hook)->RefCount++;
65
66 return Hook;
67 }
68
69
70
71 /* create a new hook and add it to the specified table */
72 static PHOOK
73 IntAddHook(PETHREAD Thread, int HookId, BOOLEAN Global, PWINSTATION_OBJECT WinStaObj)
74 {
75 PW32THREAD W32Thread;
76 PHOOK Hook;
77 PHOOKTABLE Table = Global ? GlobalHooks : MsqGetHooks(((PW32THREAD)Thread->Tcb.Win32Thread)->MessageQueue);
78 HANDLE Handle;
79
80 if (NULL == Table)
81 {
82 Table = IntAllocHookTable();
83 if (NULL == Table)
84 {
85 return NULL;
86 }
87 if (Global)
88 {
89 GlobalHooks = Table;
90 }
91 else
92 {
93 MsqSetHooks(((PW32THREAD)Thread->Tcb.Win32Thread)->MessageQueue, Table);
94 }
95 }
96
97 Hook = UserCreateObject(gHandleTable, &Handle, otHook, sizeof(HOOK));
98 if (NULL == Hook)
99 {
100 return NULL;
101 }
102
103 Hook->Self = Handle;
104 Hook->Thread = Thread;
105 Hook->HookId = HookId;
106
107 if (Thread)
108 {
109 W32Thread = ((PW32THREAD)Thread->Tcb.Win32Thread);
110 ASSERT(W32Thread != NULL);
111 W32Thread->Hooks |= HOOKID_TO_FLAG(HookId);
112 if (W32Thread->ThreadInfo != NULL)
113 W32Thread->ThreadInfo->Hooks = W32Thread->Hooks;
114 }
115
116 RtlInitUnicodeString(&Hook->ModuleName, NULL);
117
118 InsertHeadList(&Table->Hooks[HOOKID_TO_INDEX(HookId)], &Hook->Chain);
119
120 return Hook;
121 }
122
123 /* get the hook table that a given hook belongs to */
124 static PHOOKTABLE FASTCALL
125 IntGetTable(PHOOK Hook)
126 {
127 if (NULL == Hook->Thread || WH_KEYBOARD_LL == Hook->HookId ||
128 WH_MOUSE_LL == Hook->HookId)
129 {
130 return GlobalHooks;
131 }
132
133 return MsqGetHooks(((PW32THREAD)Hook->Thread->Tcb.Win32Thread)->MessageQueue);
134 }
135
136 /* get the first hook in the chain */
137 static PHOOK FASTCALL
138 IntGetFirstHook(PHOOKTABLE Table, int HookId)
139 {
140 PLIST_ENTRY Elem = Table->Hooks[HOOKID_TO_INDEX(HookId)].Flink;
141 return Elem == &Table->Hooks[HOOKID_TO_INDEX(HookId)]
142 ? NULL : CONTAINING_RECORD(Elem, HOOK, Chain);
143 }
144
145 /* find the first non-deleted hook in the chain */
146 static PHOOK FASTCALL
147 IntGetFirstValidHook(PHOOKTABLE Table, int HookId)
148 {
149 PHOOK Hook;
150 PLIST_ENTRY Elem;
151
152 Hook = IntGetFirstHook(Table, HookId);
153 while (NULL != Hook && NULL == Hook->Proc)
154 {
155 Elem = Hook->Chain.Flink;
156 Hook = (Elem == &Table->Hooks[HOOKID_TO_INDEX(HookId)]
157 ? NULL : CONTAINING_RECORD(Elem, HOOK, Chain));
158 }
159
160 return Hook;
161 }
162
163 /* find the next hook in the chain, skipping the deleted ones */
164 PHOOK
165 FASTCALL
166 IntGetNextHook(PHOOK Hook)
167 {
168 PHOOKTABLE Table = IntGetTable(Hook);
169 int HookId = Hook->HookId;
170 PLIST_ENTRY Elem;
171
172 Elem = Hook->Chain.Flink;
173 while (Elem != &Table->Hooks[HOOKID_TO_INDEX(HookId)])
174 {
175 Hook = CONTAINING_RECORD(Elem, HOOK, Chain);
176 if (NULL != Hook->Proc)
177 {
178 return Hook;
179 }
180 }
181
182 if (NULL != GlobalHooks && Table != GlobalHooks) /* now search through the global table */
183 {
184 return IntGetFirstValidHook(GlobalHooks, HookId);
185 }
186
187 return NULL;
188 }
189
190 /* free a hook, removing it from its chain */
191 static VOID FASTCALL
192 IntFreeHook(PHOOKTABLE Table, PHOOK Hook, PWINSTATION_OBJECT WinStaObj)
193 {
194 RemoveEntryList(&Hook->Chain);
195 RtlFreeUnicodeString(&Hook->ModuleName);
196
197 /* Dereference thread if required */
198 if (Hook->Flags & HOOK_THREAD_REFERENCED)
199 {
200 ObDereferenceObject(Hook->Thread);
201 }
202
203 /* Close handle */
204 UserDeleteObject(Hook->Self, otHook);
205 }
206
207 /* remove a hook, freeing it if the chain is not in use */
208 static VOID
209 IntRemoveHook(PHOOK Hook, PWINSTATION_OBJECT WinStaObj, BOOL TableAlreadyLocked)
210 {
211 PW32THREAD W32Thread;
212 PHOOKTABLE Table = IntGetTable(Hook);
213
214 ASSERT(NULL != Table);
215 if (NULL == Table)
216 {
217 return;
218 }
219
220 W32Thread = ((PW32THREAD)Hook->Thread->Tcb.Win32Thread);
221 ASSERT(W32Thread != NULL);
222 W32Thread->Hooks &= ~HOOKID_TO_FLAG(Hook->HookId);
223 if (W32Thread->ThreadInfo != NULL)
224 W32Thread->ThreadInfo->Hooks = W32Thread->Hooks;
225
226 if (0 != Table->Counts[HOOKID_TO_INDEX(Hook->HookId)])
227 {
228 Hook->Proc = NULL; /* chain is in use, just mark it and return */
229 }
230 else
231 {
232 IntFreeHook(Table, Hook, WinStaObj);
233 }
234 }
235
236 /* release a hook chain, removing deleted hooks if the use count drops to 0 */
237 static VOID FASTCALL
238 IntReleaseHookChain(PHOOKTABLE Table, int HookId, PWINSTATION_OBJECT WinStaObj)
239 {
240 PLIST_ENTRY Elem;
241 PHOOK HookObj;
242
243 if (NULL == Table)
244 {
245 return;
246 }
247
248 /* use count shouldn't already be 0 */
249 ASSERT(0 != Table->Counts[HOOKID_TO_INDEX(HookId)]);
250 if (0 == Table->Counts[HOOKID_TO_INDEX(HookId)])
251 {
252 return;
253 }
254 if (0 == --Table->Counts[HOOKID_TO_INDEX(HookId)])
255 {
256 Elem = Table->Hooks[HOOKID_TO_INDEX(HookId)].Flink;
257 while (Elem != &Table->Hooks[HOOKID_TO_INDEX(HookId)])
258 {
259 HookObj = CONTAINING_RECORD(Elem, HOOK, Chain);
260 Elem = Elem->Flink;
261 if (NULL == HookObj->Proc)
262 {
263 IntFreeHook(Table, HookObj, WinStaObj);
264 }
265 }
266 }
267 }
268
269 static LRESULT FASTCALL
270 IntCallLowLevelHook(INT HookId, INT Code, WPARAM wParam, LPARAM lParam, PHOOK Hook)
271 {
272 NTSTATUS Status;
273 ULONG_PTR uResult;
274
275 /* FIXME should get timeout from
276 * HKEY_CURRENT_USER\Control Panel\Desktop\LowLevelHooksTimeout */
277 Status = co_MsqSendMessage(((PW32THREAD)Hook->Thread->Tcb.Win32Thread)->MessageQueue, (HWND) Code, HookId,
278 wParam, lParam, 5000, TRUE, TRUE, &uResult);
279
280 return NT_SUCCESS(Status) ? uResult : 0;
281 }
282
283 /*
284 Called from inside kernel space.
285 */
286 LRESULT
287 FASTCALL
288 co_HOOK_CallHooks(INT HookId, INT Code, WPARAM wParam, LPARAM lParam)
289 {
290 PHOOK Hook, SaveHook;
291 PW32THREAD Win32Thread;
292 PW32CLIENTINFO ClientInfo;
293 PHOOKTABLE Table;
294 LRESULT Result;
295 PWINSTATION_OBJECT WinStaObj;
296 NTSTATUS Status;
297
298 ASSERT(WH_MINHOOK <= HookId && HookId <= WH_MAXHOOK);
299
300 Win32Thread = PsGetCurrentThreadWin32Thread();
301 if (NULL == Win32Thread)
302 {
303 Table = NULL;
304 }
305 else
306 {
307 Table = MsqGetHooks(Win32Thread->MessageQueue);
308 }
309
310 if (NULL == Table || ! (Hook = IntGetFirstValidHook(Table, HookId)))
311 {
312 /* try global table */
313 Table = GlobalHooks;
314 if (NULL == Table || ! (Hook = IntGetFirstValidHook(Table, HookId)))
315 {
316 return 0; /* no hook set */
317 }
318 }
319
320 if (Hook->Thread != PsGetCurrentThread()
321 && (WH_KEYBOARD_LL == HookId || WH_MOUSE_LL == HookId))
322 {
323 DPRINT("Calling hook in owning thread\n");
324 return IntCallLowLevelHook(HookId, Code, wParam, lParam, Hook);
325 }
326
327 if ((Hook->Thread != PsGetCurrentThread()) && (Hook->Thread != NULL))
328 {
329 DPRINT1("Calling hooks in other threads not implemented yet");
330 return 0;
331 }
332
333 Table->Counts[HOOKID_TO_INDEX(HookId)]++;
334 if (Table != GlobalHooks && GlobalHooks != NULL)
335 {
336 GlobalHooks->Counts[HOOKID_TO_INDEX(HookId)]++;
337 }
338
339 ClientInfo = GetWin32ClientInfo();
340 SaveHook = ClientInfo->phkCurrent;
341 ClientInfo->phkCurrent = Hook; // Load the call.
342
343 Result = co_IntCallHookProc(HookId, Code, wParam, lParam, Hook->Proc,
344 Hook->Ansi, &Hook->ModuleName);
345
346 ClientInfo->phkCurrent = SaveHook;
347
348 Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
349 KernelMode,
350 0,
351 &WinStaObj);
352
353 if (! NT_SUCCESS(Status))
354 {
355 DPRINT1("Invalid window station????\n");
356 }
357 else
358 {
359 IntReleaseHookChain(MsqGetHooks(PsGetCurrentThreadWin32Thread()->MessageQueue), HookId, WinStaObj);
360 IntReleaseHookChain(GlobalHooks, HookId, WinStaObj);
361 ObDereferenceObject(WinStaObj);
362 }
363
364 return Result;
365 }
366
367 VOID FASTCALL
368 HOOK_DestroyThreadHooks(PETHREAD Thread)
369 {
370 int HookId;
371 PLIST_ENTRY Elem;
372 PHOOK HookObj;
373 PWINSTATION_OBJECT WinStaObj;
374 NTSTATUS Status;
375
376 if (NULL != GlobalHooks)
377 {
378 Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
379 KernelMode,
380 0,
381 &WinStaObj);
382
383 if (! NT_SUCCESS(Status))
384 {
385 DPRINT1("Invalid window station????\n");
386 return;
387 }
388
389 for (HookId = WH_MINHOOK; HookId <= WH_MAXHOOK; HookId++)
390 {
391 /* only low-level keyboard/mouse global hooks can be owned by a thread */
392 switch(HookId)
393 {
394 case WH_KEYBOARD_LL:
395 case WH_MOUSE_LL:
396 Elem = GlobalHooks->Hooks[HOOKID_TO_INDEX(HookId)].Flink;
397 while (Elem != &GlobalHooks->Hooks[HOOKID_TO_INDEX(HookId)])
398 {
399 HookObj = CONTAINING_RECORD(Elem, HOOK, Chain);
400 Elem = Elem->Flink;
401 if (HookObj->Thread == Thread)
402 {
403 IntRemoveHook(HookObj, WinStaObj, TRUE);
404 }
405 }
406 break;
407 }
408 }
409 }
410 }
411
412 static LRESULT
413 FASTCALL
414 co_HOOK_CallHookNext(PHOOK Hook, INT Code, WPARAM wParam, LPARAM lParam)
415 {
416 DPRINT("CALLING HOOK %d\n",Hook->HookId);
417 return co_IntCallHookProc(Hook->HookId, Code, wParam, lParam, Hook->Proc,
418 Hook->Ansi, &Hook->ModuleName);
419 }
420
421
422 LRESULT
423 FASTCALL
424 IntCallDebugHook(
425 PHOOK Hook,
426 int Code,
427 WPARAM wParam,
428 LPARAM lParam)
429 {
430 LRESULT lResult = 0;
431 ULONG Size;
432 DEBUGHOOKINFO Debug;
433 PVOID HooklParam = NULL;
434 BOOL BadChk = FALSE;
435
436 if (lParam)
437 {
438 _SEH_TRY
439 {
440 ProbeForRead((PVOID)lParam,
441 sizeof(DEBUGHOOKINFO),
442 1);
443 RtlCopyMemory( &Debug,
444 (PVOID)lParam,
445 sizeof(DEBUGHOOKINFO));
446 }
447 _SEH_HANDLE
448 {
449 BadChk = TRUE;
450 }
451 _SEH_END;
452 if (BadChk)
453 {
454 DPRINT1("HOOK WH_DEBUG read from lParam ERROR!\n");
455 return lResult;
456 }
457 }
458 else
459 return lResult; // Need lParam!
460
461 switch (wParam)
462 {
463 case WH_CBT:
464 {
465 switch (Debug.code)
466 {
467 case HCBT_CLICKSKIPPED:
468 Size = sizeof(MOUSEHOOKSTRUCTEX);
469 break;
470 case HCBT_MOVESIZE:
471 Size = sizeof(RECT);
472 break;
473 case HCBT_ACTIVATE:
474 Size = sizeof(CBTACTIVATESTRUCT);
475 break;
476 case HCBT_CREATEWND: // Handle Ansi?
477 Size = sizeof(CBT_CREATEWND);
478 // What shall we do? Size += sizeof(CREATESTRUCTEX);
479 break;
480 default:
481 Size = sizeof(LPARAM);
482 }
483 }
484 break;
485
486 case WH_MOUSE_LL:
487 Size = sizeof(MSLLHOOKSTRUCT);
488 break;
489
490 case WH_KEYBOARD_LL:
491 Size = sizeof(KBDLLHOOKSTRUCT);
492 break;
493
494 case WH_MSGFILTER:
495 case WH_SYSMSGFILTER:
496 case WH_GETMESSAGE:
497 Size = sizeof(MSG);
498 break;
499
500 case WH_JOURNALPLAYBACK:
501 case WH_JOURNALRECORD:
502 Size = sizeof(EVENTMSG);
503 break;
504
505 case WH_FOREGROUNDIDLE:
506 case WH_KEYBOARD:
507 case WH_SHELL:
508 default:
509 Size = sizeof(LPARAM);
510 }
511
512 if (Size > sizeof(LPARAM))
513 HooklParam = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_HOOK);
514
515 if (HooklParam)
516 {
517 _SEH_TRY
518 {
519 ProbeForRead((PVOID)Debug.lParam,
520 Size,
521 1);
522 RtlCopyMemory( HooklParam,
523 (PVOID)Debug.lParam,
524 Size);
525 }
526 _SEH_HANDLE
527 {
528 BadChk = TRUE;
529 }
530 _SEH_END;
531 if (BadChk)
532 {
533 DPRINT1("HOOK WH_DEBUG read from Debug.lParam ERROR!\n");
534 ExFreePool(HooklParam);
535 return lResult;
536 }
537 }
538
539 if (HooklParam) Debug.lParam = (LPARAM)HooklParam;
540 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Debug);
541 if (HooklParam) ExFreePool(HooklParam);
542 return lResult;
543 }
544
545 /*
546 Called from user space via CallNextHook.
547 */
548 LRESULT
549 FASTCALL
550 UserCallNextHookEx(
551 PHOOK Hook,
552 int Code,
553 WPARAM wParam,
554 LPARAM lParam,
555 BOOL Ansi)
556 {
557 LRESULT lResult = 0;
558 BOOL BadChk = FALSE;
559
560 // Handle this one first.
561 if ((Hook->HookId == WH_MOUSE) ||
562 (Hook->HookId == WH_CBT && Code == HCBT_CLICKSKIPPED))
563 {
564 MOUSEHOOKSTRUCTEX Mouse;
565 if (lParam)
566 {
567 _SEH_TRY
568 {
569 ProbeForRead((PVOID)lParam,
570 sizeof(MOUSEHOOKSTRUCTEX),
571 1);
572 RtlCopyMemory( &Mouse,
573 (PVOID)lParam,
574 sizeof(MOUSEHOOKSTRUCTEX));
575 }
576 _SEH_HANDLE
577 {
578 BadChk = TRUE;
579 }
580 _SEH_END;
581 if (BadChk)
582 {
583 DPRINT1("HOOK WH_MOUSE read from lParam ERROR!\n");
584 }
585 }
586 if (!BadChk)
587 {
588 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Mouse);
589 }
590 return lResult;
591 }
592
593 switch(Hook->HookId)
594 {
595 case WH_MOUSE_LL:
596 {
597 MSLLHOOKSTRUCT Mouse;
598 if (lParam)
599 {
600 _SEH_TRY
601 {
602 ProbeForRead((PVOID)lParam,
603 sizeof(MSLLHOOKSTRUCT),
604 1);
605 RtlCopyMemory( &Mouse,
606 (PVOID)lParam,
607 sizeof(MSLLHOOKSTRUCT));
608 }
609 _SEH_HANDLE
610 {
611 BadChk = TRUE;
612 }
613 _SEH_END;
614 if (BadChk)
615 {
616 DPRINT1("HOOK WH_MOUSE_LL read from lParam ERROR!\n");
617 }
618 }
619 if (!BadChk)
620 {
621 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Mouse);
622 }
623 break;
624 }
625
626 case WH_KEYBOARD_LL:
627 {
628 KBDLLHOOKSTRUCT Keyboard;
629 if (lParam)
630 {
631 _SEH_TRY
632 {
633 ProbeForRead((PVOID)lParam,
634 sizeof(KBDLLHOOKSTRUCT),
635 1);
636 RtlCopyMemory( &Keyboard,
637 (PVOID)lParam,
638 sizeof(KBDLLHOOKSTRUCT));
639 }
640 _SEH_HANDLE
641 {
642 BadChk = TRUE;
643 }
644 _SEH_END;
645 if (BadChk)
646 {
647 DPRINT1("HOOK WH_KEYBORD_LL read from lParam ERROR!\n");
648 }
649 }
650 if (!BadChk)
651 {
652 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Keyboard);
653 }
654 break;
655 }
656
657 case WH_MSGFILTER:
658 case WH_SYSMSGFILTER:
659 case WH_GETMESSAGE:
660 {
661 MSG Msg;
662 if (lParam)
663 {
664 _SEH_TRY
665 {
666 ProbeForRead((PVOID)lParam,
667 sizeof(MSG),
668 1);
669 RtlCopyMemory( &Msg,
670 (PVOID)lParam,
671 sizeof(MSG));
672 }
673 _SEH_HANDLE
674 {
675 BadChk = TRUE;
676 }
677 _SEH_END;
678 if (BadChk)
679 {
680 DPRINT1("HOOK WH_XMESSAGEX read from lParam ERROR!\n");
681 }
682 }
683 if (!BadChk)
684 {
685 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Msg);
686 if (lParam && (Hook->HookId == WH_GETMESSAGE))
687 {
688 _SEH_TRY
689 {
690 ProbeForWrite((PVOID)lParam,
691 sizeof(MSG),
692 1);
693 RtlCopyMemory((PVOID)lParam,
694 &Msg,
695 sizeof(MSG));
696 }
697 _SEH_HANDLE
698 {
699 BadChk = TRUE;
700 }
701 _SEH_END;
702 if (BadChk)
703 {
704 DPRINT1("HOOK WH_GETMESSAGE write to lParam ERROR!\n");
705 }
706 }
707 }
708 break;
709 }
710
711 case WH_CBT:
712 DPRINT1("HOOK WH_CBT!\n");
713 switch (Code)
714 {
715 case HCBT_CREATEWND: // Use Ansi.
716 DPRINT1("HOOK HCBT_CREATEWND\n");
717 // lResult = co_HOOK_CallHookNext(Hook, Code, wParam, lParam);
718 break;
719
720 case HCBT_MOVESIZE:
721 {
722 RECT rt;
723 DPRINT1("HOOK HCBT_MOVESIZE\n");
724 if (lParam)
725 {
726 _SEH_TRY
727 {
728 ProbeForRead((PVOID)lParam,
729 sizeof(RECT),
730 1);
731 RtlCopyMemory( &rt,
732 (PVOID)lParam,
733 sizeof(RECT));
734 }
735 _SEH_HANDLE
736 {
737 BadChk = TRUE;
738 }
739 _SEH_END;
740 if (BadChk)
741 {
742 DPRINT1("HOOK HCBT_MOVESIZE read from lParam ERROR!\n");
743 }
744 }
745 if (!BadChk)
746 {
747 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&rt);
748 }
749 break;
750 }
751
752 case HCBT_ACTIVATE:
753 {
754 CBTACTIVATESTRUCT CbAs;
755 DPRINT1("HOOK HCBT_ACTIVATE\n");
756 if (lParam)
757 {
758 _SEH_TRY
759 {
760 ProbeForRead((PVOID)lParam,
761 sizeof(CBTACTIVATESTRUCT),
762 1);
763 RtlCopyMemory( &CbAs,
764 (PVOID)lParam,
765 sizeof(CBTACTIVATESTRUCT));
766 }
767 _SEH_HANDLE
768 {
769 BadChk = TRUE;
770 }
771 _SEH_END;
772 if (BadChk)
773 {
774 DPRINT1("HOOK HCBT_ACTIVATE read from lParam ERROR!\n");
775 }
776 }
777 if (!BadChk)
778 {
779 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&CbAs);
780 }
781 break;
782 }
783 /*
784 The rest just use default.
785 */
786 default:
787 DPRINT1("HOOK HCBT_ %d\n",Code);
788 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, lParam);
789 break;
790 }
791 break;
792
793 case WH_JOURNALPLAYBACK:
794 case WH_JOURNALRECORD:
795 {
796 EVENTMSG EventMsg;
797 if (lParam)
798 {
799 _SEH_TRY
800 {
801 ProbeForRead((PVOID)lParam,
802 sizeof(EVENTMSG),
803 1);
804 RtlCopyMemory( &EventMsg,
805 (PVOID)lParam,
806 sizeof(EVENTMSG));
807 }
808 _SEH_HANDLE
809 {
810 BadChk = TRUE;
811 }
812 _SEH_END;
813 if (BadChk)
814 {
815 DPRINT1("HOOK WH_JOURNAL read from lParam ERROR!\n");
816 }
817 }
818 if (!BadChk)
819 {
820 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)(lParam ? &EventMsg : NULL));
821 if (lParam)
822 {
823 _SEH_TRY
824 {
825 ProbeForWrite((PVOID)lParam,
826 sizeof(EVENTMSG),
827 1);
828 RtlCopyMemory((PVOID)lParam,
829 &EventMsg,
830 sizeof(EVENTMSG));
831 }
832 _SEH_HANDLE
833 {
834 BadChk = TRUE;
835 }
836 _SEH_END;
837 if (BadChk)
838 {
839 DPRINT1("HOOK WH_JOURNAL write to lParam ERROR!\n");
840 }
841 }
842 }
843 break;
844 }
845
846 case WH_DEBUG:
847 lResult = IntCallDebugHook(Hook, Code, wParam, lParam);
848 break;
849 /*
850 Default the rest like, WH_FOREGROUNDIDLE, WH_KEYBOARD and WH_SHELL.
851 */
852 case WH_FOREGROUNDIDLE:
853 case WH_KEYBOARD:
854 case WH_SHELL:
855 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, lParam);
856 break;
857
858 default:
859 DPRINT1("Unsupported HOOK Id -> %d\n",Hook->HookId);
860 break;
861 }
862 return lResult;
863 }
864
865 LRESULT
866 STDCALL
867 NtUserCallNextHookEx(
868 int Code,
869 WPARAM wParam,
870 LPARAM lParam,
871 BOOL Ansi)
872 {
873 PHOOK HookObj, NextObj;
874 PW32CLIENTINFO ClientInfo;
875 PWINSTATION_OBJECT WinStaObj;
876 NTSTATUS Status;
877 DECLARE_RETURN(LRESULT);
878
879 DPRINT("Enter NtUserCallNextHookEx\n");
880 UserEnterExclusive();
881
882 Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
883 KernelMode,
884 0,
885 &WinStaObj);
886 if (!NT_SUCCESS(Status))
887 {
888 SetLastNtError(Status);
889 RETURN( 0);
890 }
891
892 ObDereferenceObject(WinStaObj);
893
894 ClientInfo = GetWin32ClientInfo();
895
896 if (!ClientInfo) RETURN( 0);
897
898 HookObj = ClientInfo->phkCurrent;
899
900 if (!HookObj) RETURN( 0);
901
902 UserReferenceObject(HookObj);
903
904 Ansi = HookObj->Ansi;
905
906 if (NULL != HookObj->Thread && (HookObj->Thread != PsGetCurrentThread()))
907 {
908 DPRINT1("Thread mismatch\n");
909 UserDereferenceObject(HookObj);
910 SetLastWin32Error(ERROR_INVALID_HANDLE);
911 RETURN( 0);
912 }
913
914 NextObj = IntGetNextHook(HookObj);
915 ClientInfo->phkCurrent = NextObj; // Preset next hook from list.
916 UserCallNextHookEx( HookObj, Code, wParam, lParam, Ansi);
917 UserDereferenceObject(HookObj);
918
919 RETURN( (LRESULT)NextObj);
920
921 CLEANUP:
922 DPRINT("Leave NtUserCallNextHookEx, ret=%i\n",_ret_);
923 UserLeave();
924 END_CLEANUP;
925 }
926
927 HHOOK
928 STDCALL
929 NtUserSetWindowsHookAW(
930 int idHook,
931 HOOKPROC lpfn,
932 BOOL Ansi)
933 {
934 UNICODE_STRING USModuleName;
935 RtlInitUnicodeString(&USModuleName, NULL);
936 return NtUserSetWindowsHookEx(NULL, &USModuleName, 0, idHook, lpfn, Ansi);
937 }
938
939 HHOOK
940 STDCALL
941 NtUserSetWindowsHookEx(
942 HINSTANCE Mod,
943 PUNICODE_STRING UnsafeModuleName,
944 DWORD ThreadId,
945 int HookId,
946 HOOKPROC HookProc,
947 BOOL Ansi)
948 {
949 PWINSTATION_OBJECT WinStaObj;
950 PW32CLIENTINFO ClientInfo;
951 BOOLEAN Global;
952 PETHREAD Thread;
953 PHOOK Hook;
954 UNICODE_STRING ModuleName;
955 NTSTATUS Status;
956 HHOOK Handle;
957 DECLARE_RETURN(HHOOK);
958
959 DPRINT("Enter NtUserSetWindowsHookEx\n");
960 UserEnterExclusive();
961
962 if (HookId < WH_MINHOOK || WH_MAXHOOK < HookId || NULL == HookProc)
963 {
964 SetLastWin32Error(ERROR_INVALID_PARAMETER);
965 RETURN( NULL);
966 }
967
968 ClientInfo = GetWin32ClientInfo();
969
970 if (ThreadId) /* thread-local hook */
971 {
972 if (HookId == WH_JOURNALRECORD ||
973 HookId == WH_JOURNALPLAYBACK ||
974 HookId == WH_KEYBOARD_LL ||
975 HookId == WH_MOUSE_LL ||
976 HookId == WH_SYSMSGFILTER)
977 {
978 /* these can only be global */
979 SetLastWin32Error(ERROR_INVALID_PARAMETER);
980 RETURN( NULL);
981 }
982 Mod = NULL;
983 Global = FALSE;
984 if (! NT_SUCCESS(PsLookupThreadByThreadId((HANDLE) ThreadId, &Thread)))
985 {
986 DPRINT1("Invalid thread id 0x%x\n", ThreadId);
987 SetLastWin32Error(ERROR_INVALID_PARAMETER);
988 RETURN( NULL);
989 }
990 if (Thread->ThreadsProcess != PsGetCurrentProcess())
991 {
992 ObDereferenceObject(Thread);
993 DPRINT1("Can't specify thread belonging to another process\n");
994 SetLastWin32Error(ERROR_INVALID_PARAMETER);
995 RETURN( NULL);
996 }
997 }
998 else /* system-global hook */
999 {
1000 if (HookId == WH_KEYBOARD_LL || HookId == WH_MOUSE_LL)
1001 {
1002 Mod = NULL;
1003 Thread = PsGetCurrentThread();
1004 Status = ObReferenceObjectByPointer(Thread,
1005 THREAD_ALL_ACCESS,
1006 PsThreadType,
1007 KernelMode);
1008
1009 if (! NT_SUCCESS(Status))
1010 {
1011 SetLastNtError(Status);
1012 RETURN( (HANDLE) NULL);
1013 }
1014 }
1015 else if (NULL == Mod)
1016 {
1017 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1018 RETURN( NULL);
1019 }
1020 else
1021 {
1022 Thread = NULL;
1023 }
1024 Global = TRUE;
1025 }
1026
1027 /* We only (partially) support local WH_CBT hooks and
1028 * WH_KEYBOARD_LL, WH_MOUSE_LL and WH_GETMESSAGE hooks for now
1029 */
1030 if (Global ||
1031 WH_DEBUG == HookId ||
1032 WH_JOURNALPLAYBACK == HookId ||
1033 WH_JOURNALRECORD == HookId ||
1034 WH_FOREGROUNDIDLE == HookId ||
1035 WH_SHELL == HookId)
1036 {
1037 #if 0 /* Removed to get winEmbed working again */
1038 UNIMPLEMENTED
1039 #else
1040 DPRINT1("Not implemented: HookId %d Global %s\n", HookId, Global ? "TRUE" : "FALSE");
1041 #endif
1042
1043 if (NULL != Thread)
1044 {
1045 ObDereferenceObject(Thread);
1046 }
1047 SetLastWin32Error(ERROR_NOT_SUPPORTED);
1048 RETURN( NULL);
1049 }
1050
1051 Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
1052 KernelMode,
1053 0,
1054 &WinStaObj);
1055
1056 if (! NT_SUCCESS(Status))
1057 {
1058 if (NULL != Thread)
1059 {
1060 ObDereferenceObject(Thread);
1061 }
1062 SetLastNtError(Status);
1063 RETURN( (HANDLE) NULL);
1064 }
1065
1066 Hook = IntAddHook(Thread, HookId, Global, WinStaObj);
1067 if (NULL == Hook)
1068 {
1069 if (NULL != Thread)
1070 {
1071 ObDereferenceObject(Thread);
1072 }
1073 ObDereferenceObject(WinStaObj);
1074 RETURN( NULL);
1075 }
1076
1077 if (NULL != Thread)
1078 {
1079 Hook->Flags |= HOOK_THREAD_REFERENCED;
1080 }
1081
1082 if (NULL != Mod)
1083 {
1084 Status = MmCopyFromCaller(&ModuleName, UnsafeModuleName, sizeof(UNICODE_STRING));
1085 if (! NT_SUCCESS(Status))
1086 {
1087 UserDereferenceObject(Hook);
1088 IntRemoveHook(Hook, WinStaObj, FALSE);
1089 if (NULL != Thread)
1090 {
1091 ObDereferenceObject(Thread);
1092 }
1093 ObDereferenceObject(WinStaObj);
1094 SetLastNtError(Status);
1095 RETURN( NULL);
1096 }
1097 Hook->ModuleName.Buffer = ExAllocatePoolWithTag(PagedPool,
1098 ModuleName.MaximumLength,
1099 TAG_HOOK);
1100 if (NULL == Hook->ModuleName.Buffer)
1101 {
1102 UserDereferenceObject(Hook);
1103 IntRemoveHook(Hook, WinStaObj, FALSE);
1104 if (NULL != Thread)
1105 {
1106 ObDereferenceObject(Thread);
1107 }
1108 ObDereferenceObject(WinStaObj);
1109 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
1110 RETURN( NULL);
1111 }
1112 Hook->ModuleName.MaximumLength = ModuleName.MaximumLength;
1113 Status = MmCopyFromCaller(Hook->ModuleName.Buffer,
1114 ModuleName.Buffer,
1115 ModuleName.MaximumLength);
1116 if (! NT_SUCCESS(Status))
1117 {
1118 ExFreePool(Hook->ModuleName.Buffer);
1119 UserDereferenceObject(Hook);
1120 IntRemoveHook(Hook, WinStaObj, FALSE);
1121 if (NULL != Thread)
1122 {
1123 ObDereferenceObject(Thread);
1124 }
1125 ObDereferenceObject(WinStaObj);
1126 SetLastNtError(Status);
1127 RETURN( NULL);
1128 }
1129 Hook->ModuleName.Length = ModuleName.Length;
1130 }
1131
1132 Hook->Proc = HookProc;
1133 Hook->Ansi = Ansi;
1134 Handle = Hook->Self;
1135
1136 // Clear the client threads next hook.
1137 ClientInfo->phkCurrent = 0;
1138
1139 UserDereferenceObject(Hook);
1140 ObDereferenceObject(WinStaObj);
1141
1142 RETURN( Handle);
1143
1144 CLEANUP:
1145 DPRINT("Leave NtUserSetWindowsHookEx, ret=%i\n",_ret_);
1146 UserLeave();
1147 END_CLEANUP;
1148 }
1149
1150
1151 BOOL
1152 STDCALL
1153 NtUserUnhookWindowsHookEx(
1154 HHOOK Hook)
1155 {
1156 PWINSTATION_OBJECT WinStaObj;
1157 PHOOK HookObj;
1158 NTSTATUS Status;
1159 DECLARE_RETURN(BOOL);
1160
1161 DPRINT("Enter NtUserUnhookWindowsHookEx\n");
1162 UserEnterExclusive();
1163
1164 Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
1165 KernelMode,
1166 0,
1167 &WinStaObj);
1168
1169 if (! NT_SUCCESS(Status))
1170 {
1171 SetLastNtError(Status);
1172 RETURN( FALSE);
1173 }
1174
1175 // Status = UserReferenceObjectByHandle(gHandleTable, Hook,
1176 // otHookProc, (PVOID *) &HookObj);
1177 if (!(HookObj = IntGetHookObject(Hook)))
1178 {
1179 DPRINT1("Invalid handle passed to NtUserUnhookWindowsHookEx\n");
1180 ObDereferenceObject(WinStaObj);
1181 // SetLastNtError(Status);
1182 RETURN( FALSE);
1183 }
1184 ASSERT(Hook == HookObj->Self);
1185
1186 IntRemoveHook(HookObj, WinStaObj, FALSE);
1187
1188 UserDereferenceObject(HookObj);
1189 ObDereferenceObject(WinStaObj);
1190
1191 RETURN( TRUE);
1192
1193 CLEANUP:
1194 DPRINT("Leave NtUserUnhookWindowsHookEx, ret=%i\n",_ret_);
1195 UserLeave();
1196 END_CLEANUP;
1197 }
1198
1199 /* EOF */