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