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(gpepCSRSS
);
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(gpepCSRSS
);
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
->ptiHooked
;
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
= Hook
->ptiHooked
;
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
->ptiHooked
) // Local
1040 pti
= Hook
->ptiHooked
;
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 if ( ptiHook
->rpdesk
!= pti
->rpdesk
) // gptiCurrent->rpdesk)
1501 ERR("Local hook wrong desktop HookId: %d\n",HookId
);
1502 EngSetLastError(ERROR_ACCESS_DENIED
);
1506 if (Thread
->ThreadsProcess
!= PsGetCurrentProcess())
1509 (HookId
== WH_GETMESSAGE
||
1510 HookId
== WH_CALLWNDPROC
||
1512 HookId
== WH_HARDWARE
||
1513 HookId
== WH_DEBUG
||
1514 HookId
== WH_SHELL
||
1515 HookId
== WH_FOREGROUNDIDLE
||
1516 HookId
== WH_CALLWNDPROCRET
) )
1518 ERR("Local hook needs hMod HookId: %d\n",HookId
);
1519 EngSetLastError(ERROR_HOOK_NEEDS_HMOD
);
1523 if ( (ptiHook
->TIF_flags
& (TIF_CSRSSTHREAD
|TIF_SYSTEMTHREAD
)) &&
1524 (HookId
== WH_GETMESSAGE
||
1525 HookId
== WH_CALLWNDPROC
||
1527 HookId
== WH_HARDWARE
||
1528 HookId
== WH_DEBUG
||
1529 HookId
== WH_SHELL
||
1530 HookId
== WH_FOREGROUNDIDLE
||
1531 HookId
== WH_CALLWNDPROCRET
) )
1533 EngSetLastError(ERROR_HOOK_TYPE_NOT_ALLOWED
);
1538 else /* System-global hook */
1540 ptiHook
= pti
; // gptiCurrent;
1542 (HookId
== WH_GETMESSAGE
||
1543 HookId
== WH_CALLWNDPROC
||
1545 HookId
== WH_SYSMSGFILTER
||
1546 HookId
== WH_HARDWARE
||
1547 HookId
== WH_DEBUG
||
1548 HookId
== WH_SHELL
||
1549 HookId
== WH_FOREGROUNDIDLE
||
1550 HookId
== WH_CALLWNDPROCRET
) )
1552 ERR("Global hook needs hMod HookId: %d\n",HookId
);
1553 EngSetLastError(ERROR_HOOK_NEEDS_HMOD
);
1558 Status
= IntValidateWindowStationHandle( PsGetCurrentProcess()->Win32WindowStation
,
1563 if (!NT_SUCCESS(Status
))
1565 SetLastNtError(Status
);
1568 ObDereferenceObject(WinStaObj
);
1570 Hook
= UserCreateObject(gHandleTable
, NULL
, NULL
, (PHANDLE
)&Handle
, otHook
, sizeof(HOOK
));
1577 Hook
->ihmod
= (INT
)Mod
; // Module Index from atom table, Do this for now.
1578 Hook
->HookId
= HookId
;
1579 Hook
->rpdesk
= ptiHook
->rpdesk
;
1580 Hook
->phkNext
= NULL
; /* Dont use as a chain! Use link lists for chaining. */
1581 Hook
->Proc
= HookProc
;
1584 TRACE("Set Hook Desk 0x%x DeskInfo 0x%x Handle Desk 0x%x\n",pti
->rpdesk
, pti
->pDeskInfo
,Hook
->head
.rpdesk
);
1586 if (ThreadId
) /* Thread-local hook */
1588 InsertHeadList(&ptiHook
->aphkStart
[HOOKID_TO_INDEX(HookId
)], &Hook
->Chain
);
1589 ptiHook
->sphkCurrent
= NULL
;
1590 Hook
->ptiHooked
= ptiHook
;
1591 ptiHook
->fsHooks
|= HOOKID_TO_FLAG(HookId
);
1593 if (ptiHook
->pClientInfo
)
1595 if ( ptiHook
->ppi
== pti
->ppi
) /* gptiCurrent->ppi) */
1599 ptiHook
->pClientInfo
->fsHooks
= ptiHook
->fsHooks
;
1600 ptiHook
->pClientInfo
->phkCurrent
= NULL
;
1602 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1604 ERR("Problem writing to Local ClientInfo!\n");
1610 KeAttachProcess(&ptiHook
->ppi
->peProcess
->Pcb
);
1613 ptiHook
->pClientInfo
->fsHooks
= ptiHook
->fsHooks
;
1614 ptiHook
->pClientInfo
->phkCurrent
= NULL
;
1616 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1618 ERR("Problem writing to Remote ClientInfo!\n");
1627 InsertHeadList(&ptiHook
->rpdesk
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)], &Hook
->Chain
);
1628 Hook
->ptiHooked
= NULL
;
1629 //gptiCurrent->pDeskInfo->fsHooks |= HOOKID_TO_FLAG(HookId);
1630 ptiHook
->rpdesk
->pDeskInfo
->fsHooks
|= HOOKID_TO_FLAG(HookId
);
1631 ptiHook
->sphkCurrent
= NULL
;
1632 ptiHook
->pClientInfo
->phkCurrent
= NULL
;
1635 RtlInitUnicodeString(&Hook
->ModuleName
, NULL
);
1639 Status
= MmCopyFromCaller(&ModuleName
,
1641 sizeof(UNICODE_STRING
));
1642 if (!NT_SUCCESS(Status
))
1644 IntRemoveHook(Hook
);
1645 SetLastNtError(Status
);
1649 Hook
->ModuleName
.Buffer
= ExAllocatePoolWithTag( PagedPool
,
1650 ModuleName
.MaximumLength
,
1652 if (NULL
== Hook
->ModuleName
.Buffer
)
1654 IntRemoveHook(Hook
);
1655 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1659 Hook
->ModuleName
.MaximumLength
= ModuleName
.MaximumLength
;
1660 Status
= MmCopyFromCaller( Hook
->ModuleName
.Buffer
,
1662 ModuleName
.MaximumLength
);
1663 if (!NT_SUCCESS(Status
))
1665 ExFreePoolWithTag(Hook
->ModuleName
.Buffer
, TAG_HOOK
);
1666 Hook
->ModuleName
.Buffer
= NULL
;
1667 IntRemoveHook(Hook
);
1668 SetLastNtError(Status
);
1672 Hook
->ModuleName
.Length
= ModuleName
.Length
;
1673 /* Make proc relative to the module base */
1674 Hook
->offPfn
= (ULONG_PTR
)((char *)HookProc
- (char *)Mod
);
1679 TRACE("Installing: HookId %d Global %s\n", HookId
, !ThreadId
? "TRUE" : "FALSE");
1683 TRACE("Leave NtUserSetWindowsHookEx, ret=%i\n",_ret_
);
1684 if (Thread
) ObDereferenceObject(Thread
);
1691 NtUserUnhookWindowsHookEx(HHOOK Hook
)
1694 DECLARE_RETURN(BOOL
);
1696 TRACE("Enter NtUserUnhookWindowsHookEx\n");
1697 UserEnterExclusive();
1699 if (!(HookObj
= IntGetHookObject(Hook
)))
1701 ERR("Invalid handle passed to NtUserUnhookWindowsHookEx\n");
1702 /* SetLastNtError(Status); */
1706 ASSERT(Hook
== UserHMGetHandle(HookObj
));
1708 IntRemoveHook(HookObj
);
1710 UserDereferenceObject(HookObj
);
1715 TRACE("Leave NtUserUnhookWindowsHookEx, ret=%i\n",_ret_
);
1722 NtUserRegisterUserApiHook(
1723 PUNICODE_STRING m_dllname1
,
1724 PUNICODE_STRING m_funname1
,
1729 UNICODE_STRING strDllNameSafe
;
1730 UNICODE_STRING strFuncNameSafe
;
1733 /* Probe and capture parameters */
1734 Status
= ProbeAndCaptureUnicodeString(&strDllNameSafe
, UserMode
, m_dllname1
);
1735 if(!NT_SUCCESS(Status
))
1737 EngSetLastError(RtlNtStatusToDosError(Status
));
1741 Status
= ProbeAndCaptureUnicodeString(&strFuncNameSafe
, UserMode
, m_funname1
);
1742 if(!NT_SUCCESS(Status
))
1744 ReleaseCapturedUnicodeString(&strDllNameSafe
, UserMode
);
1745 EngSetLastError(RtlNtStatusToDosError(Status
));
1749 UserEnterExclusive();
1751 /* Call internal function */
1752 ret
= UserRegisterUserApiHook(&strDllNameSafe
, &strFuncNameSafe
);
1756 /* Cleanup only in case of failure */
1759 ReleaseCapturedUnicodeString(&strDllNameSafe
, UserMode
);
1760 ReleaseCapturedUnicodeString(&strFuncNameSafe
, UserMode
);
1768 NtUserUnregisterUserApiHook(VOID
)
1772 UserEnterExclusive();
1773 ret
= UserUnregisterUserApiHook();