diff --git a/reactos/lib/user32/windows/hook.c b/reactos/lib/user32/windows/hook.c index 7e26167f5fe..ca36e087bd2 100644 --- a/reactos/lib/user32/windows/hook.c +++ b/reactos/lib/user32/windows/hook.c @@ -281,6 +281,7 @@ User32CallHookProcFromKernel(PVOID Arguments, ULONG ArgumentLength) PHOOKPROC_CBT_CREATEWND_EXTRA_ARGUMENTS CbtCreatewndExtra; WPARAM wParam; LPARAM lParam; + KBDLLHOOKSTRUCT *KeyboardLlData; Common = (PHOOKPROC_CALLBACK_ARGUMENTS) Arguments; @@ -353,6 +354,10 @@ User32CallHookProcFromKernel(PVOID Arguments, ULONG ArgumentLength) break; } break; + case WH_KEYBOARD_LL: + KeyboardLlData = (KBDLLHOOKSTRUCT *)((PCHAR) Common + Common->lParam); + Result = Common->Proc(Common->Code, Common->wParam, (LPARAM) KeyboardLlData); + break; default: return ZwCallbackReturn(NULL, 0, STATUS_NOT_SUPPORTED); } diff --git a/reactos/subsys/win32k/include/msgqueue.h b/reactos/subsys/win32k/include/msgqueue.h index 5d813f86465..6a51d017f7e 100644 --- a/reactos/subsys/win32k/include/msgqueue.h +++ b/reactos/subsys/win32k/include/msgqueue.h @@ -28,6 +28,7 @@ typedef struct _USER_SENT_MESSAGE ULONG_PTR CompletionCallbackContext; /* entry in the dispatching list of the sender's message queue */ LIST_ENTRY DispatchingListEntry; + BOOL HookMessage; } USER_SENT_MESSAGE, *PUSER_SENT_MESSAGE; typedef struct _USER_SENT_MESSAGE_NOTIFY @@ -127,7 +128,8 @@ MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue); NTSTATUS FASTCALL MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam, - UINT uTimeout, BOOL Block, ULONG_PTR *uResult); + UINT uTimeout, BOOL Block, BOOL HookMessage, + ULONG_PTR *uResult); PUSER_MESSAGE FASTCALL MsqCreateMessage(LPMSG Msg, BOOLEAN FreeLParam); VOID FASTCALL diff --git a/reactos/subsys/win32k/main/dllmain.c b/reactos/subsys/win32k/main/dllmain.c index 8ac1f9a3b35..23deca941db 100644 --- a/reactos/subsys/win32k/main/dllmain.c +++ b/reactos/subsys/win32k/main/dllmain.c @@ -240,6 +240,43 @@ Win32kThreadCallback (struct _ETHREAD *Thread, return STATUS_SUCCESS; } +/* Only used in ntuser/input.c KeyboardThreadMain(). If it's + not called there anymore, please delete */ +NTSTATUS +Win32kInitWin32Thread(PETHREAD Thread) +{ + PEPROCESS Process; + + Process = Thread->ThreadsProcess; + + if (Process->Win32Process == NULL) + { + /* FIXME - lock the process */ + Process->Win32Process = ExAllocatePool(NonPagedPool, sizeof(W32PROCESS)); + + if (Process->Win32Process == NULL) + return STATUS_NO_MEMORY; + + RtlZeroMemory(Process->Win32Process, sizeof(W32PROCESS)); + /* FIXME - unlock the process */ + + Win32kProcessCallback(Process, TRUE); + } + + if (Thread->Tcb.Win32Thread == NULL) + { + Thread->Tcb.Win32Thread = ExAllocatePool (NonPagedPool, sizeof(W32THREAD)); + if (Thread->Tcb.Win32Thread == NULL) + return STATUS_NO_MEMORY; + + RtlZeroMemory(Thread->Tcb.Win32Thread, sizeof(W32THREAD)); + + Win32kThreadCallback(Thread, TRUE); + } + + return(STATUS_SUCCESS); +} + /* * This definition doesn't work diff --git a/reactos/subsys/win32k/ntuser/callback.c b/reactos/subsys/win32k/ntuser/callback.c index 92c5c993953..84f5f6e82bb 100644 --- a/reactos/subsys/win32k/ntuser/callback.c +++ b/reactos/subsys/win32k/ntuser/callback.c @@ -293,6 +293,9 @@ IntCallHookProc(INT HookId, return 0; } break; + case WH_KEYBOARD_LL: + ArgumentLength += sizeof(KBDLLHOOKSTRUCT); + break; default: DPRINT1("Trying to call unsupported window hook %d\n", HookId); return 0; @@ -343,6 +346,10 @@ IntCallHookProc(INT HookId, break; } break; + case WH_KEYBOARD_LL: + RtlCopyMemory(Extra, (PVOID) lParam, sizeof(KBDLLHOOKSTRUCT)); + Common->lParam = (LPARAM) (Extra - (PCHAR) Common); + break; } ResultPointer = &Result; diff --git a/reactos/subsys/win32k/ntuser/hook.c b/reactos/subsys/win32k/ntuser/hook.c index 79a66b0063f..87fadc97631 100644 --- a/reactos/subsys/win32k/ntuser/hook.c +++ b/reactos/subsys/win32k/ntuser/hook.c @@ -182,8 +182,10 @@ IntFreeHook(PHOOKTABLE Table, PHOOK Hook, PWINSTATION_OBJECT WinStaObj) RtlFreeUnicodeString(&Hook->ModuleName); /* Dereference thread if required */ - if(Hook->Flags & HOOK_THREAD_REFERENCED) - ObDereferenceObject(Hook->Thread); + if (Hook->Flags & HOOK_THREAD_REFERENCED) + { + ObDereferenceObject(Hook->Thread); + } /* Close handle */ ObmCloseHandle(WinStaObj->HandleTable, Hook->Self); @@ -191,7 +193,7 @@ IntFreeHook(PHOOKTABLE Table, PHOOK Hook, PWINSTATION_OBJECT WinStaObj) /* remove a hook, freeing it if the chain is not in use */ STATIC FASTCALL VOID -IntRemoveHook(PHOOK Hook, PWINSTATION_OBJECT WinStaObj) +IntRemoveHook(PHOOK Hook, PWINSTATION_OBJECT WinStaObj, BOOL TableAlreadyLocked) { PHOOKTABLE Table = IntGetTable(Hook); @@ -201,7 +203,10 @@ IntRemoveHook(PHOOK Hook, PWINSTATION_OBJECT WinStaObj) return; } - IntLockHookTable(Table); + if (! TableAlreadyLocked) + { + IntLockHookTable(Table); + } if (0 != Table->Counts[HOOKID_TO_INDEX(Hook->HookId)]) { Hook->Proc = NULL; /* chain is in use, just mark it and return */ @@ -210,7 +215,10 @@ IntRemoveHook(PHOOK Hook, PWINSTATION_OBJECT WinStaObj) { IntFreeHook(Table, Hook, WinStaObj); } - IntUnLockHookTable(Table); + if (! TableAlreadyLocked) + { + IntUnLockHookTable(Table); + } } /* release a hook chain, removing deleted hooks if the use count drops to 0 */ @@ -249,17 +257,42 @@ IntReleaseHookChain(PHOOKTABLE Table, int HookId, PWINSTATION_OBJECT WinStaObj) IntUnLockHookTable(Table); } +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 = MsqSendMessage(Hook->Thread->Tcb.Win32Thread->MessageQueue, (HWND) Code, HookId, + wParam, lParam, /*500*/0, TRUE, TRUE, &uResult); + + return NT_SUCCESS(Status) ? uResult : 0; +} + LRESULT FASTCALL HOOK_CallHooks(INT HookId, INT Code, WPARAM wParam, LPARAM lParam) { PHOOK Hook; - PHOOKTABLE Table = MsqGetHooks(PsGetWin32Thread()->MessageQueue); + PW32THREAD Win32Thread; + PHOOKTABLE Table; LRESULT Result; PWINSTATION_OBJECT WinStaObj; NTSTATUS Status; ASSERT(WH_MINHOOK <= HookId && HookId <= WH_MAXHOOK); + Win32Thread = PsGetWin32Thread(); + if (NULL == Win32Thread) + { + Table = NULL; + } + else + { + Table = MsqGetHooks(Win32Thread->MessageQueue); + } + if (NULL == Table || ! (Hook = IntGetFirstValidHook(Table, HookId))) { /* try global table */ @@ -270,6 +303,13 @@ HOOK_CallHooks(INT HookId, INT Code, WPARAM wParam, LPARAM lParam) } } + 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"); @@ -294,7 +334,7 @@ HOOK_CallHooks(INT HookId, INT Code, WPARAM wParam, LPARAM lParam) 0, &WinStaObj); - if(! NT_SUCCESS(Status)) + if (! NT_SUCCESS(Status)) { DPRINT1("Invalid window station????\n"); } @@ -324,7 +364,7 @@ HOOK_DestroyThreadHooks(PETHREAD Thread) 0, &WinStaObj); - if(! NT_SUCCESS(Status)) + if (! NT_SUCCESS(Status)) { DPRINT1("Invalid window station????\n"); return; @@ -344,7 +384,7 @@ HOOK_DestroyThreadHooks(PETHREAD Thread) Elem = Elem->Flink; if (HookObj->Thread == Thread) { - IntRemoveHook(HookObj, WinStaObj); + IntRemoveHook(HookObj, WinStaObj, TRUE); } } break; @@ -372,7 +412,7 @@ NtUserCallNextHookEx( 0, &WinStaObj); - if(! NT_SUCCESS(Status)) + if (! NT_SUCCESS(Status)) { SetLastNtError(Status); return FALSE; @@ -433,7 +473,7 @@ NtUserSetWindowsHookEx( BOOL Ansi) { PWINSTATION_OBJECT WinStaObj; - BOOLEAN Global, ReleaseThread; + BOOLEAN Global; PETHREAD Thread; PHOOK Hook; UNICODE_STRING ModuleName; @@ -473,15 +513,23 @@ NtUserSetWindowsHookEx( SetLastWin32Error(ERROR_INVALID_PARAMETER); return NULL; } - ReleaseThread = TRUE; } else /* system-global hook */ { - ReleaseThread = FALSE; 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; + } } else if (NULL == Mod) { @@ -495,16 +543,20 @@ NtUserSetWindowsHookEx( Global = TRUE; } - /* We only (partially) support local WH_CBT hooks for now */ - if (WH_CBT != HookId || Global) + /* We only (partially) support local WH_CBT hooks and + * WH_KEYBOARD_LL/WH_MOUSE_LL hooks for now */ + if ((WH_CBT != HookId || Global) + && WH_KEYBOARD_LL != HookId && WH_MOUSE_LL != HookId) { #if 0 /* Removed to get winEmbed working again */ UNIMPLEMENTED #else DPRINT1("Not implemented: HookId %d Global %s\n", HookId, Global ? "TRUE" : "FALSE"); #endif - if(ReleaseThread) - ObDereferenceObject(Thread); + if (NULL != Thread) + { + ObDereferenceObject(Thread); + } SetLastWin32Error(ERROR_NOT_SUPPORTED); return NULL; } @@ -514,10 +566,12 @@ NtUserSetWindowsHookEx( 0, &WinStaObj); - if(! NT_SUCCESS(Status)) + if (! NT_SUCCESS(Status)) { - if(ReleaseThread && Thread) - ObDereferenceObject(Thread); + if (NULL != Thread) + { + ObDereferenceObject(Thread); + } SetLastNtError(Status); return (HANDLE) NULL; } @@ -525,14 +579,18 @@ NtUserSetWindowsHookEx( Hook = IntAddHook(Thread, HookId, Global, WinStaObj); if (NULL == Hook) { - if(ReleaseThread) - ObDereferenceObject(Thread); + if (NULL != Thread) + { + ObDereferenceObject(Thread); + } ObDereferenceObject(WinStaObj); return NULL; } - if(ReleaseThread) + if (NULL != Thread) + { Hook->Flags |= HOOK_THREAD_REFERENCED; + } if (NULL != Mod) { @@ -540,9 +598,11 @@ NtUserSetWindowsHookEx( if (! NT_SUCCESS(Status)) { ObmDereferenceObject(Hook); - IntRemoveHook(Hook, WinStaObj); - if(ReleaseThread) - ObDereferenceObject(Thread); + IntRemoveHook(Hook, WinStaObj, FALSE); + if (NULL != Thread) + { + ObDereferenceObject(Thread); + } ObDereferenceObject(WinStaObj); SetLastNtError(Status); return NULL; @@ -553,9 +613,11 @@ NtUserSetWindowsHookEx( if (NULL == Hook->ModuleName.Buffer) { ObmDereferenceObject(Hook); - IntRemoveHook(Hook, WinStaObj); - if(ReleaseThread) - ObDereferenceObject(Thread); + IntRemoveHook(Hook, WinStaObj, FALSE); + if (NULL != Thread) + { + ObDereferenceObject(Thread); + } ObDereferenceObject(WinStaObj); SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY); return NULL; @@ -567,9 +629,11 @@ NtUserSetWindowsHookEx( if (! NT_SUCCESS(Status)) { ObmDereferenceObject(Hook); - IntRemoveHook(Hook, WinStaObj); - if(ReleaseThread) - ObDereferenceObject(Thread); + IntRemoveHook(Hook, WinStaObj, FALSE); + if (NULL != Thread) + { + ObDereferenceObject(Thread); + } ObDereferenceObject(WinStaObj); SetLastNtError(Status); return NULL; @@ -618,7 +682,7 @@ NtUserUnhookWindowsHookEx( 0, &WinStaObj); - if(! NT_SUCCESS(Status)) + if (! NT_SUCCESS(Status)) { SetLastNtError(Status); return FALSE; @@ -635,7 +699,7 @@ NtUserUnhookWindowsHookEx( } ASSERT(Hook == HookObj->Self); - IntRemoveHook(HookObj, WinStaObj); + IntRemoveHook(HookObj, WinStaObj, FALSE); ObmDereferenceObject(HookObj); ObDereferenceObject(WinStaObj); diff --git a/reactos/subsys/win32k/ntuser/input.c b/reactos/subsys/win32k/ntuser/input.c index 7b66b097322..e9d77287220 100644 --- a/reactos/subsys/win32k/ntuser/input.c +++ b/reactos/subsys/win32k/ntuser/input.c @@ -184,7 +184,7 @@ MouseThreadMain(PVOID StartContext) DPRINT("Mouse Input Thread Starting...\n"); /* - * Receive and process keyboard input. + * Receive and process mouse input. */ while(InputThreadsRunning) { @@ -409,6 +409,8 @@ KeyboardThreadMain(PVOID StartContext) MSG msg; PUSER_MESSAGE_QUEUE FocusQueue; struct _ETHREAD *FocusThread; + extern NTSTATUS Win32kInitWin32Thread(PETHREAD Thread); + PKEYBOARD_INDICATOR_TRANSLATION IndicatorTrans = NULL; UINT ModifierState = 0; @@ -434,6 +436,22 @@ KeyboardThreadMain(PVOID StartContext) return; //(Status); } + /* Not sure if converting this thread to a win32 thread is such + a great idea. Since we're posting keyboard messages to the focus + window message queue, we'll be (indirectly) doing sendmessage + stuff from this thread (for WH_KEYBOARD_LL processing), which + means we need our own message queue. If keyboard messages were + instead queued to the system message queue, the thread removing + the message from the system message queue would be responsible + for WH_KEYBOARD_LL processing and we wouldn't need this thread + to be a win32 thread. */ + Status = Win32kInitWin32Thread(PsGetCurrentThread()); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Win32K: Failed making keyboard thread a win32 thread.\n"); + return; //(Status); + } + IntKeyboardGetIndicatorTrans(KeyboardDeviceHandle, &IndicatorTrans); diff --git a/reactos/subsys/win32k/ntuser/message.c b/reactos/subsys/win32k/ntuser/message.c index f06d8ca0529..b29136e44cd 100644 --- a/reactos/subsys/win32k/ntuser/message.c +++ b/reactos/subsys/win32k/ntuser/message.c @@ -1332,7 +1332,7 @@ IntSendMessageTimeoutSingle(HWND hWnd, } Status = MsqSendMessage(Window->MessageQueue, hWnd, Msg, wParam, lParam, - uTimeout, (uFlags & SMTO_BLOCK), uResult); + uTimeout, (uFlags & SMTO_BLOCK), FALSE, uResult); IntReleaseWindowObject(Window); if (STATUS_TIMEOUT == Status) { diff --git a/reactos/subsys/win32k/ntuser/msgqueue.c b/reactos/subsys/win32k/ntuser/msgqueue.c index c1af33485df..a3179e10fa4 100644 --- a/reactos/subsys/win32k/ntuser/msgqueue.c +++ b/reactos/subsys/win32k/ntuser/msgqueue.c @@ -618,6 +618,7 @@ MsqPostKeyboardMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) PUSER_MESSAGE_QUEUE FocusMessageQueue; MSG Msg; LARGE_INTEGER LargeTickCount; + KBDLLHOOKSTRUCT KbdHookData; DPRINT("MsqPostKeyboardMessage(uMsg 0x%x, wParam 0x%x, lParam 0x%x)\n", uMsg, wParam, lParam); @@ -632,6 +633,20 @@ MsqPostKeyboardMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) /* We can't get the Msg.pt point here since we don't know thread (and thus the window station) the message will end up in yet. */ + KbdHookData.vkCode = Msg.wParam; + KbdHookData.scanCode = (Msg.lParam >> 16) & 0xff; + KbdHookData.flags = (0 == (Msg.lParam & 0x01000000) ? 0 : LLKHF_EXTENDED) | + (0 == (Msg.lParam & 0x20000000) ? 0 : LLKHF_ALTDOWN) | + (0 == (Msg.lParam & 0x80000000) ? 0 : LLKHF_UP); + KbdHookData.time = Msg.time; + KbdHookData.dwExtraInfo = 0; + if (HOOK_CallHooks(WH_KEYBOARD_LL, HC_ACTION, Msg.message, (LPARAM) &KbdHookData)) + { + DPRINT("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n", + Msg.message, Msg.wParam, Msg.lParam); + return; + } + FocusMessageQueue = IntGetFocusMessageQueue(); if( !IntGetScreenDC() ) { /* FIXME: What to do about Msg.pt here? */ @@ -793,11 +808,21 @@ MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue) IntUnLockMessageQueue(MessageQueue); - /* Call the window procedure. */ - Result = IntSendMessage(Message->Msg.hwnd, - Message->Msg.message, - Message->Msg.wParam, - Message->Msg.lParam); + if (Message->HookMessage) + { + Result = HOOK_CallHooks(Message->Msg.message, + (INT) Message->Msg.hwnd, + Message->Msg.wParam, + Message->Msg.lParam); + } + else + { + /* Call the window procedure. */ + Result = IntSendMessage(Message->Msg.hwnd, + Message->Msg.message, + Message->Msg.wParam, + Message->Msg.lParam); + } /* remove the message from the local dispatching list, because it doesn't need to be cleaned up on thread termination anymore */ @@ -957,7 +982,8 @@ MsqSendNotifyMessage(PUSER_MESSAGE_QUEUE MessageQueue, NTSTATUS FASTCALL MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam, - UINT uTimeout, BOOL Block, ULONG_PTR *uResult) + UINT uTimeout, BOOL Block, BOOL HookMessage, + ULONG_PTR *uResult) { PUSER_SENT_MESSAGE Message; KEVENT CompletionEvent; @@ -992,6 +1018,7 @@ MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue, Message->SenderQueue = ThreadQueue; IntReferenceMessageQueue(ThreadQueue); Message->CompletionCallback = NULL; + Message->HookMessage = HookMessage; IntReferenceMessageQueue(MessageQueue); diff --git a/reactos/w32api/include/winuser.h b/reactos/w32api/include/winuser.h index 7c6ee862588..4c1a681671a 100644 --- a/reactos/w32api/include/winuser.h +++ b/reactos/w32api/include/winuser.h @@ -2147,7 +2147,10 @@ extern "C" { #define MOD_ON_KEYUP 2048 #define MOD_RIGHT 16384 #define MOD_LEFT 32768 +#define LLKHF_EXTENDED 0x00000001 +#define LLKHF_INJECTED 0x00000010 #define LLKHF_ALTDOWN 0x00000020 +#define LLKHF_UP 0x00000080 #if (WINVER >= 0x0500) #define FLASHW_STOP 0 #define FLASHW_CAPTION 1