- fix logic, global mouse and keyboard hooks are supported, dinput now works again
[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 PTHREADINFO W32Thread;
76 PHOOK Hook;
77 PHOOKTABLE Table = Global ? GlobalHooks : MsqGetHooks(((PTHREADINFO)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(((PTHREADINFO)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 = ((PTHREADINFO)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(((PTHREADINFO)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 PTHREADINFO W32Thread;
212 PHOOKTABLE Table = IntGetTable(Hook);
213
214 ASSERT(NULL != Table);
215 if (NULL == Table)
216 {
217 return;
218 }
219
220 W32Thread = ((PTHREADINFO)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(PHOOK Hook, INT Code, WPARAM wParam, LPARAM lParam)
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(((PTHREADINFO)Hook->Thread->Tcb.Win32Thread)->MessageQueue,
278 (HWND) Code,
279 Hook->HookId,
280 wParam,
281 lParam,
282 5000,
283 TRUE,
284 MSQ_ISHOOK,
285 &uResult);
286
287 return NT_SUCCESS(Status) ? uResult : 0;
288 }
289
290 /*
291 Called from inside kernel space.
292 */
293 LRESULT
294 FASTCALL
295 co_HOOK_CallHooks(INT HookId, INT Code, WPARAM wParam, LPARAM lParam)
296 {
297 PHOOK Hook, SaveHook;
298 PTHREADINFO pti;
299 PCLIENTINFO ClientInfo;
300 PHOOKTABLE Table;
301 LRESULT Result;
302 PWINSTATION_OBJECT WinStaObj;
303 NTSTATUS Status;
304
305 ASSERT(WH_MINHOOK <= HookId && HookId <= WH_MAXHOOK);
306
307 pti = PsGetCurrentThreadWin32Thread();
308 if (!pti)
309 {
310 Table = NULL;
311 }
312 else
313 {
314 Table = MsqGetHooks(pti->MessageQueue);
315 }
316
317 if (NULL == Table || ! (Hook = IntGetFirstValidHook(Table, HookId)))
318 {
319 /* try global table */
320 Table = GlobalHooks;
321 if (NULL == Table || ! (Hook = IntGetFirstValidHook(Table, HookId)))
322 {
323 return 0; /* no hook set */
324 }
325 }
326
327 if ((Hook->Thread != PsGetCurrentThread()) && (Hook->Thread != NULL))
328 {
329 // Post it in message queue.
330 return IntCallLowLevelHook(Hook, Code, wParam, lParam);
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,
344 Code,
345 wParam,
346 lParam,
347 Hook->Proc,
348 Hook->Ansi,
349 &Hook->ModuleName);
350
351 ClientInfo->phkCurrent = SaveHook;
352
353 Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
354 KernelMode,
355 0,
356 &WinStaObj);
357
358 if (! NT_SUCCESS(Status))
359 {
360 DPRINT1("Invalid window station????\n");
361 }
362 else
363 {
364 IntReleaseHookChain(MsqGetHooks(pti->MessageQueue), HookId, WinStaObj);
365 IntReleaseHookChain(GlobalHooks, HookId, WinStaObj);
366 ObDereferenceObject(WinStaObj);
367 }
368
369 return Result;
370 }
371
372 VOID FASTCALL
373 HOOK_DestroyThreadHooks(PETHREAD Thread)
374 {
375 int HookId;
376 PLIST_ENTRY Elem;
377 PHOOK HookObj;
378 PWINSTATION_OBJECT WinStaObj;
379 NTSTATUS Status;
380
381 if (NULL != GlobalHooks)
382 {
383 Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
384 KernelMode,
385 0,
386 &WinStaObj);
387
388 if (! NT_SUCCESS(Status))
389 {
390 DPRINT1("Invalid window station????\n");
391 return;
392 }
393
394 for (HookId = WH_MINHOOK; HookId <= WH_MAXHOOK; HookId++)
395 {
396 /* only low-level keyboard/mouse global hooks can be owned by a thread */
397 switch(HookId)
398 {
399 case WH_KEYBOARD_LL:
400 case WH_MOUSE_LL:
401 Elem = GlobalHooks->Hooks[HOOKID_TO_INDEX(HookId)].Flink;
402 while (Elem != &GlobalHooks->Hooks[HOOKID_TO_INDEX(HookId)])
403 {
404 HookObj = CONTAINING_RECORD(Elem, HOOK, Chain);
405 Elem = Elem->Flink;
406 if (HookObj->Thread == Thread)
407 {
408 IntRemoveHook(HookObj, WinStaObj, TRUE);
409 }
410 }
411 break;
412 }
413 }
414 }
415 }
416
417 static LRESULT
418 FASTCALL
419 co_HOOK_CallHookNext(PHOOK Hook, INT Code, WPARAM wParam, LPARAM lParam)
420 {
421 if ((Hook->Thread != PsGetCurrentThread()) && (Hook->Thread != NULL))
422 {
423 DPRINT1("CALLING HOOK from another Thread. %d\n",Hook->HookId);
424 return IntCallLowLevelHook(Hook, Code, wParam, lParam);
425 }
426 DPRINT("CALLING HOOK %d\n",Hook->HookId);
427 return co_IntCallHookProc(Hook->HookId,
428 Code,
429 wParam,
430 lParam,
431 Hook->Proc,
432 Hook->Ansi,
433 &Hook->ModuleName);
434 }
435
436
437 LRESULT
438 FASTCALL
439 IntCallDebugHook(
440 PHOOK Hook,
441 int Code,
442 WPARAM wParam,
443 LPARAM lParam)
444 {
445 LRESULT lResult = 0;
446 ULONG Size;
447 DEBUGHOOKINFO Debug;
448 PVOID HooklParam = NULL;
449 BOOL BadChk = FALSE;
450
451 if (lParam)
452 {
453 _SEH2_TRY
454 {
455 ProbeForRead((PVOID)lParam,
456 sizeof(DEBUGHOOKINFO),
457 1);
458 RtlCopyMemory( &Debug,
459 (PVOID)lParam,
460 sizeof(DEBUGHOOKINFO));
461 }
462 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
463 {
464 BadChk = TRUE;
465 }
466 _SEH2_END;
467 if (BadChk)
468 {
469 DPRINT1("HOOK WH_DEBUG read from lParam ERROR!\n");
470 return lResult;
471 }
472 }
473 else
474 return lResult; // Need lParam!
475
476 switch (wParam)
477 {
478 case WH_CBT:
479 {
480 switch (Debug.code)
481 {
482 case HCBT_CLICKSKIPPED:
483 Size = sizeof(MOUSEHOOKSTRUCTEX);
484 break;
485 case HCBT_MOVESIZE:
486 Size = sizeof(RECT);
487 break;
488 case HCBT_ACTIVATE:
489 Size = sizeof(CBTACTIVATESTRUCT);
490 break;
491 case HCBT_CREATEWND: // Handle Ansi?
492 Size = sizeof(CBT_CREATEWND);
493 // What shall we do? Size += sizeof(CREATESTRUCTEX);
494 break;
495 default:
496 Size = sizeof(LPARAM);
497 }
498 }
499 break;
500
501 case WH_MOUSE_LL:
502 Size = sizeof(MSLLHOOKSTRUCT);
503 break;
504
505 case WH_KEYBOARD_LL:
506 Size = sizeof(KBDLLHOOKSTRUCT);
507 break;
508
509 case WH_MSGFILTER:
510 case WH_SYSMSGFILTER:
511 case WH_GETMESSAGE:
512 Size = sizeof(MSG);
513 break;
514
515 case WH_JOURNALPLAYBACK:
516 case WH_JOURNALRECORD:
517 Size = sizeof(EVENTMSG);
518 break;
519
520 case WH_FOREGROUNDIDLE:
521 case WH_KEYBOARD:
522 case WH_SHELL:
523 default:
524 Size = sizeof(LPARAM);
525 }
526
527 if (Size > sizeof(LPARAM))
528 HooklParam = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_HOOK);
529
530 if (HooklParam)
531 {
532 _SEH2_TRY
533 {
534 ProbeForRead((PVOID)Debug.lParam,
535 Size,
536 1);
537 RtlCopyMemory( HooklParam,
538 (PVOID)Debug.lParam,
539 Size);
540 }
541 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
542 {
543 BadChk = TRUE;
544 }
545 _SEH2_END;
546 if (BadChk)
547 {
548 DPRINT1("HOOK WH_DEBUG read from Debug.lParam ERROR!\n");
549 ExFreePool(HooklParam);
550 return lResult;
551 }
552 }
553
554 if (HooklParam) Debug.lParam = (LPARAM)HooklParam;
555 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Debug);
556 if (HooklParam) ExFreePoolWithTag(HooklParam, TAG_HOOK);
557 return lResult;
558 }
559
560 /*
561 Called from user space via CallNextHook.
562 */
563 LRESULT
564 FASTCALL
565 UserCallNextHookEx(
566 PHOOK Hook,
567 int Code,
568 WPARAM wParam,
569 LPARAM lParam,
570 BOOL Ansi)
571 {
572 LRESULT lResult = 0;
573 BOOL BadChk = FALSE;
574
575 // Handle this one first.
576 if ((Hook->HookId == WH_MOUSE) ||
577 (Hook->HookId == WH_CBT && Code == HCBT_CLICKSKIPPED))
578 {
579 MOUSEHOOKSTRUCTEX Mouse;
580 if (lParam)
581 {
582 _SEH2_TRY
583 {
584 ProbeForRead((PVOID)lParam,
585 sizeof(MOUSEHOOKSTRUCTEX),
586 1);
587 RtlCopyMemory( &Mouse,
588 (PVOID)lParam,
589 sizeof(MOUSEHOOKSTRUCTEX));
590 }
591 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
592 {
593 BadChk = TRUE;
594 }
595 _SEH2_END;
596 if (BadChk)
597 {
598 DPRINT1("HOOK WH_MOUSE read from lParam ERROR!\n");
599 }
600 }
601 if (!BadChk)
602 {
603 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Mouse);
604 }
605 return lResult;
606 }
607
608 switch(Hook->HookId)
609 {
610 case WH_MOUSE_LL:
611 {
612 MSLLHOOKSTRUCT Mouse;
613 if (lParam)
614 {
615 _SEH2_TRY
616 {
617 ProbeForRead((PVOID)lParam,
618 sizeof(MSLLHOOKSTRUCT),
619 1);
620 RtlCopyMemory( &Mouse,
621 (PVOID)lParam,
622 sizeof(MSLLHOOKSTRUCT));
623 }
624 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
625 {
626 BadChk = TRUE;
627 }
628 _SEH2_END;
629 if (BadChk)
630 {
631 DPRINT1("HOOK WH_MOUSE_LL read from lParam ERROR!\n");
632 }
633 }
634 if (!BadChk)
635 {
636 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Mouse);
637 }
638 break;
639 }
640
641 case WH_KEYBOARD_LL:
642 {
643 KBDLLHOOKSTRUCT Keyboard;
644 if (lParam)
645 {
646 _SEH2_TRY
647 {
648 ProbeForRead((PVOID)lParam,
649 sizeof(KBDLLHOOKSTRUCT),
650 1);
651 RtlCopyMemory( &Keyboard,
652 (PVOID)lParam,
653 sizeof(KBDLLHOOKSTRUCT));
654 }
655 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
656 {
657 BadChk = TRUE;
658 }
659 _SEH2_END;
660 if (BadChk)
661 {
662 DPRINT1("HOOK WH_KEYBORD_LL read from lParam ERROR!\n");
663 }
664 }
665 if (!BadChk)
666 {
667 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Keyboard);
668 }
669 break;
670 }
671
672 case WH_MSGFILTER:
673 case WH_SYSMSGFILTER:
674 case WH_GETMESSAGE:
675 {
676 MSG Msg;
677 if (lParam)
678 {
679 _SEH2_TRY
680 {
681 ProbeForRead((PVOID)lParam,
682 sizeof(MSG),
683 1);
684 RtlCopyMemory( &Msg,
685 (PVOID)lParam,
686 sizeof(MSG));
687 }
688 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
689 {
690 BadChk = TRUE;
691 }
692 _SEH2_END;
693 if (BadChk)
694 {
695 DPRINT1("HOOK WH_XMESSAGEX read from lParam ERROR!\n");
696 }
697 }
698 if (!BadChk)
699 {
700 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Msg);
701 if (lParam && (Hook->HookId == WH_GETMESSAGE))
702 {
703 _SEH2_TRY
704 {
705 ProbeForWrite((PVOID)lParam,
706 sizeof(MSG),
707 1);
708 RtlCopyMemory((PVOID)lParam,
709 &Msg,
710 sizeof(MSG));
711 }
712 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
713 {
714 BadChk = TRUE;
715 }
716 _SEH2_END;
717 if (BadChk)
718 {
719 DPRINT1("HOOK WH_GETMESSAGE write to lParam ERROR!\n");
720 }
721 }
722 }
723 break;
724 }
725
726 case WH_CBT:
727 DPRINT1("HOOK WH_CBT!\n");
728 switch (Code)
729 {
730 case HCBT_CREATEWND: // Use Ansi.
731 DPRINT1("HOOK HCBT_CREATEWND\n");
732 // lResult = co_HOOK_CallHookNext(Hook, Code, wParam, lParam);
733 break;
734
735 case HCBT_MOVESIZE:
736 {
737 RECT rt;
738 DPRINT1("HOOK HCBT_MOVESIZE\n");
739 if (lParam)
740 {
741 _SEH2_TRY
742 {
743 ProbeForRead((PVOID)lParam,
744 sizeof(RECT),
745 1);
746 RtlCopyMemory( &rt,
747 (PVOID)lParam,
748 sizeof(RECT));
749 }
750 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
751 {
752 BadChk = TRUE;
753 }
754 _SEH2_END;
755 if (BadChk)
756 {
757 DPRINT1("HOOK HCBT_MOVESIZE read from lParam ERROR!\n");
758 }
759 }
760 if (!BadChk)
761 {
762 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&rt);
763 }
764 break;
765 }
766
767 case HCBT_ACTIVATE:
768 {
769 CBTACTIVATESTRUCT CbAs;
770 DPRINT1("HOOK HCBT_ACTIVATE\n");
771 if (lParam)
772 {
773 _SEH2_TRY
774 {
775 ProbeForRead((PVOID)lParam,
776 sizeof(CBTACTIVATESTRUCT),
777 1);
778 RtlCopyMemory( &CbAs,
779 (PVOID)lParam,
780 sizeof(CBTACTIVATESTRUCT));
781 }
782 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
783 {
784 BadChk = TRUE;
785 }
786 _SEH2_END;
787 if (BadChk)
788 {
789 DPRINT1("HOOK HCBT_ACTIVATE read from lParam ERROR!\n");
790 }
791 }
792 if (!BadChk)
793 {
794 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&CbAs);
795 }
796 break;
797 }
798 /*
799 The rest just use default.
800 */
801 default:
802 DPRINT1("HOOK HCBT_ %d\n",Code);
803 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, lParam);
804 break;
805 }
806 break;
807
808 case WH_JOURNALPLAYBACK:
809 case WH_JOURNALRECORD:
810 {
811 EVENTMSG EventMsg;
812 if (lParam)
813 {
814 _SEH2_TRY
815 {
816 ProbeForRead((PVOID)lParam,
817 sizeof(EVENTMSG),
818 1);
819 RtlCopyMemory( &EventMsg,
820 (PVOID)lParam,
821 sizeof(EVENTMSG));
822 }
823 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
824 {
825 BadChk = TRUE;
826 }
827 _SEH2_END;
828 if (BadChk)
829 {
830 DPRINT1("HOOK WH_JOURNAL read from lParam ERROR!\n");
831 }
832 }
833 if (!BadChk)
834 {
835 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)(lParam ? &EventMsg : NULL));
836 if (lParam)
837 {
838 _SEH2_TRY
839 {
840 ProbeForWrite((PVOID)lParam,
841 sizeof(EVENTMSG),
842 1);
843 RtlCopyMemory((PVOID)lParam,
844 &EventMsg,
845 sizeof(EVENTMSG));
846 }
847 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
848 {
849 BadChk = TRUE;
850 }
851 _SEH2_END;
852 if (BadChk)
853 {
854 DPRINT1("HOOK WH_JOURNAL write to lParam ERROR!\n");
855 }
856 }
857 }
858 break;
859 }
860
861 case WH_DEBUG:
862 lResult = IntCallDebugHook(Hook, Code, wParam, lParam);
863 break;
864 /*
865 Default the rest like, WH_FOREGROUNDIDLE, WH_KEYBOARD and WH_SHELL.
866 */
867 case WH_FOREGROUNDIDLE:
868 case WH_KEYBOARD:
869 case WH_SHELL:
870 lResult = co_HOOK_CallHookNext(Hook, Code, wParam, lParam);
871 break;
872
873 default:
874 DPRINT1("Unsupported HOOK Id -> %d\n",Hook->HookId);
875 break;
876 }
877 return lResult;
878 }
879
880 LRESULT
881 APIENTRY
882 NtUserCallNextHookEx(
883 int Code,
884 WPARAM wParam,
885 LPARAM lParam,
886 BOOL Ansi)
887 {
888 PHOOK HookObj, NextObj;
889 PCLIENTINFO ClientInfo;
890 PWINSTATION_OBJECT WinStaObj;
891 NTSTATUS Status;
892 DECLARE_RETURN(LRESULT);
893
894 DPRINT("Enter NtUserCallNextHookEx\n");
895 UserEnterExclusive();
896
897 Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
898 KernelMode,
899 0,
900 &WinStaObj);
901 if (!NT_SUCCESS(Status))
902 {
903 SetLastNtError(Status);
904 RETURN( 0);
905 }
906
907 ObDereferenceObject(WinStaObj);
908
909 ClientInfo = GetWin32ClientInfo();
910
911 if (!ClientInfo) RETURN( 0);
912
913 HookObj = ClientInfo->phkCurrent;
914
915 if (!HookObj) RETURN( 0);
916
917 UserReferenceObject(HookObj);
918
919 Ansi = HookObj->Ansi;
920
921 if (NULL != HookObj->Thread && (HookObj->Thread != PsGetCurrentThread()))
922 {
923 DPRINT1("Thread mismatch\n");
924 UserDereferenceObject(HookObj);
925 SetLastWin32Error(ERROR_INVALID_HANDLE);
926 RETURN( 0);
927 }
928
929 NextObj = IntGetNextHook(HookObj);
930 ClientInfo->phkCurrent = NextObj; // Preset next hook from list.
931 UserCallNextHookEx( HookObj, Code, wParam, lParam, Ansi);
932 UserDereferenceObject(HookObj);
933
934 RETURN( (LRESULT)NextObj);
935
936 CLEANUP:
937 DPRINT("Leave NtUserCallNextHookEx, ret=%i\n",_ret_);
938 UserLeave();
939 END_CLEANUP;
940 }
941
942 HHOOK
943 APIENTRY
944 NtUserSetWindowsHookAW(
945 int idHook,
946 HOOKPROC lpfn,
947 BOOL Ansi)
948 {
949 UNICODE_STRING USModuleName;
950 RtlInitUnicodeString(&USModuleName, NULL);
951 return NtUserSetWindowsHookEx(NULL, &USModuleName, 0, idHook, lpfn, Ansi);
952 }
953
954 HHOOK
955 APIENTRY
956 NtUserSetWindowsHookEx(
957 HINSTANCE Mod,
958 PUNICODE_STRING UnsafeModuleName,
959 DWORD ThreadId,
960 int HookId,
961 HOOKPROC HookProc,
962 BOOL Ansi)
963 {
964 PWINSTATION_OBJECT WinStaObj;
965 PCLIENTINFO ClientInfo;
966 BOOLEAN Global;
967 PETHREAD Thread;
968 PHOOK Hook;
969 UNICODE_STRING ModuleName;
970 NTSTATUS Status;
971 HHOOK Handle;
972 BOOLEAN ThreadReferenced = FALSE;
973 DECLARE_RETURN(HHOOK);
974
975 DPRINT("Enter NtUserSetWindowsHookEx\n");
976 UserEnterExclusive();
977
978 if (HookId < WH_MINHOOK || WH_MAXHOOK < HookId )
979 {
980 SetLastWin32Error(ERROR_INVALID_PARAMETER);
981 RETURN( NULL);
982 }
983
984 if (!HookProc)
985 {
986 SetLastWin32Error(ERROR_INVALID_FILTER_PROC);
987 RETURN( NULL);
988 }
989
990 ClientInfo = GetWin32ClientInfo();
991
992 if (ThreadId) /* thread-local hook */
993 {
994 if (HookId == WH_JOURNALRECORD ||
995 HookId == WH_JOURNALPLAYBACK ||
996 HookId == WH_KEYBOARD_LL ||
997 HookId == WH_MOUSE_LL ||
998 HookId == WH_SYSMSGFILTER)
999 {
1000 /* these can only be global */
1001 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1002 RETURN( NULL);
1003 }
1004 Mod = NULL;
1005 Global = FALSE;
1006 if (! NT_SUCCESS(PsLookupThreadByThreadId((HANDLE) ThreadId, &Thread)))
1007 {
1008 DPRINT1("Invalid thread id 0x%x\n", ThreadId);
1009 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1010 RETURN( NULL);
1011 }
1012 /* Thread was referenced */
1013 ThreadReferenced = TRUE;
1014 if (Thread->ThreadsProcess != PsGetCurrentProcess())
1015 {
1016 ObDereferenceObject(Thread);
1017 DPRINT1("Can't specify thread belonging to another process\n");
1018 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1019 RETURN( NULL);
1020 }
1021 }
1022 else /* system-global hook */
1023 {
1024 if (HookId == WH_KEYBOARD_LL || HookId == WH_MOUSE_LL)
1025 {
1026 Mod = NULL;
1027 Thread = PsGetCurrentThread();
1028 Status = ObReferenceObjectByPointer(Thread,
1029 THREAD_ALL_ACCESS,
1030 PsThreadType,
1031 KernelMode);
1032
1033 if (! NT_SUCCESS(Status))
1034 {
1035 SetLastNtError(Status);
1036 RETURN( (HANDLE) NULL);
1037 }
1038
1039 /* Thread was referenced */
1040 ThreadReferenced = TRUE;
1041 }
1042 else if (NULL == Mod)
1043 {
1044 SetLastWin32Error(ERROR_HOOK_NEEDS_HMOD);
1045 RETURN( NULL);
1046 }
1047 else
1048 {
1049 Thread = NULL;
1050 }
1051 Global = TRUE;
1052 }
1053
1054 if ( ( Global && (HookId != WH_KEYBOARD_LL && HookId != WH_MOUSE_LL) ) ||
1055 WH_DEBUG == HookId ||
1056 WH_JOURNALPLAYBACK == HookId ||
1057 WH_JOURNALRECORD == HookId)
1058 {
1059 #if 0 /* Removed to get winEmbed working again */
1060 UNIMPLEMENTED
1061 #else
1062 DPRINT1("Not implemented: HookId %d Global %s\n", HookId, Global ? "TRUE" : "FALSE");
1063 #endif
1064
1065 /* Dereference thread if needed */
1066 if (ThreadReferenced) ObDereferenceObject(Thread);
1067 SetLastWin32Error(ERROR_NOT_SUPPORTED);
1068 RETURN( NULL);
1069 }
1070
1071 Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
1072 KernelMode,
1073 0,
1074 &WinStaObj);
1075
1076 if (! NT_SUCCESS(Status))
1077 {
1078 /* Dereference thread if needed */
1079 if (ThreadReferenced) ObDereferenceObject(Thread);
1080 SetLastNtError(Status);
1081 RETURN( (HANDLE) NULL);
1082 }
1083
1084 Hook = IntAddHook(Thread, HookId, Global, WinStaObj);
1085 if (NULL == Hook)
1086 {
1087 /* Dereference thread if needed */
1088 if (ThreadReferenced) ObDereferenceObject(Thread);
1089 ObDereferenceObject(WinStaObj);
1090 RETURN( NULL);
1091 }
1092
1093 /* Let IntFreeHook now that this thread needs a dereference */
1094 if (ThreadReferenced)
1095 {
1096 Hook->Flags |= HOOK_THREAD_REFERENCED;
1097 }
1098
1099 if (NULL != Mod)
1100 {
1101 Status = MmCopyFromCaller(&ModuleName, UnsafeModuleName, sizeof(UNICODE_STRING));
1102 if (! NT_SUCCESS(Status))
1103 {
1104 UserDereferenceObject(Hook);
1105 IntRemoveHook(Hook, WinStaObj, FALSE);
1106 ObDereferenceObject(WinStaObj);
1107 SetLastNtError(Status);
1108 RETURN( NULL);
1109 }
1110 Hook->ModuleName.Buffer = ExAllocatePoolWithTag(PagedPool,
1111 ModuleName.MaximumLength,
1112 TAG_HOOK);
1113 if (NULL == Hook->ModuleName.Buffer)
1114 {
1115 UserDereferenceObject(Hook);
1116 IntRemoveHook(Hook, WinStaObj, FALSE);
1117 ObDereferenceObject(WinStaObj);
1118 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
1119 RETURN( NULL);
1120 }
1121 Hook->ModuleName.MaximumLength = ModuleName.MaximumLength;
1122 Status = MmCopyFromCaller(Hook->ModuleName.Buffer,
1123 ModuleName.Buffer,
1124 ModuleName.MaximumLength);
1125 if (! NT_SUCCESS(Status))
1126 {
1127 ExFreePoolWithTag(Hook->ModuleName.Buffer, TAG_HOOK);
1128 UserDereferenceObject(Hook);
1129 IntRemoveHook(Hook, WinStaObj, FALSE);
1130 ObDereferenceObject(WinStaObj);
1131 SetLastNtError(Status);
1132 RETURN( NULL);
1133 }
1134 Hook->ModuleName.Length = ModuleName.Length;
1135 /* make proc relative to the module base */
1136 Hook->Proc = (void *)((char *)HookProc - (char *)Mod);
1137 }
1138 else
1139 Hook->Proc = HookProc;
1140
1141 Hook->Ansi = Ansi;
1142 Handle = Hook->Self;
1143
1144 // Clear the client threads next hook.
1145 ClientInfo->phkCurrent = 0;
1146
1147 UserDereferenceObject(Hook);
1148
1149 ObDereferenceObject(WinStaObj);
1150
1151 RETURN( Handle);
1152
1153 CLEANUP:
1154 DPRINT("Leave NtUserSetWindowsHookEx, ret=%i\n",_ret_);
1155 UserLeave();
1156 END_CLEANUP;
1157 }
1158
1159
1160 BOOL
1161 APIENTRY
1162 NtUserUnhookWindowsHookEx(
1163 HHOOK Hook)
1164 {
1165 PWINSTATION_OBJECT WinStaObj;
1166 PHOOK HookObj;
1167 NTSTATUS Status;
1168 DECLARE_RETURN(BOOL);
1169
1170 DPRINT("Enter NtUserUnhookWindowsHookEx\n");
1171 UserEnterExclusive();
1172
1173 Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
1174 KernelMode,
1175 0,
1176 &WinStaObj);
1177
1178 if (! NT_SUCCESS(Status))
1179 {
1180 SetLastNtError(Status);
1181 RETURN( FALSE);
1182 }
1183
1184 // Status = UserReferenceObjectByHandle(gHandleTable, Hook,
1185 // otHookProc, (PVOID *) &HookObj);
1186 if (!(HookObj = IntGetHookObject(Hook)))
1187 {
1188 DPRINT1("Invalid handle passed to NtUserUnhookWindowsHookEx\n");
1189 ObDereferenceObject(WinStaObj);
1190 // SetLastNtError(Status);
1191 RETURN( FALSE);
1192 }
1193 ASSERT(Hook == HookObj->Self);
1194
1195 IntRemoveHook(HookObj, WinStaObj, FALSE);
1196
1197 UserDereferenceObject(HookObj);
1198 ObDereferenceObject(WinStaObj);
1199
1200 RETURN( TRUE);
1201
1202 CLEANUP:
1203 DPRINT("Leave NtUserUnhookWindowsHookEx, ret=%i\n",_ret_);
1204 UserLeave();
1205 END_CLEANUP;
1206 }
1207
1208 /* EOF */