[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 <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
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 UserReferenceObject(HookObj);
1054
1055 Ansi = HookObj->Ansi;
1056
1057 if (NULL != HookObj->Thread && (HookObj->Thread != PsGetCurrentThread()))
1058 {
1059 DPRINT1("Thread mismatch\n");
1060 UserDereferenceObject(HookObj);
1061 SetLastWin32Error(ERROR_INVALID_HANDLE);
1062 RETURN( 0);
1063 }
1064
1065 NextObj = IntGetNextHook(HookObj);
1066 ClientInfo->phkCurrent = NextObj; /* Preset next hook from list. */
1067 UserCallNextHookEx( HookObj, Code, wParam, lParam, Ansi);
1068 UserDereferenceObject(HookObj);
1069
1070 RETURN( (LRESULT)NextObj);
1071
1072 CLEANUP:
1073 DPRINT("Leave NtUserCallNextHookEx, ret=%i\n",_ret_);
1074 UserLeave();
1075 END_CLEANUP;
1076 }
1077
1078 HHOOK
1079 APIENTRY
1080 NtUserSetWindowsHookAW(int idHook,
1081 HOOKPROC lpfn,
1082 BOOL Ansi)
1083 {
1084 UNICODE_STRING USModuleName;
1085
1086 RtlInitUnicodeString(&USModuleName, NULL);
1087
1088 return NtUserSetWindowsHookEx(NULL, &USModuleName, 0, idHook, lpfn, Ansi);
1089 }
1090
1091 HHOOK
1092 APIENTRY
1093 NtUserSetWindowsHookEx(HINSTANCE Mod,
1094 PUNICODE_STRING UnsafeModuleName,
1095 DWORD ThreadId,
1096 int HookId,
1097 HOOKPROC HookProc,
1098 BOOL Ansi)
1099 {
1100 PWINSTATION_OBJECT WinStaObj;
1101 PCLIENTINFO ClientInfo;
1102 BOOLEAN Global;
1103 PETHREAD Thread;
1104 PHOOK Hook;
1105 UNICODE_STRING ModuleName;
1106 NTSTATUS Status;
1107 HHOOK Handle;
1108 BOOLEAN ThreadReferenced = FALSE;
1109 DECLARE_RETURN(HHOOK);
1110
1111 DPRINT("Enter NtUserSetWindowsHookEx\n");
1112 UserEnterExclusive();
1113
1114 if (HookId < WH_MINHOOK || WH_MAXHOOK < HookId )
1115 {
1116 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1117 RETURN( NULL);
1118 }
1119
1120 if (!HookProc)
1121 {
1122 SetLastWin32Error(ERROR_INVALID_FILTER_PROC);
1123 RETURN( NULL);
1124 }
1125
1126 ClientInfo = GetWin32ClientInfo();
1127
1128 if (ThreadId) /* thread-local hook */
1129 {
1130 if (HookId == WH_JOURNALRECORD ||
1131 HookId == WH_JOURNALPLAYBACK ||
1132 HookId == WH_KEYBOARD_LL ||
1133 HookId == WH_MOUSE_LL ||
1134 HookId == WH_SYSMSGFILTER)
1135 {
1136 /* these can only be global */
1137 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1138 RETURN( NULL);
1139 }
1140
1141 Mod = NULL;
1142 Global = FALSE;
1143
1144 if (!NT_SUCCESS(PsLookupThreadByThreadId((HANDLE)(DWORD_PTR) ThreadId, &Thread)))
1145 {
1146 DPRINT1("Invalid thread id 0x%x\n", ThreadId);
1147 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1148 RETURN( NULL);
1149 }
1150
1151 /* Thread was referenced */
1152 ThreadReferenced = TRUE;
1153 if (Thread->ThreadsProcess != PsGetCurrentProcess())
1154 {
1155 ObDereferenceObject(Thread);
1156 DPRINT1("Can't specify thread belonging to another process\n");
1157 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1158 RETURN( NULL);
1159 }
1160 }
1161 else /* system-global hook */
1162 {
1163 if (HookId == WH_KEYBOARD_LL || HookId == WH_MOUSE_LL)
1164 {
1165 Mod = NULL;
1166 Thread = PsGetCurrentThread();
1167
1168 Status = ObReferenceObjectByPointer(Thread,
1169 THREAD_ALL_ACCESS,
1170 PsThreadType,
1171 KernelMode);
1172
1173 if (!NT_SUCCESS(Status))
1174 {
1175 SetLastNtError(Status);
1176 RETURN( (HANDLE) NULL);
1177 }
1178
1179 /* Thread was referenced */
1180 ThreadReferenced = TRUE;
1181 }
1182 else if (NULL == Mod)
1183 {
1184 SetLastWin32Error(ERROR_HOOK_NEEDS_HMOD);
1185 RETURN( NULL);
1186 }
1187 else
1188 {
1189 Thread = NULL;
1190 }
1191 Global = TRUE;
1192 }
1193
1194 if ((Global && (HookId != WH_KEYBOARD_LL && HookId != WH_MOUSE_LL)) ||
1195 WH_DEBUG == HookId ||
1196 WH_JOURNALPLAYBACK == HookId ||
1197 WH_JOURNALRECORD == HookId)
1198 {
1199 #if 0 /* Removed to get winEmbed working again */
1200 UNIMPLEMENTED
1201 #else
1202 DPRINT1("Not implemented: HookId %d Global %s\n", HookId, Global ? "TRUE" : "FALSE");
1203 #endif
1204
1205 /* Dereference thread if needed */
1206 if (ThreadReferenced) ObDereferenceObject(Thread);
1207 SetLastWin32Error(ERROR_NOT_SUPPORTED);
1208 RETURN( NULL);
1209 }
1210
1211 Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
1212 KernelMode,
1213 0,
1214 &WinStaObj);
1215
1216 if (!NT_SUCCESS(Status))
1217 {
1218 /* Dereference thread if needed */
1219 if (ThreadReferenced) ObDereferenceObject(Thread);
1220 SetLastNtError(Status);
1221 RETURN( (HANDLE) NULL);
1222 }
1223
1224 Hook = IntAddHook(Thread, HookId, Global, WinStaObj);
1225 if (NULL == Hook)
1226 {
1227 /* Dereference thread if needed */
1228 if (ThreadReferenced) ObDereferenceObject(Thread);
1229 ObDereferenceObject(WinStaObj);
1230 RETURN( NULL);
1231 }
1232
1233 /* Let IntFreeHook now that this thread needs a dereference */
1234 if (ThreadReferenced)
1235 {
1236 Hook->Flags |= HOOK_THREAD_REFERENCED;
1237 }
1238
1239 if (NULL != Mod)
1240 {
1241 Status = MmCopyFromCaller(&ModuleName, UnsafeModuleName, sizeof(UNICODE_STRING));
1242 if (!NT_SUCCESS(Status))
1243 {
1244 UserDereferenceObject(Hook);
1245 IntRemoveHook(Hook, WinStaObj, FALSE);
1246 ObDereferenceObject(WinStaObj);
1247 SetLastNtError(Status);
1248 RETURN( NULL);
1249 }
1250
1251 Hook->ModuleName.Buffer = ExAllocatePoolWithTag(PagedPool,
1252 ModuleName.MaximumLength,
1253 TAG_HOOK);
1254 if (NULL == Hook->ModuleName.Buffer)
1255 {
1256 UserDereferenceObject(Hook);
1257 IntRemoveHook(Hook, WinStaObj, FALSE);
1258 ObDereferenceObject(WinStaObj);
1259 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
1260 RETURN( NULL);
1261 }
1262
1263 Hook->ModuleName.MaximumLength = ModuleName.MaximumLength;
1264 Status = MmCopyFromCaller(Hook->ModuleName.Buffer,
1265 ModuleName.Buffer,
1266 ModuleName.MaximumLength);
1267 if (!NT_SUCCESS(Status))
1268 {
1269 ExFreePoolWithTag(Hook->ModuleName.Buffer, TAG_HOOK);
1270 UserDereferenceObject(Hook);
1271 IntRemoveHook(Hook, WinStaObj, FALSE);
1272 ObDereferenceObject(WinStaObj);
1273 SetLastNtError(Status);
1274 RETURN( NULL);
1275 }
1276
1277 Hook->ModuleName.Length = ModuleName.Length;
1278 /* make proc relative to the module base */
1279 Hook->Proc = (void *)((char *)HookProc - (char *)Mod);
1280 }
1281 else
1282 Hook->Proc = HookProc;
1283
1284 Hook->Ansi = Ansi;
1285 Handle = UserHMGetHandle(Hook);
1286
1287 /* Clear the client threads next hook. */
1288 ClientInfo->phkCurrent = 0;
1289
1290 UserDereferenceObject(Hook);
1291
1292 ObDereferenceObject(WinStaObj);
1293
1294 RETURN( Handle);
1295
1296 CLEANUP:
1297 DPRINT("Leave NtUserSetWindowsHookEx, ret=%i\n",_ret_);
1298 UserLeave();
1299 END_CLEANUP;
1300 }
1301
1302
1303 BOOL
1304 APIENTRY
1305 NtUserUnhookWindowsHookEx(HHOOK Hook)
1306 {
1307 PWINSTATION_OBJECT WinStaObj;
1308 PHOOK HookObj;
1309 NTSTATUS Status;
1310 DECLARE_RETURN(BOOL);
1311
1312 DPRINT("Enter NtUserUnhookWindowsHookEx\n");
1313 UserEnterExclusive();
1314
1315 Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
1316 KernelMode,
1317 0,
1318 &WinStaObj);
1319
1320 if (!NT_SUCCESS(Status))
1321 {
1322 SetLastNtError(Status);
1323 RETURN( FALSE);
1324 }
1325
1326 /* Status = UserReferenceObjectByHandle(gHandleTable, Hook,
1327 otHookProc, (PVOID *) &HookObj); */
1328 if (!(HookObj = IntGetHookObject(Hook)))
1329 {
1330 DPRINT1("Invalid handle passed to NtUserUnhookWindowsHookEx\n");
1331 ObDereferenceObject(WinStaObj);
1332 /* SetLastNtError(Status); */
1333 RETURN( FALSE);
1334 }
1335
1336 ASSERT(Hook == UserHMGetHandle(HookObj));
1337
1338 IntRemoveHook(HookObj, WinStaObj, FALSE);
1339
1340 UserDereferenceObject(HookObj);
1341 ObDereferenceObject(WinStaObj);
1342
1343 RETURN( TRUE);
1344
1345 CLEANUP:
1346 DPRINT("Leave NtUserUnhookWindowsHookEx, ret=%i\n",_ret_);
1347 UserLeave();
1348 END_CLEANUP;
1349 }
1350
1351 /* EOF */