📄 hook.c
字号:
/*
* ReactOS W32 Subsystem
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* $Id: hook.c 23192 2006-07-20 14:53:47Z ion $
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* PURPOSE: Window hooks
* FILE: subsys/win32k/ntuser/hook.c
* PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
* REVISION HISTORY:
* 06-06-2001 CSH Created
* NOTE: Most of this code was adapted from Wine,
* Copyright (C) 2002 Alexandre Julliard
*/
#include <w32k.h>
#define NDEBUG
#include <debug.h>
#define HOOKID_TO_INDEX(HookId) (HookId - WH_MINHOOK)
static PHOOKTABLE GlobalHooks;
/* create a new hook table */
static PHOOKTABLE
IntAllocHookTable(void)
{
PHOOKTABLE Table;
UINT i;
Table = ExAllocatePoolWithTag(PagedPool, sizeof(HOOKTABLE), TAG_HOOK);
if (NULL != Table)
{
for (i = 0; i < NB_HOOKS; i++)
{
InitializeListHead(&Table->Hooks[i]);
Table->Counts[i] = 0;
}
}
return Table;
}
PHOOK FASTCALL IntGetHookObject(HHOOK hHook)
{
PHOOK Hook;
if (!hHook)
{
SetLastWin32Error(ERROR_INVALID_HOOK_HANDLE);
return NULL;
}
Hook = (PHOOK)UserGetObject(gHandleTable, hHook, otHook);
if (!Hook)
{
SetLastWin32Error(ERROR_INVALID_HOOK_HANDLE);
return NULL;
}
ASSERT(USER_BODY_TO_HEADER(Hook)->RefCount >= 0);
USER_BODY_TO_HEADER(Hook)->RefCount++;
return Hook;
}
/* create a new hook and add it to the specified table */
static PHOOK
IntAddHook(PETHREAD Thread, int HookId, BOOLEAN Global, PWINSTATION_OBJECT WinStaObj)
{
PHOOK Hook;
PHOOKTABLE Table = Global ? GlobalHooks : MsqGetHooks(((PW32THREAD)Thread->Tcb.Win32Thread)->MessageQueue);
HANDLE Handle;
if (NULL == Table)
{
Table = IntAllocHookTable();
if (NULL == Table)
{
return NULL;
}
if (Global)
{
GlobalHooks = Table;
}
else
{
MsqSetHooks(((PW32THREAD)Thread->Tcb.Win32Thread)->MessageQueue, Table);
}
}
Hook = ObmCreateObject(gHandleTable, &Handle, otHook, sizeof(HOOK));
if (NULL == Hook)
{
return NULL;
}
Hook->Self = Handle;
Hook->Thread = Thread;
Hook->HookId = HookId;
RtlInitUnicodeString(&Hook->ModuleName, NULL);
InsertHeadList(&Table->Hooks[HOOKID_TO_INDEX(HookId)], &Hook->Chain);
return Hook;
}
/* get the hook table that a given hook belongs to */
static PHOOKTABLE FASTCALL
IntGetTable(PHOOK Hook)
{
if (NULL == Hook->Thread || WH_KEYBOARD_LL == Hook->HookId ||
WH_MOUSE_LL == Hook->HookId)
{
return GlobalHooks;
}
return MsqGetHooks(((PW32THREAD)Hook->Thread->Tcb.Win32Thread)->MessageQueue);
}
/* get the first hook in the chain */
static PHOOK FASTCALL
IntGetFirstHook(PHOOKTABLE Table, int HookId)
{
PLIST_ENTRY Elem = Table->Hooks[HOOKID_TO_INDEX(HookId)].Flink;
return Elem == &Table->Hooks[HOOKID_TO_INDEX(HookId)]
? NULL : CONTAINING_RECORD(Elem, HOOK, Chain);
}
/* find the first non-deleted hook in the chain */
static PHOOK FASTCALL
IntGetFirstValidHook(PHOOKTABLE Table, int HookId)
{
PHOOK Hook;
PLIST_ENTRY Elem;
Hook = IntGetFirstHook(Table, HookId);
while (NULL != Hook && NULL == Hook->Proc)
{
Elem = Hook->Chain.Flink;
Hook = (Elem == &Table->Hooks[HOOKID_TO_INDEX(HookId)]
? NULL : CONTAINING_RECORD(Elem, HOOK, Chain));
}
return Hook;
}
/* find the next hook in the chain, skipping the deleted ones */
static PHOOK FASTCALL
IntGetNextHook(PHOOK Hook)
{
PHOOKTABLE Table = IntGetTable(Hook);
int HookId = Hook->HookId;
PLIST_ENTRY Elem;
Elem = Hook->Chain.Flink;
while (Elem != &Table->Hooks[HOOKID_TO_INDEX(HookId)])
{
Hook = CONTAINING_RECORD(Elem, HOOK, Chain);
if (NULL != Hook->Proc)
{
return Hook;
}
}
if (NULL != GlobalHooks && Table != GlobalHooks) /* now search through the global table */
{
return IntGetFirstValidHook(GlobalHooks, HookId);
}
return NULL;
}
/* free a hook, removing it from its chain */
static VOID FASTCALL
IntFreeHook(PHOOKTABLE Table, PHOOK Hook, PWINSTATION_OBJECT WinStaObj)
{
RemoveEntryList(&Hook->Chain);
RtlFreeUnicodeString(&Hook->ModuleName);
/* Dereference thread if required */
if (Hook->Flags & HOOK_THREAD_REFERENCED)
{
ObDereferenceObject(Hook->Thread);
}
/* Close handle */
ObmDeleteObject(Hook->Self, otHook);
}
/* remove a hook, freeing it if the chain is not in use */
static VOID
IntRemoveHook(PHOOK Hook, PWINSTATION_OBJECT WinStaObj, BOOL TableAlreadyLocked)
{
PHOOKTABLE Table = IntGetTable(Hook);
ASSERT(NULL != Table);
if (NULL == Table)
{
return;
}
if (0 != Table->Counts[HOOKID_TO_INDEX(Hook->HookId)])
{
Hook->Proc = NULL; /* chain is in use, just mark it and return */
}
else
{
IntFreeHook(Table, Hook, WinStaObj);
}
}
/* release a hook chain, removing deleted hooks if the use count drops to 0 */
static VOID FASTCALL
IntReleaseHookChain(PHOOKTABLE Table, int HookId, PWINSTATION_OBJECT WinStaObj)
{
PLIST_ENTRY Elem;
PHOOK HookObj;
if (NULL == Table)
{
return;
}
/* use count shouldn't already be 0 */
ASSERT(0 != Table->Counts[HOOKID_TO_INDEX(HookId)]);
if (0 == Table->Counts[HOOKID_TO_INDEX(HookId)])
{
return;
}
if (0 == --Table->Counts[HOOKID_TO_INDEX(HookId)])
{
Elem = Table->Hooks[HOOKID_TO_INDEX(HookId)].Flink;
while (Elem != &Table->Hooks[HOOKID_TO_INDEX(HookId)])
{
HookObj = CONTAINING_RECORD(Elem, HOOK, Chain);
Elem = Elem->Flink;
if (NULL == HookObj->Proc)
{
IntFreeHook(Table, HookObj, WinStaObj);
}
}
}
}
static LRESULT FASTCALL
IntCallLowLevelHook(INT HookId, INT Code, WPARAM wParam, LPARAM lParam, PHOOK Hook)
{
NTSTATUS Status;
ULONG_PTR uResult;
/* FIXME should get timeout from
* HKEY_CURRENT_USER\Control Panel\Desktop\LowLevelHooksTimeout */
Status = co_MsqSendMessage(((PW32THREAD)Hook->Thread->Tcb.Win32Thread)->MessageQueue, (HWND) Code, HookId,
wParam, lParam, 5000, TRUE, TRUE, &uResult);
return NT_SUCCESS(Status) ? uResult : 0;
}
LRESULT FASTCALL
co_HOOK_CallHooks(INT HookId, INT Code, WPARAM wParam, LPARAM lParam)
{
PHOOK Hook;
PW32THREAD Win32Thread;
PHOOKTABLE Table;
LRESULT Result;
PWINSTATION_OBJECT WinStaObj;
NTSTATUS Status;
ASSERT(WH_MINHOOK <= HookId && HookId <= WH_MAXHOOK);
Win32Thread = PsGetCurrentThreadWin32Thread();
if (NULL == Win32Thread)
{
Table = NULL;
}
else
{
Table = MsqGetHooks(Win32Thread->MessageQueue);
}
if (NULL == Table || ! (Hook = IntGetFirstValidHook(Table, HookId)))
{
/* try global table */
Table = GlobalHooks;
if (NULL == Table || ! (Hook = IntGetFirstValidHook(Table, HookId)))
{
return 0; /* no hook set */
}
}
if (Hook->Thread != PsGetCurrentThread()
&& (WH_KEYBOARD_LL == HookId || WH_MOUSE_LL == HookId))
{
DPRINT("Calling hook in owning thread\n");
return IntCallLowLevelHook(HookId, Code, wParam, lParam, Hook);
}
if (Hook->Thread != PsGetCurrentThread())
{
DPRINT1("Calling hooks in other threads not implemented yet");
return 0;
}
Table->Counts[HOOKID_TO_INDEX(HookId)]++;
if (Table != GlobalHooks && GlobalHooks != NULL)
{
GlobalHooks->Counts[HOOKID_TO_INDEX(HookId)]++;
}
Result = co_IntCallHookProc(HookId, Code, wParam, lParam, Hook->Proc,
Hook->Ansi, &Hook->ModuleName);
Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
KernelMode,
0,
&WinStaObj);
if (! NT_SUCCESS(Status))
{
DPRINT1("Invalid window station????\n");
}
else
{
IntReleaseHookChain(MsqGetHooks(PsGetCurrentThreadWin32Thread()->MessageQueue), HookId, WinStaObj);
IntReleaseHookChain(GlobalHooks, HookId, WinStaObj);
ObDereferenceObject(WinStaObj);
}
return Result;
}
VOID FASTCALL
HOOK_DestroyThreadHooks(PETHREAD Thread)
{
int HookId;
PLIST_ENTRY Elem;
PHOOK HookObj;
PWINSTATION_OBJECT WinStaObj;
NTSTATUS Status;
if (NULL != GlobalHooks)
{
Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
KernelMode,
0,
&WinStaObj);
if (! NT_SUCCESS(Status))
{
DPRINT1("Invalid window station????\n");
return;
}
for (HookId = WH_MINHOOK; HookId <= WH_MAXHOOK; HookId++)
{
/* only low-level keyboard/mouse global hooks can be owned by a thread */
switch(HookId)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -