2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Window hooks
5 * FILE: subsystems/win32/win32k/ntuser/hook.c
6 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * James Tabor (james.tabor@rectos.org)
8 * Rafal Harabien (rafalh@reactos.org)
9 * NOTE: Most of this code was adapted from Wine,
10 * Copyright (C) 2002 Alexandre Julliard
14 DBG_DEFAULT_CHANNEL(UserHook
);
16 typedef struct _HOOKPACK
21 } HOOKPACK
, *PHOOKPACK
;
23 UNICODE_STRING strUahModule
;
24 UNICODE_STRING strUahInitFunc
;
25 PPROCESSINFO ppiUahServer
;
27 /* PRIVATE FUNCTIONS *********************************************************/
29 /* Calls ClientLoadLibrary in user32 in order to load or unload a module */
31 IntLoadHookModule(int iHookID
, HHOOK hHook
, BOOL Unload
)
36 ppi
= PsGetCurrentProcessWin32Process();
38 ERR("IntLoadHookModule. Client PID: %d\n", PsGetProcessId(ppi
->peProcess
));
40 /* Check if this is the api hook */
41 if(iHookID
== WH_APIHOOK
)
43 if(!Unload
&& !(ppi
->W32PF_flags
& W32PF_APIHOOKLOADED
))
45 /* A callback in user mode can trigger UserLoadApiHook to be called and
46 as a result IntLoadHookModule will be called recursively.
47 To solve this we set the flag that means that the appliaction has
48 loaded the api hook before the callback and in case of error we remove it */
49 ppi
->W32PF_flags
|= W32PF_APIHOOKLOADED
;
51 /* Call ClientLoadLibrary in user32 */
52 bResult
= co_IntClientLoadLibrary(&strUahModule
, &strUahInitFunc
, Unload
, TRUE
);
53 TRACE("co_IntClientLoadLibrary returned %d\n", bResult
);
56 /* Remove the flag we set before */
57 ppi
->W32PF_flags
&= ~W32PF_APIHOOKLOADED
;
61 else if(Unload
&& (ppi
->W32PF_flags
& W32PF_APIHOOKLOADED
))
63 /* Call ClientLoadLibrary in user32 */
64 bResult
= co_IntClientLoadLibrary(NULL
, NULL
, Unload
, TRUE
);
67 ppi
->W32PF_flags
&= ~W32PF_APIHOOKLOADED
;
81 IntHookModuleUnloaded:
82 Sends a internal message to all threads of the requested desktop
83 and notifies them that a global hook was destroyed
84 and an injected module must be unloaded.
85 As a result, IntLoadHookModule will be called for all the threads that
86 will receive the special purpose internal message.
89 IntHookModuleUnloaded(PDESKTOP pdesk
, int iHookID
, HHOOK hHook
)
91 PTHREADINFO ptiCurrent
;
92 PLIST_ENTRY ListEntry
;
95 ERR("IntHookModuleUnloaded: iHookID=%d\n", iHookID
);
97 ppiCsr
= PsGetProcessWin32Process(CsrProcess
);
99 ListEntry
= pdesk
->PtiList
.Flink
;
100 while(ListEntry
!= &pdesk
->PtiList
)
102 ptiCurrent
= CONTAINING_RECORD(ListEntry
, THREADINFO
, PtiLink
);
104 /* FIXME: Do some more security checks here */
106 /* FIXME: The first check is a reactos specific hack for system threads */
107 if(!PsIsSystemProcess(ptiCurrent
->ppi
->peProcess
) &&
108 ptiCurrent
->ppi
!= ppiCsr
)
110 if(ptiCurrent
->ppi
->W32PF_flags
& W32PF_APIHOOKLOADED
)
112 TRACE("IntHookModuleUnloaded: sending message to PID %d, ppi=0x%x\n", PsGetProcessId(ptiCurrent
->ppi
->peProcess
), ptiCurrent
->ppi
);
113 co_MsqSendMessageAsync( ptiCurrent
,
124 ListEntry
= ListEntry
->Flink
;
132 UserLoadApiHook(VOID
)
134 return IntLoadHookModule(WH_APIHOOK
, 0, FALSE
);
139 UserRegisterUserApiHook(
140 PUNICODE_STRING pstrDllName
,
141 PUNICODE_STRING pstrFuncName
)
143 PTHREADINFO pti
, ptiCurrent
;
145 PWND DesktopWindow
, pwndCurrent
;
149 pti
= PsGetCurrentThreadWin32Thread();
150 ppiCsr
= PsGetProcessWin32Process(CsrProcess
);
152 /* Fail if the api hook is already registered */
153 if(gpsi
->dwSRVIFlags
& SRVINFO_APIHOOK
)
158 ERR("UserRegisterUserApiHook. Server PID: %d\n", PsGetProcessId(pti
->ppi
->peProcess
));
160 /* Register the api hook */
161 gpsi
->dwSRVIFlags
|= SRVINFO_APIHOOK
;
163 strUahModule
= *pstrDllName
;
164 strUahInitFunc
= *pstrFuncName
;
165 ppiUahServer
= pti
->ppi
;
167 /* Broadcast an internal message to every top level window */
168 DesktopWindow
= UserGetWindowObject(IntGetDesktopWindow());
169 List
= IntWinListChildren(DesktopWindow
);
173 for (i
= 0; List
[i
]; i
++)
175 pwndCurrent
= UserGetWindowObject(List
[i
]);
176 if(pwndCurrent
== NULL
)
180 ptiCurrent
= pwndCurrent
->head
.pti
;
182 /* FIXME: The first check is a reactos specific hack for system threads */
183 if(PsIsSystemProcess(ptiCurrent
->ppi
->peProcess
) ||
184 ptiCurrent
->ppi
== ppiCsr
)
189 co_MsqSendMessageAsync( ptiCurrent
,
192 FALSE
, /* Load the module */
199 ExFreePoolWithTag(List
, USERTAG_WINDOWLIST
);
207 UserUnregisterUserApiHook(VOID
)
211 pti
= PsGetCurrentThreadWin32Thread();
213 /* Fail if the api hook is not registered */
214 if(!(gpsi
->dwSRVIFlags
& SRVINFO_APIHOOK
))
219 /* Only the process that registered the api hook can uregister it */
220 if(ppiUahServer
!= PsGetCurrentProcessWin32Process())
225 ERR("UserUnregisterUserApiHook. Server PID: %d\n", PsGetProcessId(pti
->ppi
->peProcess
));
227 /* Unregister the api hook */
228 gpsi
->dwSRVIFlags
&= ~SRVINFO_APIHOOK
;
230 ReleaseCapturedUnicodeString(&strUahModule
, UserMode
);
231 ReleaseCapturedUnicodeString(&strUahInitFunc
, UserMode
);
233 /* Notify all applications that the api hook module must be unloaded */
234 return IntHookModuleUnloaded(pti
->rpdesk
, WH_APIHOOK
, 0);
240 co_IntCallLowLevelHook(PHOOK Hook
,
251 ULONG_PTR uResult
= 0;
254 pti
= Hook
->Thread
->Tcb
.Win32Thread
;
256 pti
= Hook
->head
.pti
;
258 pHP
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(HOOKPACK
), TAG_HOOK
);
262 pHP
->lParam
= lParam
;
263 pHP
->pHookStructs
= NULL
;
265 // This prevents stack corruption from the caller.
268 case WH_JOURNALPLAYBACK
:
269 case WH_JOURNALRECORD
:
271 Size
= sizeof(EVENTMSG
);
274 Size
= sizeof(KBDLLHOOKSTRUCT
);
277 Size
= sizeof(MSLLHOOKSTRUCT
);
282 Size
= sizeof(MOUSEHOOKSTRUCT
);
292 pHP
->pHookStructs
= ExAllocatePoolWithTag(NonPagedPool
, Size
, TAG_HOOK
);
293 if (pHP
->pHookStructs
) RtlCopyMemory(pHP
->pHookStructs
, (PVOID
)lParam
, Size
);
296 /* FIXME: Should get timeout from
297 * HKEY_CURRENT_USER\Control Panel\Desktop\LowLevelHooksTimeout */
298 Status
= co_MsqSendMessage( pti
->MessageQueue
,
299 IntToPtr(Code
), // hWnd
307 if (!NT_SUCCESS(Status
))
309 ERR("Error Hook Call SendMsg. %d Status: 0x%x\n", Hook
->HookId
, Status
);
310 if (pHP
->pHookStructs
) ExFreePoolWithTag(pHP
->pHookStructs
, TAG_HOOK
);
311 ExFreePoolWithTag(pHP
, TAG_HOOK
);
313 return NT_SUCCESS(Status
) ? uResult
: 0;
318 // Dispatch MsgQueue Hook Call processor!
322 co_CallHook( INT HookId
,
329 PHOOKPACK pHP
= (PHOOKPACK
)lParam
;
332 lParam
= pHP
->lParam
;
336 case WH_JOURNALPLAYBACK
:
337 case WH_JOURNALRECORD
:
342 lParam
= (LPARAM
)pHP
->pHookStructs
;
346 /* The odds are high for this to be a Global call. */
347 Result
= co_IntCallHookProc( HookId
,
355 /* The odds so high, no one is waiting for the results. */
356 if (pHP
->pHookStructs
) ExFreePoolWithTag(pHP
->pHookStructs
, TAG_HOOK
);
357 ExFreePoolWithTag(pHP
, TAG_HOOK
);
364 co_HOOK_CallHookNext( PHOOK Hook
,
369 TRACE("Calling Next HOOK %d\n", Hook
->HookId
);
371 return co_IntCallHookProc( Hook
->HookId
,
383 co_IntCallDebugHook(PHOOK Hook
,
392 PVOID HooklParam
= NULL
;
399 ProbeForRead((PVOID
)lParam
,
400 sizeof(DEBUGHOOKINFO
),
403 RtlCopyMemory(&Debug
,
405 sizeof(DEBUGHOOKINFO
));
407 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
415 ERR("HOOK WH_DEBUG read from lParam ERROR!\n");
420 return lResult
; /* Need lParam! */
428 case HCBT_CLICKSKIPPED
:
429 Size
= sizeof(MOUSEHOOKSTRUCTEX
);
437 Size
= sizeof(CBTACTIVATESTRUCT
);
440 case HCBT_CREATEWND
: /* Handle ANSI? */
441 Size
= sizeof(CBT_CREATEWND
);
442 /* What shall we do? Size += sizeof(HOOKPROC_CBT_CREATEWND_EXTRA_ARGUMENTS); same as CREATESTRUCTEX */
446 Size
= sizeof(LPARAM
);
452 Size
= sizeof(MSLLHOOKSTRUCT
);
456 Size
= sizeof(KBDLLHOOKSTRUCT
);
460 case WH_SYSMSGFILTER
:
465 case WH_JOURNALPLAYBACK
:
466 case WH_JOURNALRECORD
:
467 Size
= sizeof(EVENTMSG
);
470 case WH_FOREGROUNDIDLE
:
474 Size
= sizeof(LPARAM
);
477 if (Size
> sizeof(LPARAM
))
478 HooklParam
= ExAllocatePoolWithTag(NonPagedPool
, Size
, TAG_HOOK
);
484 ProbeForRead((PVOID
)Debug
.lParam
,
488 RtlCopyMemory(HooklParam
,
492 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
500 ERR("HOOK WH_DEBUG read from Debug.lParam ERROR!\n");
501 ExFreePoolWithTag(HooklParam
, TAG_HOOK
);
506 if (HooklParam
) Debug
.lParam
= (LPARAM
)HooklParam
;
507 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Debug
);
508 if (HooklParam
) ExFreePoolWithTag(HooklParam
, TAG_HOOK
);
516 co_UserCallNextHookEx(PHOOK Hook
,
525 /* Handle this one first. */
526 if ((Hook
->HookId
== WH_MOUSE
) ||
527 (Hook
->HookId
== WH_CBT
&& Code
== HCBT_CLICKSKIPPED
))
529 MOUSEHOOKSTRUCTEX Mouse
;
534 ProbeForRead((PVOID
)lParam
,
535 sizeof(MOUSEHOOKSTRUCTEX
),
538 RtlCopyMemory(&Mouse
,
540 sizeof(MOUSEHOOKSTRUCTEX
));
542 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
550 ERR("HOOK WH_MOUSE read from lParam ERROR!\n");
556 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Mouse
);
566 MSLLHOOKSTRUCT Mouse
;
572 ProbeForRead((PVOID
)lParam
,
573 sizeof(MSLLHOOKSTRUCT
),
576 RtlCopyMemory(&Mouse
,
578 sizeof(MSLLHOOKSTRUCT
));
580 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
588 ERR("HOOK WH_MOUSE_LL read from lParam ERROR!\n");
594 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Mouse
);
601 KBDLLHOOKSTRUCT Keyboard
;
607 ProbeForRead((PVOID
)lParam
,
608 sizeof(KBDLLHOOKSTRUCT
),
611 RtlCopyMemory(&Keyboard
,
613 sizeof(KBDLLHOOKSTRUCT
));
615 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
623 ERR("HOOK WH_KEYBORD_LL read from lParam ERROR!\n");
629 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Keyboard
);
635 case WH_SYSMSGFILTER
:
644 ProbeForRead((PVOID
)lParam
,
652 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
660 ERR("HOOK WH_XMESSAGEX read from lParam ERROR!\n");
666 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Msg
);
668 if (lParam
&& (Hook
->HookId
== WH_GETMESSAGE
))
672 ProbeForWrite((PVOID
)lParam
,
676 RtlCopyMemory((PVOID
)lParam
,
680 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
688 ERR("HOOK WH_GETMESSAGE write to lParam ERROR!\n");
696 TRACE("HOOK WH_CBT!\n");
701 LPCBT_CREATEWNDW pcbtcww
= (LPCBT_CREATEWNDW
)lParam
;
703 TRACE("HOOK HCBT_CREATEWND\n");
708 ProbeForRead( pcbtcww
,
709 sizeof(CBT_CREATEWNDA
),
711 ProbeForWrite(pcbtcww
->lpcs
,
712 sizeof(CREATESTRUCTA
),
714 ProbeForRead( pcbtcww
->lpcs
->lpszName
,
718 if (!IS_ATOM(pcbtcww
->lpcs
->lpszClass
))
720 ProbeForRead( pcbtcww
->lpcs
->lpszClass
,
727 ProbeForRead( pcbtcww
,
728 sizeof(CBT_CREATEWNDW
),
730 ProbeForWrite(pcbtcww
->lpcs
,
731 sizeof(CREATESTRUCTW
),
733 ProbeForRead( pcbtcww
->lpcs
->lpszName
,
737 if (!IS_ATOM(pcbtcww
->lpcs
->lpszClass
))
739 ProbeForRead( pcbtcww
->lpcs
->lpszClass
,
745 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
753 ERR("HOOK HCBT_CREATEWND write ERROR!\n");
755 /* The next call handles the structures. */
756 if (!BadChk
&& Hook
->Proc
)
758 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, lParam
);
767 TRACE("HOOK HCBT_MOVESIZE\n");
773 ProbeForRead((PVOID
)lParam
,
781 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
789 ERR("HOOK HCBT_MOVESIZE read from lParam ERROR!\n");
795 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&rt
);
802 CBTACTIVATESTRUCT CbAs
;
804 TRACE("HOOK HCBT_ACTIVATE\n");
809 ProbeForRead((PVOID
)lParam
,
810 sizeof(CBTACTIVATESTRUCT
),
815 sizeof(CBTACTIVATESTRUCT
));
817 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
825 ERR("HOOK HCBT_ACTIVATE read from lParam ERROR!\n");
831 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&CbAs
);
836 /* The rest just use default. */
838 TRACE("HOOK HCBT_ %d\n",Code
);
839 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, lParam
);
844 Note WH_JOURNALPLAYBACK,
845 "To have the system wait before processing the message, the return value
846 must be the amount of time, in clock ticks, that the system should wait."
848 case WH_JOURNALPLAYBACK
:
849 case WH_JOURNALRECORD
:
857 ProbeForRead((PVOID
)lParam
,
861 RtlCopyMemory(&EventMsg
,
865 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
873 ERR("HOOK WH_JOURNAL read from lParam ERROR!\n");
879 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)(lParam
? &EventMsg
: NULL
));
885 ProbeForWrite((PVOID
)lParam
,
889 RtlCopyMemory((PVOID
)lParam
,
893 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
901 ERR("HOOK WH_JOURNAL write to lParam ERROR!\n");
909 lResult
= co_IntCallDebugHook(Hook
, Code
, wParam
, lParam
, Ansi
);
913 * Default the rest like, WH_FOREGROUNDIDLE, WH_KEYBOARD and WH_SHELL.
915 case WH_FOREGROUNDIDLE
:
918 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, lParam
);
922 ERR("Unsupported HOOK Id -> %d\n",Hook
->HookId
);
930 IntGetHookObject(HHOOK hHook
)
936 EngSetLastError(ERROR_INVALID_HOOK_HANDLE
);
940 Hook
= (PHOOK
)UserGetObject(gHandleTable
, hHook
, otHook
);
943 EngSetLastError(ERROR_INVALID_HOOK_HANDLE
);
947 UserReferenceObject(Hook
);
955 IntGetGlobalHookHandles(PDESKTOP pdo
, int HookId
)
957 PLIST_ENTRY pLastHead
, pElem
;
963 pLastHead
= &pdo
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
964 for (pElem
= pLastHead
->Flink
; pElem
!= pLastHead
; pElem
= pElem
->Flink
)
967 pList
= ExAllocatePoolWithTag(PagedPool
, (cHooks
+ 1) * sizeof(HHOOK
), TAG_HOOK
);
970 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
974 for (pElem
= pLastHead
->Flink
; pElem
!= pLastHead
; pElem
= pElem
->Flink
)
976 pHook
= CONTAINING_RECORD(pElem
, HOOK
, Chain
);
977 pList
[i
++] = pHook
->head
.h
;
984 /* Find the next hook in the chain */
987 IntGetNextHook(PHOOK Hook
)
989 int HookId
= Hook
->HookId
;
990 PLIST_ENTRY pLastHead
, pElem
;
995 pti
= ((PTHREADINFO
)Hook
->Thread
->Tcb
.Win32Thread
);
996 pLastHead
= &pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1000 pti
= PsGetCurrentThreadWin32Thread();
1001 pLastHead
= &pti
->rpdesk
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1004 pElem
= Hook
->Chain
.Flink
;
1005 if (pElem
!= pLastHead
)
1006 return CONTAINING_RECORD(pElem
, HOOK
, Chain
);
1010 /* Free a hook, removing it from its chain */
1014 IntFreeHook(PHOOK Hook
)
1016 RemoveEntryList(&Hook
->Chain
);
1017 if (Hook
->ModuleName
.Buffer
)
1019 ExFreePoolWithTag(Hook
->ModuleName
.Buffer
, TAG_HOOK
);
1020 Hook
->ModuleName
.Buffer
= NULL
;
1023 UserDeleteObject(UserHMGetHandle(Hook
), otHook
);
1026 /* Remove a hook, freeing it from the chain */
1030 IntRemoveHook(PHOOK Hook
)
1036 HookId
= Hook
->HookId
;
1038 if (Hook
->Thread
) // Local
1040 pti
= ((PTHREADINFO
)Hook
->Thread
->Tcb
.Win32Thread
);
1044 if ( IsListEmpty(&pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)]) )
1046 pti
->fsHooks
&= ~HOOKID_TO_FLAG(HookId
);
1049 pti
->pClientInfo
->fsHooks
= pti
->fsHooks
;
1051 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1061 pdo
= IntGetActiveDesktop();
1065 IsListEmpty(&pdo
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)]) )
1067 pdo
->pDeskInfo
->fsHooks
&= ~HOOKID_TO_FLAG(HookId
);
1074 HOOK_DestroyThreadHooks(PETHREAD Thread
)
1082 pti
= Thread
->Tcb
.Win32Thread
;
1083 pdo
= IntGetActiveDesktop();
1087 ERR("Kill Thread Hooks pti 0x%x pdo 0x%x\n",pti
,pdo
);
1091 // Local Thread cleanup.
1094 for (HookId
= WH_MINHOOK
; HookId
<= WH_MAXHOOK
; HookId
++)
1096 PLIST_ENTRY pLastHead
= &pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1098 pElem
= pLastHead
->Flink
;
1099 while (pElem
!= pLastHead
)
1101 HookObj
= CONTAINING_RECORD(pElem
, HOOK
, Chain
);
1102 pElem
= HookObj
->Chain
.Flink
; // get next element before hook is destroyed
1103 IntRemoveHook(HookObj
);
1107 pti
->pClientInfo
->fsHooks
= 0;
1109 // Global search based on Thread and cleanup.
1110 if (pdo
->pDeskInfo
->fsHooks
)
1112 for (HookId
= WH_MINHOOK
; HookId
<= WH_MAXHOOK
; HookId
++)
1114 PLIST_ENTRY pGLE
= &pdo
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1116 pElem
= pGLE
->Flink
;
1117 while (pElem
!= pGLE
)
1119 HookObj
= CONTAINING_RECORD(pElem
, HOOK
, Chain
);
1120 pElem
= HookObj
->Chain
.Flink
; // Get next element before hook is destroyed
1121 if (HookObj
->head
.pti
== pti
)
1123 IntRemoveHook(HookObj
);
1132 Win32k Kernel Space Hook Caller.
1136 co_HOOK_CallHooks( INT HookId
,
1141 PHOOK Hook
, SaveHook
;
1143 PCLIENTINFO ClientInfo
;
1144 PLIST_ENTRY pLastHead
;
1146 BOOL Local
= FALSE
, Global
= FALSE
;
1148 USER_REFERENCE_ENTRY Ref
;
1150 ASSERT(WH_MINHOOK
<= HookId
&& HookId
<= WH_MAXHOOK
);
1152 pti
= PsGetCurrentThreadWin32Thread();
1153 if (!pti
|| !pti
->rpdesk
|| !pti
->rpdesk
->pDeskInfo
)
1155 pdo
= IntGetActiveDesktop();
1156 /* If KeyboardThread|MouseThread|(RawInputThread or RIT) aka system threads,
1157 pti->fsHooks most likely, is zero. So process KbT & MsT to "send" the message.
1159 if ( !pti
|| !pdo
|| (!(HookId
== WH_KEYBOARD_LL
) && !(HookId
== WH_MOUSE_LL
)) )
1161 TRACE("No PDO %d\n", HookId
);
1170 if ( pti
->TIF_flags
& (TIF_INCLEANUP
|TIF_DISABLEHOOKS
))
1172 TRACE("Hook Thread dead %d\n", HookId
);
1176 if ( ISITHOOKED(HookId
) )
1178 TRACE("Local Hooker %d\n", HookId
);
1182 if ( pdo
->pDeskInfo
->fsHooks
& HOOKID_TO_FLAG(HookId
) )
1184 TRACE("Global Hooker %d\n", HookId
);
1188 if ( !Local
&& !Global
) goto Exit
; // No work!
1192 /* SetWindowHookEx sorts out the Thread issue by placing the Hook to
1193 the correct Thread if not NULL.
1197 pLastHead
= &pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1198 if (IsListEmpty(pLastHead
))
1200 ERR("No Local Hook Found!\n");
1204 Hook
= CONTAINING_RECORD(pLastHead
->Flink
, HOOK
, Chain
);
1205 UserRefObjectCo(Hook
, &Ref
);
1207 ClientInfo
= pti
->pClientInfo
;
1208 SaveHook
= pti
->sphkCurrent
;
1209 /* Note: Setting pti->sphkCurrent will also lock the next hook to this
1210 * hook ID. So, the CallNextHookEx will only call to that hook ID
1211 * chain anyway. For Thread Hooks....
1214 /* Load it for the next call. */
1215 pti
->sphkCurrent
= Hook
;
1216 Hook
->phkNext
= IntGetNextHook(Hook
);
1221 ClientInfo
->phkCurrent
= Hook
;
1223 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1225 ClientInfo
= NULL
; // Don't bother next run.
1229 Result
= co_IntCallHookProc( HookId
,
1240 ClientInfo
->phkCurrent
= SaveHook
;
1242 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1247 pti
->sphkCurrent
= SaveHook
;
1248 Hook
->phkNext
= NULL
;
1249 UserDerefObjectCo(Hook
);
1254 PTHREADINFO ptiHook
;
1255 HHOOK
*pHookHandles
;
1258 /* Keep hooks in array because hooks can be destroyed in user world */
1259 pHookHandles
= IntGetGlobalHookHandles(pdo
, HookId
);
1263 /* Performance goes down the drain. If more hooks are associated to this
1264 * hook ID, this will have to post to each of the thread message queues
1265 * or make a direct call.
1267 for(i
= 0; pHookHandles
[i
]; ++i
)
1269 Hook
= (PHOOK
)UserGetObject(gHandleTable
, pHookHandles
[i
], otHook
);
1272 ERR("Invalid hook!\n");
1275 UserRefObjectCo(Hook
, &Ref
);
1277 /* Hook->Thread is null, we hax around this with Hook->head.pti. */
1278 ptiHook
= Hook
->head
.pti
;
1280 if ( (pti
->TIF_flags
& TIF_DISABLEHOOKS
) || (ptiHook
->TIF_flags
& TIF_INCLEANUP
))
1282 TRACE("Next Hook 0x%x, 0x%x\n",ptiHook
->rpdesk
,pdo
);
1286 if (ptiHook
!= pti
)
1289 if ( HookId
== WH_JOURNALPLAYBACK
|| // 1 | 0
1290 HookId
== WH_JOURNALRECORD
|| // 1 | 0
1291 HookId
== WH_KEYBOARD
|| // 1 | 200
1292 HookId
== WH_MOUSE
|| // 1 | 200
1293 HookId
== WH_KEYBOARD_LL
|| // 0 | 300
1294 HookId
== WH_MOUSE_LL
) // 0 | 300
1296 TRACE("\nGlobal Hook posting to another Thread! %d\n",HookId
);
1297 Result
= co_IntCallLowLevelHook(Hook
, Code
, wParam
, lParam
);
1301 { /* Make the direct call. */
1302 TRACE("Local Hook calling to Thread! %d\n",HookId
);
1303 Result
= co_IntCallHookProc( HookId
,
1311 UserDerefObjectCo(Hook
);
1313 ExFreePoolWithTag(pHookHandles
, TAG_HOOK
);
1314 TRACE("Ret: Global HookId %d Result 0x%x\n", HookId
,Result
);
1322 IntUnhookWindowsHook(int HookId
, HOOKPROC pfnFilterProc
)
1325 PLIST_ENTRY pLastHead
, pElement
;
1326 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
1328 if (HookId
< WH_MINHOOK
|| WH_MAXHOOK
< HookId
)
1330 EngSetLastError(ERROR_INVALID_HOOK_FILTER
);
1336 pLastHead
= &pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1338 pElement
= pLastHead
->Flink
;
1339 while (pElement
!= pLastHead
)
1341 Hook
= CONTAINING_RECORD(pElement
, HOOK
, Chain
);
1343 if (Hook
->Proc
== pfnFilterProc
)
1345 if (Hook
->head
.pti
== pti
)
1347 IntRemoveHook(Hook
);
1348 UserDereferenceObject(Hook
);
1353 EngSetLastError(ERROR_ACCESS_DENIED
);
1358 pElement
= Hook
->Chain
.Flink
;
1365 * Support for compatibility only? Global hooks are processed in kernel space.
1366 * This is very thread specific! Never seeing applications with more than one
1367 * hook per thread installed. Most of the applications are Global hookers and
1368 * associated with just one hook Id. Maybe it's for diagnostic testing or a
1369 * throw back to 3.11?
1373 NtUserCallNextHookEx( int Code
,
1379 PHOOK HookObj
, NextObj
;
1380 PCLIENTINFO ClientInfo
;
1381 LRESULT lResult
= 0;
1382 DECLARE_RETURN(LRESULT
);
1384 TRACE("Enter NtUserCallNextHookEx\n");
1385 UserEnterExclusive();
1387 pti
= GetW32ThreadInfo();
1389 HookObj
= pti
->sphkCurrent
;
1391 if (!HookObj
) RETURN( 0);
1393 NextObj
= HookObj
->phkNext
;
1395 pti
->sphkCurrent
= NextObj
;
1396 ClientInfo
= pti
->pClientInfo
;
1399 ClientInfo
->phkCurrent
= NextObj
;
1401 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1407 /* Now in List run down. */
1408 if (ClientInfo
&& NextObj
)
1410 NextObj
->phkNext
= IntGetNextHook(NextObj
);
1411 lResult
= co_UserCallNextHookEx( NextObj
, Code
, wParam
, lParam
, NextObj
->Ansi
);
1416 TRACE("Leave NtUserCallNextHookEx, ret=%i\n",_ret_
);
1423 NtUserSetWindowsHookAW( int idHook
,
1428 UNICODE_STRING USModuleName
;
1430 RtlInitUnicodeString(&USModuleName
, NULL
);
1431 ThreadId
= PtrToUint(NtCurrentTeb()->ClientId
.UniqueThread
);
1433 return NtUserSetWindowsHookEx( NULL
,
1443 NtUserSetWindowsHookEx( HINSTANCE Mod
,
1444 PUNICODE_STRING UnsafeModuleName
,
1450 PWINSTATION_OBJECT WinStaObj
;
1452 UNICODE_STRING ModuleName
;
1455 PETHREAD Thread
= NULL
;
1456 PTHREADINFO pti
, ptiHook
= NULL
;
1457 DECLARE_RETURN(HHOOK
);
1459 TRACE("Enter NtUserSetWindowsHookEx\n");
1460 UserEnterExclusive();
1462 pti
= PsGetCurrentThreadWin32Thread();
1464 if (HookId
< WH_MINHOOK
|| WH_MAXHOOK
< HookId
)
1466 EngSetLastError(ERROR_INVALID_HOOK_FILTER
);
1472 EngSetLastError(ERROR_INVALID_FILTER_PROC
);
1476 if (ThreadId
) /* thread-local hook */
1478 if ( HookId
== WH_JOURNALRECORD
||
1479 HookId
== WH_JOURNALPLAYBACK
||
1480 HookId
== WH_KEYBOARD_LL
||
1481 HookId
== WH_MOUSE_LL
||
1482 HookId
== WH_SYSMSGFILTER
)
1484 ERR("Local hook installing Global HookId: %d\n",HookId
);
1485 /* these can only be global */
1486 EngSetLastError(ERROR_GLOBAL_ONLY_HOOK
);
1490 if (!NT_SUCCESS(PsLookupThreadByThreadId((HANDLE
)(DWORD_PTR
) ThreadId
, &Thread
)))
1492 ERR("Invalid thread id 0x%x\n", ThreadId
);
1493 EngSetLastError(ERROR_INVALID_PARAMETER
);
1497 ptiHook
= Thread
->Tcb
.Win32Thread
;
1499 ObDereferenceObject(Thread
);
1501 if ( ptiHook
->rpdesk
!= pti
->rpdesk
) // gptiCurrent->rpdesk)
1503 ERR("Local hook wrong desktop HookId: %d\n",HookId
);
1504 EngSetLastError(ERROR_ACCESS_DENIED
);
1508 if (Thread
->ThreadsProcess
!= PsGetCurrentProcess())
1511 (HookId
== WH_GETMESSAGE
||
1512 HookId
== WH_CALLWNDPROC
||
1514 HookId
== WH_HARDWARE
||
1515 HookId
== WH_DEBUG
||
1516 HookId
== WH_SHELL
||
1517 HookId
== WH_FOREGROUNDIDLE
||
1518 HookId
== WH_CALLWNDPROCRET
) )
1520 ERR("Local hook needs hMod HookId: %d\n",HookId
);
1521 EngSetLastError(ERROR_HOOK_NEEDS_HMOD
);
1525 if ( (ptiHook
->TIF_flags
& (TIF_CSRSSTHREAD
|TIF_SYSTEMTHREAD
)) &&
1526 (HookId
== WH_GETMESSAGE
||
1527 HookId
== WH_CALLWNDPROC
||
1529 HookId
== WH_HARDWARE
||
1530 HookId
== WH_DEBUG
||
1531 HookId
== WH_SHELL
||
1532 HookId
== WH_FOREGROUNDIDLE
||
1533 HookId
== WH_CALLWNDPROCRET
) )
1535 EngSetLastError(ERROR_HOOK_TYPE_NOT_ALLOWED
);
1540 else /* System-global hook */
1542 ptiHook
= pti
; // gptiCurrent;
1544 (HookId
== WH_GETMESSAGE
||
1545 HookId
== WH_CALLWNDPROC
||
1547 HookId
== WH_SYSMSGFILTER
||
1548 HookId
== WH_HARDWARE
||
1549 HookId
== WH_DEBUG
||
1550 HookId
== WH_SHELL
||
1551 HookId
== WH_FOREGROUNDIDLE
||
1552 HookId
== WH_CALLWNDPROCRET
) )
1554 ERR("Global hook needs hMod HookId: %d\n",HookId
);
1555 EngSetLastError(ERROR_HOOK_NEEDS_HMOD
);
1560 Status
= IntValidateWindowStationHandle( PsGetCurrentProcess()->Win32WindowStation
,
1565 if (!NT_SUCCESS(Status
))
1567 SetLastNtError(Status
);
1570 ObDereferenceObject(WinStaObj
);
1572 Hook
= UserCreateObject(gHandleTable
, NULL
, NULL
, (PHANDLE
)&Handle
, otHook
, sizeof(HOOK
));
1579 Hook
->ihmod
= (INT
)Mod
; // Module Index from atom table, Do this for now.
1580 Hook
->Thread
= Thread
; /* Set Thread, Null is Global. */
1581 Hook
->HookId
= HookId
;
1582 Hook
->rpdesk
= ptiHook
->rpdesk
;
1583 Hook
->phkNext
= NULL
; /* Dont use as a chain! Use link lists for chaining. */
1584 Hook
->Proc
= HookProc
;
1587 TRACE("Set Hook Desk 0x%x DeskInfo 0x%x Handle Desk 0x%x\n",pti
->rpdesk
, pti
->pDeskInfo
,Hook
->head
.rpdesk
);
1589 if (ThreadId
) /* Thread-local hook */
1591 InsertHeadList(&ptiHook
->aphkStart
[HOOKID_TO_INDEX(HookId
)], &Hook
->Chain
);
1592 ptiHook
->sphkCurrent
= NULL
;
1593 Hook
->ptiHooked
= ptiHook
;
1594 ptiHook
->fsHooks
|= HOOKID_TO_FLAG(HookId
);
1596 if (ptiHook
->pClientInfo
)
1598 if ( ptiHook
->ppi
== pti
->ppi
) /* gptiCurrent->ppi) */
1602 ptiHook
->pClientInfo
->fsHooks
= ptiHook
->fsHooks
;
1603 ptiHook
->pClientInfo
->phkCurrent
= NULL
;
1605 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1607 ERR("Problem writing to Local ClientInfo!\n");
1613 KeAttachProcess(&ptiHook
->ppi
->peProcess
->Pcb
);
1616 ptiHook
->pClientInfo
->fsHooks
= ptiHook
->fsHooks
;
1617 ptiHook
->pClientInfo
->phkCurrent
= NULL
;
1619 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1621 ERR("Problem writing to Remote ClientInfo!\n");
1630 InsertHeadList(&ptiHook
->rpdesk
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)], &Hook
->Chain
);
1631 Hook
->ptiHooked
= NULL
;
1632 //gptiCurrent->pDeskInfo->fsHooks |= HOOKID_TO_FLAG(HookId);
1633 ptiHook
->rpdesk
->pDeskInfo
->fsHooks
|= HOOKID_TO_FLAG(HookId
);
1634 ptiHook
->sphkCurrent
= NULL
;
1635 ptiHook
->pClientInfo
->phkCurrent
= NULL
;
1638 RtlInitUnicodeString(&Hook
->ModuleName
, NULL
);
1642 Status
= MmCopyFromCaller(&ModuleName
,
1644 sizeof(UNICODE_STRING
));
1645 if (!NT_SUCCESS(Status
))
1647 IntRemoveHook(Hook
);
1648 SetLastNtError(Status
);
1652 Hook
->ModuleName
.Buffer
= ExAllocatePoolWithTag( PagedPool
,
1653 ModuleName
.MaximumLength
,
1655 if (NULL
== Hook
->ModuleName
.Buffer
)
1657 IntRemoveHook(Hook
);
1658 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1662 Hook
->ModuleName
.MaximumLength
= ModuleName
.MaximumLength
;
1663 Status
= MmCopyFromCaller( Hook
->ModuleName
.Buffer
,
1665 ModuleName
.MaximumLength
);
1666 if (!NT_SUCCESS(Status
))
1668 ExFreePoolWithTag(Hook
->ModuleName
.Buffer
, TAG_HOOK
);
1669 Hook
->ModuleName
.Buffer
= NULL
;
1670 IntRemoveHook(Hook
);
1671 SetLastNtError(Status
);
1675 Hook
->ModuleName
.Length
= ModuleName
.Length
;
1676 /* Make proc relative to the module base */
1677 Hook
->offPfn
= (ULONG_PTR
)((char *)HookProc
- (char *)Mod
);
1682 TRACE("Installing: HookId %d Global %s\n", HookId
, !ThreadId
? "TRUE" : "FALSE");
1686 TRACE("Leave NtUserSetWindowsHookEx, ret=%i\n",_ret_
);
1693 NtUserUnhookWindowsHookEx(HHOOK Hook
)
1696 DECLARE_RETURN(BOOL
);
1698 TRACE("Enter NtUserUnhookWindowsHookEx\n");
1699 UserEnterExclusive();
1701 if (!(HookObj
= IntGetHookObject(Hook
)))
1703 ERR("Invalid handle passed to NtUserUnhookWindowsHookEx\n");
1704 /* SetLastNtError(Status); */
1708 ASSERT(Hook
== UserHMGetHandle(HookObj
));
1710 IntRemoveHook(HookObj
);
1712 UserDereferenceObject(HookObj
);
1717 TRACE("Leave NtUserUnhookWindowsHookEx, ret=%i\n",_ret_
);
1724 NtUserRegisterUserApiHook(
1725 PUNICODE_STRING m_dllname1
,
1726 PUNICODE_STRING m_funname1
,
1731 UNICODE_STRING strDllNameSafe
;
1732 UNICODE_STRING strFuncNameSafe
;
1735 /* Probe and capture parameters */
1736 Status
= ProbeAndCaptureUnicodeString(&strDllNameSafe
, UserMode
, m_dllname1
);
1737 if(!NT_SUCCESS(Status
))
1739 EngSetLastError(RtlNtStatusToDosError(Status
));
1743 Status
= ProbeAndCaptureUnicodeString(&strFuncNameSafe
, UserMode
, m_funname1
);
1744 if(!NT_SUCCESS(Status
))
1746 ReleaseCapturedUnicodeString(&strDllNameSafe
, UserMode
);
1747 EngSetLastError(RtlNtStatusToDosError(Status
));
1751 UserEnterExclusive();
1753 /* Call internal function */
1754 ret
= UserRegisterUserApiHook(&strDllNameSafe
, &strFuncNameSafe
);
1758 /* Cleanup only in case of failure */
1761 ReleaseCapturedUnicodeString(&strDllNameSafe
, UserMode
);
1762 ReleaseCapturedUnicodeString(&strFuncNameSafe
, UserMode
);
1770 NtUserUnregisterUserApiHook(VOID
)
1774 UserEnterExclusive();
1775 ret
= UserUnregisterUserApiHook();