/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * PURPOSE: Window hooks * FILE: subsystem/win32/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 #define NDEBUG #include static PHOOKTABLE GlobalHooks; /* PRIVATE FUNCTIONS *********************************************************/ /* 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(Hook->head.cLockObj >= 0); Hook->head.cLockObj++; 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) { PTHREADINFO W32Thread; PHOOK Hook; PHOOKTABLE Table = Global ? GlobalHooks : MsqGetHooks(((PTHREADINFO)Thread->Tcb.Win32Thread)->MessageQueue); HANDLE Handle; if (NULL == Table) { Table = IntAllocHookTable(); if (NULL == Table) { return NULL; } if (Global) { GlobalHooks = Table; } else { MsqSetHooks(((PTHREADINFO)Thread->Tcb.Win32Thread)->MessageQueue, Table); } } Hook = UserCreateObject(gHandleTable, NULL, &Handle, otHook, sizeof(HOOK)); if (NULL == Hook) { return NULL; } Hook->Thread = Thread; Hook->HookId = HookId; if (Thread) { W32Thread = ((PTHREADINFO)Thread->Tcb.Win32Thread); ASSERT(W32Thread != NULL); W32Thread->fsHooks |= HOOKID_TO_FLAG(HookId); if (W32Thread->pClientInfo) W32Thread->pClientInfo->fsHooks = W32Thread->fsHooks; if (W32Thread->pDeskInfo) // Do this for now. W32Thread->pDeskInfo->fsHooks= W32Thread->fsHooks; Hook->head.pti = W32Thread; Hook->head.rpdesk = W32Thread->rpdesk; } 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(((PTHREADINFO)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 */ 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 */ UserDeleteObject(UserHMGetHandle(Hook), otHook); } /* remove a hook, freeing it if the chain is not in use */ static VOID IntRemoveHook(PHOOK Hook, PWINSTATION_OBJECT WinStaObj, BOOL TableAlreadyLocked) { PTHREADINFO W32Thread; PHOOKTABLE Table = IntGetTable(Hook); ASSERT(NULL != Table); // At this point this should not be null! W32Thread = ((PTHREADINFO)Hook->Thread->Tcb.Win32Thread); ASSERT(W32Thread != NULL); W32Thread->fsHooks &= ~HOOKID_TO_FLAG(Hook->HookId); GetWin32ClientInfo()->fsHooks = W32Thread->fsHooks; if (W32Thread->pDeskInfo) // Do this for now. W32Thread->pDeskInfo->fsHooks= W32Thread->fsHooks; 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(PHOOK Hook, INT Code, WPARAM wParam, LPARAM lParam) { NTSTATUS Status; ULONG_PTR uResult; /* FIXME should get timeout from * HKEY_CURRENT_USER\Control Panel\Desktop\LowLevelHooksTimeout */ Status = co_MsqSendMessage(((PTHREADINFO)Hook->Thread->Tcb.Win32Thread)->MessageQueue, IntToPtr(Code), Hook->HookId, wParam, lParam, 5000, TRUE, MSQ_ISHOOK, &uResult); return NT_SUCCESS(Status) ? uResult : 0; } /* Called from inside kernel space. */ LRESULT FASTCALL co_HOOK_CallHooks(INT HookId, INT Code, WPARAM wParam, LPARAM lParam) { PHOOK Hook, SaveHook; PTHREADINFO pti; PCLIENTINFO ClientInfo; PHOOKTABLE Table; LRESULT Result; PWINSTATION_OBJECT WinStaObj; NTSTATUS Status; ASSERT(WH_MINHOOK <= HookId && HookId <= WH_MAXHOOK); pti = PsGetCurrentThreadWin32Thread(); if (!pti) { Table = NULL; } else { Table = MsqGetHooks(pti->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()) && (Hook->Thread != NULL)) { DPRINT1("\nHook found by Id and posted to Thread! %d\n",HookId ); /* Post it in message queue. */ return IntCallLowLevelHook(Hook, Code, wParam, lParam); } Table->Counts[HOOKID_TO_INDEX(HookId)]++; if (Table != GlobalHooks && GlobalHooks != NULL) { GlobalHooks->Counts[HOOKID_TO_INDEX(HookId)]++; } ClientInfo = GetWin32ClientInfo(); SaveHook = ClientInfo->phkCurrent; ClientInfo->phkCurrent = Hook; /* Load the call. */ Result = co_IntCallHookProc(HookId, Code, wParam, lParam, Hook->Proc, Hook->Ansi, &Hook->ModuleName); ClientInfo->phkCurrent = SaveHook; Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation, KernelMode, 0, &WinStaObj); if (!NT_SUCCESS(Status)) { DPRINT1("Invalid window station????\n"); } else { IntReleaseHookChain(MsqGetHooks(pti->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) { case WH_KEYBOARD_LL: case WH_MOUSE_LL: Elem = GlobalHooks->Hooks[HOOKID_TO_INDEX(HookId)].Flink; while (Elem != &GlobalHooks->Hooks[HOOKID_TO_INDEX(HookId)]) { HookObj = CONTAINING_RECORD(Elem, HOOK, Chain); Elem = Elem->Flink; if (HookObj->Thread == Thread) { IntRemoveHook(HookObj, WinStaObj, TRUE); } } break; } } } } static LRESULT FASTCALL co_HOOK_CallHookNext(PHOOK Hook, INT Code, WPARAM wParam, LPARAM lParam) { if ((Hook->Thread != PsGetCurrentThread()) && (Hook->Thread != NULL)) { DPRINT1("CALLING HOOK from another Thread. %d\n", Hook->HookId); return IntCallLowLevelHook(Hook, Code, wParam, lParam); } DPRINT("CALLING HOOK %d\n", Hook->HookId); return co_IntCallHookProc(Hook->HookId, Code, wParam, lParam, Hook->Proc, Hook->Ansi, &Hook->ModuleName); } LRESULT FASTCALL IntCallDebugHook(PHOOK Hook, int Code, WPARAM wParam, LPARAM lParam) { LRESULT lResult = 0; ULONG Size; DEBUGHOOKINFO Debug; PVOID HooklParam = NULL; BOOL BadChk = FALSE; if (lParam) { _SEH2_TRY { ProbeForRead((PVOID)lParam, sizeof(DEBUGHOOKINFO), 1); RtlCopyMemory(&Debug, (PVOID)lParam, sizeof(DEBUGHOOKINFO)); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { BadChk = TRUE; } _SEH2_END; if (BadChk) { DPRINT1("HOOK WH_DEBUG read from lParam ERROR!\n"); return lResult; } } else return lResult; /* Need lParam! */ switch (wParam) { case WH_CBT: { switch (Debug.code) { case HCBT_CLICKSKIPPED: Size = sizeof(MOUSEHOOKSTRUCTEX); break; case HCBT_MOVESIZE: Size = sizeof(RECT); break; case HCBT_ACTIVATE: Size = sizeof(CBTACTIVATESTRUCT); break; case HCBT_CREATEWND: /* Handle Ansi? */ Size = sizeof(CBT_CREATEWND); /* What shall we do? Size += sizeof(HOOKPROC_CBT_CREATEWND_EXTRA_ARGUMENTS); same as CREATESTRUCTEX */ break; default: Size = sizeof(LPARAM); } } break; case WH_MOUSE_LL: Size = sizeof(MSLLHOOKSTRUCT); break; case WH_KEYBOARD_LL: Size = sizeof(KBDLLHOOKSTRUCT); break; case WH_MSGFILTER: case WH_SYSMSGFILTER: case WH_GETMESSAGE: Size = sizeof(MSG); break; case WH_JOURNALPLAYBACK: case WH_JOURNALRECORD: Size = sizeof(EVENTMSG); break; case WH_FOREGROUNDIDLE: case WH_KEYBOARD: case WH_SHELL: default: Size = sizeof(LPARAM); } if (Size > sizeof(LPARAM)) HooklParam = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_HOOK); if (HooklParam) { _SEH2_TRY { ProbeForRead((PVOID)Debug.lParam, Size, 1); RtlCopyMemory(HooklParam, (PVOID)Debug.lParam, Size); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { BadChk = TRUE; } _SEH2_END; if (BadChk) { DPRINT1("HOOK WH_DEBUG read from Debug.lParam ERROR!\n"); ExFreePool(HooklParam); return lResult; } } if (HooklParam) Debug.lParam = (LPARAM)HooklParam; lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Debug); if (HooklParam) ExFreePoolWithTag(HooklParam, TAG_HOOK); return lResult; } /* Called from user space via CallNextHook. */ LRESULT FASTCALL UserCallNextHookEx(PHOOK Hook, int Code, WPARAM wParam, LPARAM lParam, BOOL Ansi) { LRESULT lResult = 0; BOOL BadChk = FALSE; /* Handle this one first. */ if ((Hook->HookId == WH_MOUSE) || (Hook->HookId == WH_CBT && Code == HCBT_CLICKSKIPPED)) { MOUSEHOOKSTRUCTEX Mouse; if (lParam) { _SEH2_TRY { ProbeForRead((PVOID)lParam, sizeof(MOUSEHOOKSTRUCTEX), 1); RtlCopyMemory(&Mouse, (PVOID)lParam, sizeof(MOUSEHOOKSTRUCTEX)); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { BadChk = TRUE; } _SEH2_END; if (BadChk) { DPRINT1("HOOK WH_MOUSE read from lParam ERROR!\n"); } } if (!BadChk) { lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Mouse); } return lResult; } switch(Hook->HookId) { case WH_MOUSE_LL: { MSLLHOOKSTRUCT Mouse; if (lParam) { _SEH2_TRY { ProbeForRead((PVOID)lParam, sizeof(MSLLHOOKSTRUCT), 1); RtlCopyMemory(&Mouse, (PVOID)lParam, sizeof(MSLLHOOKSTRUCT)); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { BadChk = TRUE; } _SEH2_END; if (BadChk) { DPRINT1("HOOK WH_MOUSE_LL read from lParam ERROR!\n"); } } if (!BadChk) { lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Mouse); } break; } case WH_KEYBOARD_LL: { KBDLLHOOKSTRUCT Keyboard; if (lParam) { _SEH2_TRY { ProbeForRead((PVOID)lParam, sizeof(KBDLLHOOKSTRUCT), 1); RtlCopyMemory(&Keyboard, (PVOID)lParam, sizeof(KBDLLHOOKSTRUCT)); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { BadChk = TRUE; } _SEH2_END; if (BadChk) { DPRINT1("HOOK WH_KEYBORD_LL read from lParam ERROR!\n"); } } if (!BadChk) { lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Keyboard); } break; } case WH_MSGFILTER: case WH_SYSMSGFILTER: case WH_GETMESSAGE: { MSG Msg; if (lParam) { _SEH2_TRY { ProbeForRead((PVOID)lParam, sizeof(MSG), 1); RtlCopyMemory(&Msg, (PVOID)lParam, sizeof(MSG)); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { BadChk = TRUE; } _SEH2_END; if (BadChk) { DPRINT1("HOOK WH_XMESSAGEX read from lParam ERROR!\n"); } } if (!BadChk) { lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&Msg); if (lParam && (Hook->HookId == WH_GETMESSAGE)) { _SEH2_TRY { ProbeForWrite((PVOID)lParam, sizeof(MSG), 1); RtlCopyMemory((PVOID)lParam, &Msg, sizeof(MSG)); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { BadChk = TRUE; } _SEH2_END; if (BadChk) { DPRINT1("HOOK WH_GETMESSAGE write to lParam ERROR!\n"); } } } break; } case WH_CBT: DPRINT("HOOK WH_CBT!\n"); switch (Code) { case HCBT_CREATEWND: { LPCBT_CREATEWNDW pcbtcww = (LPCBT_CREATEWNDW)lParam; DPRINT("HOOK HCBT_CREATEWND\n"); _SEH2_TRY { if (Ansi) { ProbeForRead( pcbtcww, sizeof(CBT_CREATEWNDA), 1); ProbeForWrite(pcbtcww->lpcs, sizeof(CREATESTRUCTA), 1); ProbeForRead( pcbtcww->lpcs->lpszName, sizeof(CHAR), 1); if (!IS_ATOM(pcbtcww->lpcs->lpszClass)) { ProbeForRead( pcbtcww->lpcs->lpszClass, sizeof(CHAR), 1); } } else { ProbeForRead( pcbtcww, sizeof(CBT_CREATEWNDW), 1); ProbeForWrite(pcbtcww->lpcs, sizeof(CREATESTRUCTW), 1); ProbeForRead( pcbtcww->lpcs->lpszName, sizeof(WCHAR), 1); if (!IS_ATOM(pcbtcww->lpcs->lpszClass)) { ProbeForRead( pcbtcww->lpcs->lpszClass, sizeof(WCHAR), 1); } } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { BadChk = TRUE; } _SEH2_END; if (BadChk) { DPRINT1("HOOK HCBT_CREATEWND write ERROR!\n"); } /* The next call handles the structures. */ if (!BadChk && Hook->Proc) { lResult = co_HOOK_CallHookNext(Hook, Code, wParam, lParam); } break; } case HCBT_MOVESIZE: { RECTL rt; DPRINT("HOOK HCBT_MOVESIZE\n"); if (lParam) { _SEH2_TRY { ProbeForRead((PVOID)lParam, sizeof(RECT), 1); RtlCopyMemory(&rt, (PVOID)lParam, sizeof(RECT)); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { BadChk = TRUE; } _SEH2_END; if (BadChk) { DPRINT1("HOOK HCBT_MOVESIZE read from lParam ERROR!\n"); } } if (!BadChk) { lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&rt); } break; } case HCBT_ACTIVATE: { CBTACTIVATESTRUCT CbAs; DPRINT("HOOK HCBT_ACTIVATE\n"); if (lParam) { _SEH2_TRY { ProbeForRead((PVOID)lParam, sizeof(CBTACTIVATESTRUCT), 1); RtlCopyMemory(&CbAs, (PVOID)lParam, sizeof(CBTACTIVATESTRUCT)); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { BadChk = TRUE; } _SEH2_END; if (BadChk) { DPRINT1("HOOK HCBT_ACTIVATE read from lParam ERROR!\n"); } } if (!BadChk) { lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)&CbAs); } break; } /* The rest just use default. */ default: DPRINT("HOOK HCBT_ %d\n",Code); lResult = co_HOOK_CallHookNext(Hook, Code, wParam, lParam); break; } break; case WH_JOURNALPLAYBACK: case WH_JOURNALRECORD: { EVENTMSG EventMsg; if (lParam) { _SEH2_TRY { ProbeForRead((PVOID)lParam, sizeof(EVENTMSG), 1); RtlCopyMemory(&EventMsg, (PVOID)lParam, sizeof(EVENTMSG)); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { BadChk = TRUE; } _SEH2_END; if (BadChk) { DPRINT1("HOOK WH_JOURNAL read from lParam ERROR!\n"); } } if (!BadChk) { lResult = co_HOOK_CallHookNext(Hook, Code, wParam, (LPARAM)(lParam ? &EventMsg : NULL)); if (lParam) { _SEH2_TRY { ProbeForWrite((PVOID)lParam, sizeof(EVENTMSG), 1); RtlCopyMemory((PVOID)lParam, &EventMsg, sizeof(EVENTMSG)); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { BadChk = TRUE; } _SEH2_END; if (BadChk) { DPRINT1("HOOK WH_JOURNAL write to lParam ERROR!\n"); } } } break; } case WH_DEBUG: lResult = IntCallDebugHook(Hook, Code, wParam, lParam); break; /* * Default the rest like, WH_FOREGROUNDIDLE, WH_KEYBOARD and WH_SHELL. */ case WH_FOREGROUNDIDLE: case WH_KEYBOARD: case WH_SHELL: lResult = co_HOOK_CallHookNext(Hook, Code, wParam, lParam); break; default: DPRINT1("Unsupported HOOK Id -> %d\n",Hook->HookId); break; } return lResult; } LRESULT APIENTRY NtUserCallNextHookEx(int Code, WPARAM wParam, LPARAM lParam, BOOL Ansi) { PHOOK HookObj, NextObj; PCLIENTINFO ClientInfo; PWINSTATION_OBJECT WinStaObj; NTSTATUS Status; DECLARE_RETURN(LRESULT); DPRINT("Enter NtUserCallNextHookEx\n"); UserEnterExclusive(); Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation, KernelMode, 0, &WinStaObj); if (!NT_SUCCESS(Status)) { SetLastNtError(Status); RETURN( 0); } ObDereferenceObject(WinStaObj); ClientInfo = GetWin32ClientInfo(); if (!ClientInfo) RETURN( 0); HookObj = ClientInfo->phkCurrent; if (!HookObj) RETURN( 0); /* Check that the first hook in the chain is not this hook */ NextObj = IntGetFirstHook(IntGetTable(HookObj), HookObj->HookId); /* Its the same so it has already been called */ if (HookObj == NextObj) RETURN(0); UserReferenceObject(HookObj); Ansi = HookObj->Ansi; if (NULL != HookObj->Thread && (HookObj->Thread != PsGetCurrentThread())) { DPRINT1("Thread mismatch\n"); UserDereferenceObject(HookObj); SetLastWin32Error(ERROR_INVALID_HANDLE); RETURN( 0); } NextObj = IntGetNextHook(HookObj); ClientInfo->phkCurrent = NextObj; /* Preset next hook from list. */ UserCallNextHookEx( HookObj, Code, wParam, lParam, Ansi); UserDereferenceObject(HookObj); RETURN( (LRESULT)NextObj); CLEANUP: DPRINT("Leave NtUserCallNextHookEx, ret=%i\n",_ret_); UserLeave(); END_CLEANUP; } HHOOK APIENTRY NtUserSetWindowsHookAW(int idHook, HOOKPROC lpfn, BOOL Ansi) { UNICODE_STRING USModuleName; RtlInitUnicodeString(&USModuleName, NULL); return NtUserSetWindowsHookEx(NULL, &USModuleName, 0, idHook, lpfn, Ansi); } HHOOK APIENTRY NtUserSetWindowsHookEx(HINSTANCE Mod, PUNICODE_STRING UnsafeModuleName, DWORD ThreadId, int HookId, HOOKPROC HookProc, BOOL Ansi) { PWINSTATION_OBJECT WinStaObj; PCLIENTINFO ClientInfo; BOOLEAN Global; PETHREAD Thread; PHOOK Hook; UNICODE_STRING ModuleName; NTSTATUS Status; HHOOK Handle; BOOLEAN ThreadReferenced = FALSE; DECLARE_RETURN(HHOOK); DPRINT("Enter NtUserSetWindowsHookEx\n"); UserEnterExclusive(); if (HookId < WH_MINHOOK || WH_MAXHOOK < HookId ) { SetLastWin32Error(ERROR_INVALID_PARAMETER); RETURN( NULL); } if (!HookProc) { SetLastWin32Error(ERROR_INVALID_FILTER_PROC); RETURN( NULL); } ClientInfo = GetWin32ClientInfo(); if (ThreadId) /* thread-local hook */ { if (HookId == WH_JOURNALRECORD || HookId == WH_JOURNALPLAYBACK || HookId == WH_KEYBOARD_LL || HookId == WH_MOUSE_LL || HookId == WH_SYSMSGFILTER) { /* these can only be global */ SetLastWin32Error(ERROR_INVALID_PARAMETER); RETURN( NULL); } Mod = NULL; Global = FALSE; if (!NT_SUCCESS(PsLookupThreadByThreadId((HANDLE)(DWORD_PTR) ThreadId, &Thread))) { DPRINT1("Invalid thread id 0x%x\n", ThreadId); SetLastWin32Error(ERROR_INVALID_PARAMETER); RETURN( NULL); } /* Thread was referenced */ ThreadReferenced = TRUE; if (Thread->ThreadsProcess != PsGetCurrentProcess()) { ObDereferenceObject(Thread); DPRINT1("Can't specify thread belonging to another process\n"); SetLastWin32Error(ERROR_INVALID_PARAMETER); RETURN( NULL); } } else /* system-global hook */ { if (HookId == WH_KEYBOARD_LL || HookId == WH_MOUSE_LL) { Mod = NULL; Thread = PsGetCurrentThread(); Status = ObReferenceObjectByPointer(Thread, THREAD_ALL_ACCESS, PsThreadType, KernelMode); if (!NT_SUCCESS(Status)) { SetLastNtError(Status); RETURN( (HANDLE) NULL); } /* Thread was referenced */ ThreadReferenced = TRUE; } else if (NULL == Mod) { SetLastWin32Error(ERROR_HOOK_NEEDS_HMOD); RETURN( NULL); } else { Thread = NULL; } Global = TRUE; } if ((Global && (HookId != WH_KEYBOARD_LL && HookId != WH_MOUSE_LL)) || WH_DEBUG == HookId || WH_JOURNALPLAYBACK == HookId || WH_JOURNALRECORD == HookId) { #if 0 /* Removed to get winEmbed working again */ UNIMPLEMENTED #else DPRINT1("Not implemented: HookId %d Global %s\n", HookId, Global ? "TRUE" : "FALSE"); #endif /* Dereference thread if needed */ if (ThreadReferenced) ObDereferenceObject(Thread); SetLastWin32Error(ERROR_NOT_SUPPORTED); RETURN( NULL); } Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation, KernelMode, 0, &WinStaObj); if (!NT_SUCCESS(Status)) { /* Dereference thread if needed */ if (ThreadReferenced) ObDereferenceObject(Thread); SetLastNtError(Status); RETURN( (HANDLE) NULL); } Hook = IntAddHook(Thread, HookId, Global, WinStaObj); if (NULL == Hook) { /* Dereference thread if needed */ if (ThreadReferenced) ObDereferenceObject(Thread); ObDereferenceObject(WinStaObj); RETURN( NULL); } /* Let IntFreeHook now that this thread needs a dereference */ if (ThreadReferenced) { Hook->Flags |= HOOK_THREAD_REFERENCED; } if (NULL != Mod) { Status = MmCopyFromCaller(&ModuleName, UnsafeModuleName, sizeof(UNICODE_STRING)); if (!NT_SUCCESS(Status)) { UserDereferenceObject(Hook); IntRemoveHook(Hook, WinStaObj, FALSE); ObDereferenceObject(WinStaObj); SetLastNtError(Status); RETURN( NULL); } Hook->ModuleName.Buffer = ExAllocatePoolWithTag(PagedPool, ModuleName.MaximumLength, TAG_HOOK); if (NULL == Hook->ModuleName.Buffer) { UserDereferenceObject(Hook); IntRemoveHook(Hook, WinStaObj, FALSE); ObDereferenceObject(WinStaObj); SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY); RETURN( NULL); } Hook->ModuleName.MaximumLength = ModuleName.MaximumLength; Status = MmCopyFromCaller(Hook->ModuleName.Buffer, ModuleName.Buffer, ModuleName.MaximumLength); if (!NT_SUCCESS(Status)) { ExFreePoolWithTag(Hook->ModuleName.Buffer, TAG_HOOK); UserDereferenceObject(Hook); IntRemoveHook(Hook, WinStaObj, FALSE); ObDereferenceObject(WinStaObj); SetLastNtError(Status); RETURN( NULL); } Hook->ModuleName.Length = ModuleName.Length; /* make proc relative to the module base */ Hook->Proc = (void *)((char *)HookProc - (char *)Mod); } else Hook->Proc = HookProc; Hook->Ansi = Ansi; Handle = UserHMGetHandle(Hook); /* Clear the client threads next hook. */ ClientInfo->phkCurrent = 0; UserDereferenceObject(Hook); ObDereferenceObject(WinStaObj); RETURN( Handle); CLEANUP: DPRINT("Leave NtUserSetWindowsHookEx, ret=%i\n",_ret_); UserLeave(); END_CLEANUP; } BOOL APIENTRY NtUserUnhookWindowsHookEx(HHOOK Hook) { PWINSTATION_OBJECT WinStaObj; PHOOK HookObj; NTSTATUS Status; DECLARE_RETURN(BOOL); DPRINT("Enter NtUserUnhookWindowsHookEx\n"); UserEnterExclusive(); Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation, KernelMode, 0, &WinStaObj); if (!NT_SUCCESS(Status)) { SetLastNtError(Status); RETURN( FALSE); } /* Status = UserReferenceObjectByHandle(gHandleTable, Hook, otHookProc, (PVOID *) &HookObj); */ if (!(HookObj = IntGetHookObject(Hook))) { DPRINT1("Invalid handle passed to NtUserUnhookWindowsHookEx\n"); ObDereferenceObject(WinStaObj); /* SetLastNtError(Status); */ RETURN( FALSE); } ASSERT(Hook == UserHMGetHandle(HookObj)); IntRemoveHook(HookObj, WinStaObj, FALSE); UserDereferenceObject(HookObj); ObDereferenceObject(WinStaObj); RETURN( TRUE); CLEANUP: DPRINT("Leave NtUserUnhookWindowsHookEx, ret=%i\n",_ret_); UserLeave(); END_CLEANUP; } /* EOF */