2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * PURPOSE: Window hooks
24 * FILE: subsys/win32k/ntuser/hook.c
25 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
27 * 06-06-2001 CSH Created
28 * NOTE: Most of this code was adapted from Wine,
29 * Copyright (C) 2002 Alexandre Julliard
35 #include <win32k/debug1.h>
37 #define HOOKID_TO_INDEX(HookId) (HookId - WH_MINHOOK)
39 STATIC PHOOKTABLE GlobalHooks
;
41 /* create a new hook table */
42 STATIC FASTCALL PHOOKTABLE
43 IntAllocHookTable(void)
48 Table
= ExAllocatePoolWithTag(PagedPool
, sizeof(HOOKTABLE
), TAG_HOOK
);
51 ExInitializeFastMutex(&Table
->Lock
);
52 for (i
= 0; i
< NB_HOOKS
; i
++)
54 InitializeListHead(&Table
->Hooks
[i
]);
62 /* create a new hook and add it to the specified table */
64 IntAddHook(PETHREAD Thread
, int HookId
, BOOLEAN Global
, PWINSTATION_OBJECT WinStaObj
)
67 PHOOKTABLE Table
= Global
? GlobalHooks
: MsqGetHooks(Thread
->Tcb
.Win32Thread
->MessageQueue
);
72 Table
= IntAllocHookTable();
83 MsqSetHooks(Thread
->Tcb
.Win32Thread
->MessageQueue
, Table
);
87 Hook
= ObmCreateObject(WinStaObj
->HandleTable
, &Handle
,
88 otHookProc
, sizeof(HOOK
));
95 Hook
->Thread
= Thread
;
96 Hook
->HookId
= HookId
;
97 RtlInitUnicodeString(&Hook
->ModuleName
, NULL
);
99 IntLockHookTable(Table
);
100 InsertHeadList(&Table
->Hooks
[HOOKID_TO_INDEX(HookId
)], &Hook
->Chain
);
101 IntUnLockHookTable(Table
);
106 /* get the hook table that a given hook belongs to */
107 STATIC PHOOKTABLE FASTCALL
108 IntGetTable(PHOOK Hook
)
110 if (NULL
== Hook
->Thread
|| WH_KEYBOARD_LL
== Hook
->HookId
||
111 WH_MOUSE_LL
== Hook
->HookId
)
116 return MsqGetHooks(Hook
->Thread
->Tcb
.Win32Thread
->MessageQueue
);
119 /* get the first hook in the chain */
120 STATIC PHOOK FASTCALL
121 IntGetFirstHook(PHOOKTABLE Table
, int HookId
)
123 PLIST_ENTRY Elem
= Table
->Hooks
[HOOKID_TO_INDEX(HookId
)].Flink
;
124 return Elem
== &Table
->Hooks
[HOOKID_TO_INDEX(HookId
)]
125 ? NULL
: CONTAINING_RECORD(Elem
, HOOK
, Chain
);
128 /* find the first non-deleted hook in the chain */
129 STATIC PHOOK FASTCALL
130 IntGetFirstValidHook(PHOOKTABLE Table
, int HookId
)
135 IntLockHookTable(Table
);
136 Hook
= IntGetFirstHook(Table
, HookId
);
137 while (NULL
!= Hook
&& NULL
== Hook
->Proc
)
139 Elem
= Hook
->Chain
.Flink
;
140 Hook
= (Elem
== &Table
->Hooks
[HOOKID_TO_INDEX(HookId
)]
141 ? NULL
: CONTAINING_RECORD(Elem
, HOOK
, Chain
));
143 IntUnLockHookTable(Table
);
148 /* find the next hook in the chain, skipping the deleted ones */
149 STATIC PHOOK FASTCALL
150 IntGetNextHook(PHOOK Hook
)
152 PHOOKTABLE Table
= IntGetTable(Hook
);
153 int HookId
= Hook
->HookId
;
156 IntLockHookTable(Table
);
157 Elem
= Hook
->Chain
.Flink
;
158 while (Elem
!= &Table
->Hooks
[HOOKID_TO_INDEX(HookId
)])
160 Hook
= CONTAINING_RECORD(Elem
, HOOK
, Chain
);
161 if (NULL
!= Hook
->Proc
)
163 IntUnLockHookTable(Table
);
167 IntUnLockHookTable(Table
);
169 if (NULL
!= GlobalHooks
&& Table
!= GlobalHooks
) /* now search through the global table */
171 return IntGetFirstValidHook(GlobalHooks
, HookId
);
177 /* free a hook, removing it from its chain */
179 IntFreeHook(PHOOKTABLE Table
, PHOOK Hook
, PWINSTATION_OBJECT WinStaObj
)
181 RemoveEntryList(&Hook
->Chain
);
182 RtlFreeUnicodeString(&Hook
->ModuleName
);
184 /* Dereference thread if required */
185 if(Hook
->Flags
& HOOK_THREAD_REFERENCED
)
186 ObDereferenceObject(Hook
->Thread
);
189 ObmCloseHandle(WinStaObj
->HandleTable
, Hook
->Self
);
192 /* remove a hook, freeing it if the chain is not in use */
194 IntRemoveHook(PHOOK Hook
, PWINSTATION_OBJECT WinStaObj
)
196 PHOOKTABLE Table
= IntGetTable(Hook
);
198 ASSERT(NULL
!= Table
);
204 IntLockHookTable(Table
);
205 if (0 != Table
->Counts
[HOOKID_TO_INDEX(Hook
->HookId
)])
207 Hook
->Proc
= NULL
; /* chain is in use, just mark it and return */
211 IntFreeHook(Table
, Hook
, WinStaObj
);
213 IntUnLockHookTable(Table
);
216 /* release a hook chain, removing deleted hooks if the use count drops to 0 */
218 IntReleaseHookChain(PHOOKTABLE Table
, int HookId
, PWINSTATION_OBJECT WinStaObj
)
228 IntLockHookTable(Table
);
229 /* use count shouldn't already be 0 */
230 ASSERT(0 != Table
->Counts
[HOOKID_TO_INDEX(HookId
)]);
231 if (0 == Table
->Counts
[HOOKID_TO_INDEX(HookId
)])
233 IntUnLockHookTable(Table
);
236 if (0 == --Table
->Counts
[HOOKID_TO_INDEX(HookId
)])
238 Elem
= Table
->Hooks
[HOOKID_TO_INDEX(HookId
)].Flink
;
239 while (Elem
!= &Table
->Hooks
[HOOKID_TO_INDEX(HookId
)])
241 HookObj
= CONTAINING_RECORD(Elem
, HOOK
, Chain
);
243 if (NULL
== HookObj
->Proc
)
245 IntFreeHook(Table
, HookObj
, WinStaObj
);
249 IntUnLockHookTable(Table
);
253 HOOK_CallHooks(INT HookId
, INT Code
, WPARAM wParam
, LPARAM lParam
)
256 PHOOKTABLE Table
= MsqGetHooks(PsGetWin32Thread()->MessageQueue
);
258 PWINSTATION_OBJECT WinStaObj
;
261 ASSERT(WH_MINHOOK
<= HookId
&& HookId
<= WH_MAXHOOK
);
263 if (NULL
== Table
|| ! (Hook
= IntGetFirstValidHook(Table
, HookId
)))
265 /* try global table */
267 if (NULL
== Table
|| ! (Hook
= IntGetFirstValidHook(Table
, HookId
)))
269 return 0; /* no hook set */
273 if (Hook
->Thread
!= PsGetCurrentThread())
275 DPRINT1("Calling hooks in other threads not implemented yet");
279 IntLockHookTable(Table
);
280 Table
->Counts
[HOOKID_TO_INDEX(HookId
)]++;
281 IntUnLockHookTable(Table
);
282 if (Table
!= GlobalHooks
&& GlobalHooks
!= NULL
)
284 IntLockHookTable(GlobalHooks
);
285 GlobalHooks
->Counts
[HOOKID_TO_INDEX(HookId
)]++;
286 IntUnLockHookTable(GlobalHooks
);
289 Result
= IntCallHookProc(HookId
, Code
, wParam
, lParam
, Hook
->Proc
,
290 Hook
->Ansi
, &Hook
->ModuleName
);
292 Status
= IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation
,
297 if(! NT_SUCCESS(Status
))
299 DPRINT1("Invalid window station????\n");
303 IntReleaseHookChain(MsqGetHooks(PsGetWin32Thread()->MessageQueue
), HookId
, WinStaObj
);
304 IntReleaseHookChain(GlobalHooks
, HookId
, WinStaObj
);
305 ObDereferenceObject(WinStaObj
);
312 HOOK_DestroyThreadHooks(PETHREAD Thread
)
317 PWINSTATION_OBJECT WinStaObj
;
320 if (NULL
!= GlobalHooks
)
322 Status
= IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation
,
327 if(! NT_SUCCESS(Status
))
329 DPRINT1("Invalid window station????\n");
332 IntLockHookTable(GlobalHooks
);
333 for (HookId
= WH_MINHOOK
; HookId
<= WH_MAXHOOK
; HookId
++)
335 /* only low-level keyboard/mouse global hooks can be owned by a thread */
340 Elem
= GlobalHooks
->Hooks
[HOOKID_TO_INDEX(HookId
)].Flink
;
341 while (Elem
!= &GlobalHooks
->Hooks
[HOOKID_TO_INDEX(HookId
)])
343 HookObj
= CONTAINING_RECORD(Elem
, HOOK
, Chain
);
345 if (HookObj
->Thread
== Thread
)
347 IntRemoveHook(HookObj
, WinStaObj
);
353 IntUnLockHookTable(GlobalHooks
);
354 ObDereferenceObject(WinStaObj
);
360 NtUserCallNextHookEx(
366 PHOOK HookObj
, NextObj
;
367 PWINSTATION_OBJECT WinStaObj
;
370 Status
= IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation
,
375 if(! NT_SUCCESS(Status
))
377 SetLastNtError(Status
);
381 Status
= ObmReferenceObjectByHandle(WinStaObj
->HandleTable
, Hook
,
382 otHookProc
, (PVOID
*) &HookObj
);
383 ObDereferenceObject(WinStaObj
);
384 if (! NT_SUCCESS(Status
))
386 DPRINT1("Invalid handle passed to NtUserCallNextHookEx\n");
387 SetLastNtError(Status
);
390 ASSERT(Hook
== HookObj
->Self
);
392 if (NULL
!= HookObj
->Thread
&& (HookObj
->Thread
!= PsGetCurrentThread()))
394 DPRINT1("Thread mismatch\n");
395 ObmDereferenceObject(HookObj
);
396 SetLastWin32Error(ERROR_INVALID_HANDLE
);
400 NextObj
= IntGetNextHook(HookObj
);
401 ObmDereferenceObject(HookObj
);
404 DPRINT1("Calling next hook not implemented\n");
406 SetLastWin32Error(ERROR_NOT_SUPPORTED
);
415 NtUserSetWindowsHookAW(
427 NtUserSetWindowsHookEx(
429 PUNICODE_STRING UnsafeModuleName
,
435 PWINSTATION_OBJECT WinStaObj
;
436 BOOLEAN Global
, ReleaseThread
;
439 UNICODE_STRING ModuleName
;
443 if (HookId
< WH_MINHOOK
|| WH_MAXHOOK
< HookId
|| NULL
== HookProc
)
445 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
449 if (ThreadId
) /* thread-local hook */
451 if (HookId
== WH_JOURNALRECORD
||
452 HookId
== WH_JOURNALPLAYBACK
||
453 HookId
== WH_KEYBOARD_LL
||
454 HookId
== WH_MOUSE_LL
||
455 HookId
== WH_SYSMSGFILTER
)
457 /* these can only be global */
458 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
463 if (! NT_SUCCESS(PsLookupThreadByThreadId((PVOID
) ThreadId
, &Thread
)))
465 DPRINT1("Invalid thread id 0x%x\n", ThreadId
);
466 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
469 if (Thread
->ThreadsProcess
!= PsGetCurrentProcess())
471 ObDereferenceObject(Thread
);
472 DPRINT1("Can't specify thread belonging to another process\n");
473 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
476 ReleaseThread
= TRUE
;
478 else /* system-global hook */
480 ReleaseThread
= FALSE
;
481 if (HookId
== WH_KEYBOARD_LL
|| HookId
== WH_MOUSE_LL
)
484 Thread
= PsGetCurrentThread();
486 else if (NULL
== Mod
)
488 SetLastWin32Error(ERROR_INVALID_PARAMETER
);
498 /* We only (partially) support local WH_CBT hooks for now */
499 if (WH_CBT
!= HookId
|| Global
)
501 #if 0 /* Removed to get winEmbed working again */
504 DPRINT1("Not implemented: HookId %d Global %s\n", HookId
, Global
? "TRUE" : "FALSE");
507 ObDereferenceObject(Thread
);
508 SetLastWin32Error(ERROR_NOT_SUPPORTED
);
512 Status
= IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation
,
517 if(! NT_SUCCESS(Status
))
519 if(ReleaseThread
&& Thread
)
520 ObDereferenceObject(Thread
);
521 SetLastNtError(Status
);
522 return (HANDLE
) NULL
;
525 Hook
= IntAddHook(Thread
, HookId
, Global
, WinStaObj
);
529 ObDereferenceObject(Thread
);
530 ObDereferenceObject(WinStaObj
);
535 Hook
->Flags
|= HOOK_THREAD_REFERENCED
;
539 Status
= MmCopyFromCaller(&ModuleName
, UnsafeModuleName
, sizeof(UNICODE_STRING
));
540 if (! NT_SUCCESS(Status
))
542 ObmDereferenceObject(Hook
);
543 IntRemoveHook(Hook
, WinStaObj
);
545 ObDereferenceObject(Thread
);
546 ObDereferenceObject(WinStaObj
);
547 SetLastNtError(Status
);
550 Hook
->ModuleName
.Buffer
= ExAllocatePoolWithTag(PagedPool
,
551 ModuleName
.MaximumLength
,
553 if (NULL
== Hook
->ModuleName
.Buffer
)
555 ObmDereferenceObject(Hook
);
556 IntRemoveHook(Hook
, WinStaObj
);
558 ObDereferenceObject(Thread
);
559 ObDereferenceObject(WinStaObj
);
560 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY
);
563 Hook
->ModuleName
.MaximumLength
= ModuleName
.MaximumLength
;
564 Status
= MmCopyFromCaller(Hook
->ModuleName
.Buffer
,
566 ModuleName
.MaximumLength
);
567 if (! NT_SUCCESS(Status
))
569 ObmDereferenceObject(Hook
);
570 IntRemoveHook(Hook
, WinStaObj
);
572 ObDereferenceObject(Thread
);
573 ObDereferenceObject(WinStaObj
);
574 SetLastNtError(Status
);
577 Hook
->ModuleName
.Length
= ModuleName
.Length
;
580 Hook
->Proc
= HookProc
;
584 ObmDereferenceObject(Hook
);
585 ObDereferenceObject(WinStaObj
);
592 NtUserSetWinEventHook(
609 NtUserUnhookWindowsHookEx(
612 PWINSTATION_OBJECT WinStaObj
;
616 Status
= IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation
,
621 if(! NT_SUCCESS(Status
))
623 SetLastNtError(Status
);
627 Status
= ObmReferenceObjectByHandle(WinStaObj
->HandleTable
, Hook
,
628 otHookProc
, (PVOID
*) &HookObj
);
629 if (! NT_SUCCESS(Status
))
631 DPRINT1("Invalid handle passed to NtUserUnhookWindowsHookEx\n");
632 ObDereferenceObject(WinStaObj
);
633 SetLastNtError(Status
);
636 ASSERT(Hook
== HookObj
->Self
);
638 IntRemoveHook(HookObj
, WinStaObj
);
640 ObmDereferenceObject(HookObj
);
641 ObDereferenceObject(WinStaObj
);
648 NtUserUnhookWinEvent(