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
,
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
, TYPE_HOOK
);
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
), TYPE_HOOK
);
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
)
1063 pdo
= IntGetActiveDesktop();
1067 IsListEmpty(&pdo
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)]) )
1069 pdo
->pDeskInfo
->fsHooks
&= ~HOOKID_TO_FLAG(HookId
);
1076 HOOK_DestroyThreadHooks(PETHREAD Thread
)
1084 pti
= Thread
->Tcb
.Win32Thread
;
1085 pdo
= IntGetActiveDesktop();
1089 ERR("Kill Thread Hooks pti 0x%x pdo 0x%x\n",pti
,pdo
);
1093 // Local Thread cleanup.
1096 for (HookId
= WH_MINHOOK
; HookId
<= WH_MAXHOOK
; HookId
++)
1098 PLIST_ENTRY pLastHead
= &pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1100 pElem
= pLastHead
->Flink
;
1101 while (pElem
!= pLastHead
)
1103 HookObj
= CONTAINING_RECORD(pElem
, HOOK
, Chain
);
1104 pElem
= HookObj
->Chain
.Flink
; // get next element before hook is destroyed
1105 IntRemoveHook(HookObj
);
1109 pti
->pClientInfo
->fsHooks
= 0;
1111 // Global search based on Thread and cleanup.
1112 if (pdo
->pDeskInfo
->fsHooks
)
1114 for (HookId
= WH_MINHOOK
; HookId
<= WH_MAXHOOK
; HookId
++)
1116 PLIST_ENTRY pGLE
= &pdo
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1118 pElem
= pGLE
->Flink
;
1119 while (pElem
!= pGLE
)
1121 HookObj
= CONTAINING_RECORD(pElem
, HOOK
, Chain
);
1122 pElem
= HookObj
->Chain
.Flink
; // Get next element before hook is destroyed
1123 if (HookObj
->head
.pti
== pti
)
1125 IntRemoveHook(HookObj
);
1134 Win32k Kernel Space Hook Caller.
1138 co_HOOK_CallHooks( INT HookId
,
1143 PHOOK Hook
, SaveHook
;
1145 PCLIENTINFO ClientInfo
;
1146 PLIST_ENTRY pLastHead
;
1148 BOOL Local
= FALSE
, Global
= FALSE
;
1150 USER_REFERENCE_ENTRY Ref
;
1152 ASSERT(WH_MINHOOK
<= HookId
&& HookId
<= WH_MAXHOOK
);
1154 pti
= PsGetCurrentThreadWin32Thread();
1155 if (!pti
|| !pti
->rpdesk
|| !pti
->rpdesk
->pDeskInfo
)
1157 pdo
= IntGetActiveDesktop();
1158 /* If KeyboardThread|MouseThread|(RawInputThread or RIT) aka system threads,
1159 pti->fsHooks most likely, is zero. So process KbT & MsT to "send" the message.
1161 if ( !pti
|| !pdo
|| (!(HookId
== WH_KEYBOARD_LL
) && !(HookId
== WH_MOUSE_LL
)) )
1163 TRACE("No PDO %d\n", HookId
);
1172 if ( pti
->TIF_flags
& (TIF_INCLEANUP
|TIF_DISABLEHOOKS
))
1174 TRACE("Hook Thread dead %d\n", HookId
);
1178 if ( ISITHOOKED(HookId
) )
1180 TRACE("Local Hooker %d\n", HookId
);
1184 if ( pdo
->pDeskInfo
->fsHooks
& HOOKID_TO_FLAG(HookId
) )
1186 TRACE("Global Hooker %d\n", HookId
);
1190 if ( !Local
&& !Global
) goto Exit
; // No work!
1194 /* SetWindowHookEx sorts out the Thread issue by placing the Hook to
1195 the correct Thread if not NULL.
1199 pLastHead
= &pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1200 if (IsListEmpty(pLastHead
))
1202 ERR("No Local Hook Found!\n");
1206 Hook
= CONTAINING_RECORD(pLastHead
->Flink
, HOOK
, Chain
);
1207 UserRefObjectCo(Hook
, &Ref
);
1209 ClientInfo
= pti
->pClientInfo
;
1210 SaveHook
= pti
->sphkCurrent
;
1211 /* Note: Setting pti->sphkCurrent will also lock the next hook to this
1212 * hook ID. So, the CallNextHookEx will only call to that hook ID
1213 * chain anyway. For Thread Hooks....
1216 /* Load it for the next call. */
1217 pti
->sphkCurrent
= Hook
;
1218 Hook
->phkNext
= IntGetNextHook(Hook
);
1223 ClientInfo
->phkCurrent
= Hook
;
1225 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1227 ClientInfo
= NULL
; // Don't bother next run.
1231 Result
= co_IntCallHookProc( HookId
,
1242 ClientInfo
->phkCurrent
= SaveHook
;
1244 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1251 pti
->sphkCurrent
= SaveHook
;
1252 Hook
->phkNext
= NULL
;
1253 UserDerefObjectCo(Hook
);
1258 PTHREADINFO ptiHook
;
1259 HHOOK
*pHookHandles
;
1262 /* Keep hooks in array because hooks can be destroyed in user world */
1263 pHookHandles
= IntGetGlobalHookHandles(pdo
, HookId
);
1267 /* Performance goes down the drain. If more hooks are associated to this
1268 * hook ID, this will have to post to each of the thread message queues
1269 * or make a direct call.
1271 for(i
= 0; pHookHandles
[i
]; ++i
)
1273 Hook
= (PHOOK
)UserGetObject(gHandleTable
, pHookHandles
[i
], TYPE_HOOK
);
1276 ERR("Invalid hook!\n");
1279 UserRefObjectCo(Hook
, &Ref
);
1281 /* Hook->Thread is null, we hax around this with Hook->head.pti. */
1282 ptiHook
= Hook
->head
.pti
;
1284 if ( (pti
->TIF_flags
& TIF_DISABLEHOOKS
) || (ptiHook
->TIF_flags
& TIF_INCLEANUP
))
1286 TRACE("Next Hook 0x%x, 0x%x\n",ptiHook
->rpdesk
,pdo
);
1290 if (ptiHook
!= pti
)
1293 if ( HookId
== WH_JOURNALPLAYBACK
|| // 1 | 0
1294 HookId
== WH_JOURNALRECORD
|| // 1 | 0
1295 HookId
== WH_KEYBOARD
|| // 1 | 200
1296 HookId
== WH_MOUSE
|| // 1 | 200
1297 HookId
== WH_KEYBOARD_LL
|| // 0 | 300
1298 HookId
== WH_MOUSE_LL
) // 0 | 300
1300 TRACE("\nGlobal Hook posting to another Thread! %d\n",HookId
);
1301 Result
= co_IntCallLowLevelHook(Hook
, Code
, wParam
, lParam
);
1305 { /* Make the direct call. */
1306 TRACE("Local Hook calling to Thread! %d\n",HookId
);
1307 Result
= co_IntCallHookProc( HookId
,
1315 UserDerefObjectCo(Hook
);
1317 ExFreePoolWithTag(pHookHandles
, TAG_HOOK
);
1318 TRACE("Ret: Global HookId %d Result 0x%x\n", HookId
,Result
);
1326 IntUnhookWindowsHook(int HookId
, HOOKPROC pfnFilterProc
)
1329 PLIST_ENTRY pLastHead
, pElement
;
1330 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
1332 if (HookId
< WH_MINHOOK
|| WH_MAXHOOK
< HookId
)
1334 EngSetLastError(ERROR_INVALID_HOOK_FILTER
);
1340 pLastHead
= &pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1342 pElement
= pLastHead
->Flink
;
1343 while (pElement
!= pLastHead
)
1345 Hook
= CONTAINING_RECORD(pElement
, HOOK
, Chain
);
1347 if (Hook
->Proc
== pfnFilterProc
)
1349 if (Hook
->head
.pti
== pti
)
1351 IntRemoveHook(Hook
);
1352 UserDereferenceObject(Hook
);
1357 EngSetLastError(ERROR_ACCESS_DENIED
);
1362 pElement
= Hook
->Chain
.Flink
;
1369 * Support for compatibility only? Global hooks are processed in kernel space.
1370 * This is very thread specific! Never seeing applications with more than one
1371 * hook per thread installed. Most of the applications are Global hookers and
1372 * associated with just one hook Id. Maybe it's for diagnostic testing or a
1373 * throw back to 3.11?
1377 NtUserCallNextHookEx( int Code
,
1383 PHOOK HookObj
, NextObj
;
1384 PCLIENTINFO ClientInfo
;
1385 LRESULT lResult
= 0;
1386 DECLARE_RETURN(LRESULT
);
1388 TRACE("Enter NtUserCallNextHookEx\n");
1389 UserEnterExclusive();
1391 pti
= GetW32ThreadInfo();
1393 HookObj
= pti
->sphkCurrent
;
1395 if (!HookObj
) RETURN( 0);
1397 NextObj
= HookObj
->phkNext
;
1399 pti
->sphkCurrent
= NextObj
;
1400 ClientInfo
= pti
->pClientInfo
;
1403 ClientInfo
->phkCurrent
= NextObj
;
1405 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1411 /* Now in List run down. */
1412 if (ClientInfo
&& NextObj
)
1414 NextObj
->phkNext
= IntGetNextHook(NextObj
);
1415 lResult
= co_UserCallNextHookEx( NextObj
, Code
, wParam
, lParam
, NextObj
->Ansi
);
1420 TRACE("Leave NtUserCallNextHookEx, ret=%i\n",_ret_
);
1427 NtUserSetWindowsHookAW( int idHook
,
1432 UNICODE_STRING USModuleName
;
1434 RtlInitUnicodeString(&USModuleName
, NULL
);
1435 ThreadId
= PtrToUint(NtCurrentTeb()->ClientId
.UniqueThread
);
1437 return NtUserSetWindowsHookEx( NULL
,
1447 NtUserSetWindowsHookEx( HINSTANCE Mod
,
1448 PUNICODE_STRING UnsafeModuleName
,
1454 PWINSTATION_OBJECT WinStaObj
;
1456 UNICODE_STRING ModuleName
;
1459 PTHREADINFO pti
, ptiHook
= NULL
;
1460 DECLARE_RETURN(HHOOK
);
1462 TRACE("Enter NtUserSetWindowsHookEx\n");
1463 UserEnterExclusive();
1465 pti
= PsGetCurrentThreadWin32Thread();
1467 if (HookId
< WH_MINHOOK
|| WH_MAXHOOK
< HookId
)
1469 EngSetLastError(ERROR_INVALID_HOOK_FILTER
);
1475 EngSetLastError(ERROR_INVALID_FILTER_PROC
);
1479 if (ThreadId
) /* thread-local hook */
1481 if ( HookId
== WH_JOURNALRECORD
||
1482 HookId
== WH_JOURNALPLAYBACK
||
1483 HookId
== WH_KEYBOARD_LL
||
1484 HookId
== WH_MOUSE_LL
||
1485 HookId
== WH_SYSMSGFILTER
)
1487 ERR("Local hook installing Global HookId: %d\n",HookId
);
1488 /* these can only be global */
1489 EngSetLastError(ERROR_GLOBAL_ONLY_HOOK
);
1493 if ( !(ptiHook
= IntTID2PTI( (HANDLE
)ThreadId
)))
1495 ERR("Invalid thread id 0x%x\n", ThreadId
);
1496 EngSetLastError(ERROR_INVALID_PARAMETER
);
1500 if ( ptiHook
->rpdesk
!= pti
->rpdesk
) // gptiCurrent->rpdesk)
1502 ERR("Local hook wrong desktop HookId: %d\n",HookId
);
1503 EngSetLastError(ERROR_ACCESS_DENIED
);
1507 if (ptiHook
->ppi
!= pti
->ppi
)
1510 (HookId
== WH_GETMESSAGE
||
1511 HookId
== WH_CALLWNDPROC
||
1513 HookId
== WH_HARDWARE
||
1514 HookId
== WH_DEBUG
||
1515 HookId
== WH_SHELL
||
1516 HookId
== WH_FOREGROUNDIDLE
||
1517 HookId
== WH_CALLWNDPROCRET
) )
1519 ERR("Local hook needs hMod HookId: %d\n",HookId
);
1520 EngSetLastError(ERROR_HOOK_NEEDS_HMOD
);
1524 if ( (ptiHook
->TIF_flags
& (TIF_CSRSSTHREAD
|TIF_SYSTEMTHREAD
)) &&
1525 (HookId
== WH_GETMESSAGE
||
1526 HookId
== WH_CALLWNDPROC
||
1528 HookId
== WH_HARDWARE
||
1529 HookId
== WH_DEBUG
||
1530 HookId
== WH_SHELL
||
1531 HookId
== WH_FOREGROUNDIDLE
||
1532 HookId
== WH_CALLWNDPROCRET
) )
1534 EngSetLastError(ERROR_HOOK_TYPE_NOT_ALLOWED
);
1539 else /* System-global hook */
1541 ptiHook
= pti
; // gptiCurrent;
1543 (HookId
== WH_GETMESSAGE
||
1544 HookId
== WH_CALLWNDPROC
||
1546 HookId
== WH_SYSMSGFILTER
||
1547 HookId
== WH_HARDWARE
||
1548 HookId
== WH_DEBUG
||
1549 HookId
== WH_SHELL
||
1550 HookId
== WH_FOREGROUNDIDLE
||
1551 HookId
== WH_CALLWNDPROCRET
) )
1553 ERR("Global hook needs hMod HookId: %d\n",HookId
);
1554 EngSetLastError(ERROR_HOOK_NEEDS_HMOD
);
1559 Status
= IntValidateWindowStationHandle( PsGetCurrentProcess()->Win32WindowStation
,
1564 if (!NT_SUCCESS(Status
))
1566 SetLastNtError(Status
);
1569 ObDereferenceObject(WinStaObj
);
1571 Hook
= UserCreateObject(gHandleTable
, NULL
, NULL
, (PHANDLE
)&Handle
, TYPE_HOOK
, sizeof(HOOK
));
1578 Hook
->ihmod
= (INT
)Mod
; // Module Index from atom table, Do this for now.
1579 Hook
->HookId
= HookId
;
1580 Hook
->rpdesk
= ptiHook
->rpdesk
;
1581 Hook
->phkNext
= NULL
; /* Dont use as a chain! Use link lists for chaining. */
1582 Hook
->Proc
= HookProc
;
1585 TRACE("Set Hook Desk 0x%x DeskInfo 0x%x Handle Desk 0x%x\n",pti
->rpdesk
, pti
->pDeskInfo
,Hook
->head
.rpdesk
);
1587 if (ThreadId
) /* Thread-local hook */
1589 InsertHeadList(&ptiHook
->aphkStart
[HOOKID_TO_INDEX(HookId
)], &Hook
->Chain
);
1590 ptiHook
->sphkCurrent
= NULL
;
1591 Hook
->ptiHooked
= ptiHook
;
1592 ptiHook
->fsHooks
|= HOOKID_TO_FLAG(HookId
);
1594 if (ptiHook
->pClientInfo
)
1596 if ( ptiHook
->ppi
== pti
->ppi
) /* gptiCurrent->ppi) */
1600 ptiHook
->pClientInfo
->fsHooks
= ptiHook
->fsHooks
;
1601 ptiHook
->pClientInfo
->phkCurrent
= NULL
;
1603 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1605 ERR("Problem writing to Local ClientInfo!\n");
1611 KeAttachProcess(&ptiHook
->ppi
->peProcess
->Pcb
);
1614 ptiHook
->pClientInfo
->fsHooks
= ptiHook
->fsHooks
;
1615 ptiHook
->pClientInfo
->phkCurrent
= NULL
;
1617 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1619 ERR("Problem writing to Remote ClientInfo!\n");
1628 InsertHeadList(&ptiHook
->rpdesk
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)], &Hook
->Chain
);
1629 Hook
->ptiHooked
= NULL
;
1630 //gptiCurrent->pDeskInfo->fsHooks |= HOOKID_TO_FLAG(HookId);
1631 ptiHook
->rpdesk
->pDeskInfo
->fsHooks
|= HOOKID_TO_FLAG(HookId
);
1632 ptiHook
->sphkCurrent
= NULL
;
1633 ptiHook
->pClientInfo
->phkCurrent
= NULL
;
1636 RtlInitUnicodeString(&Hook
->ModuleName
, NULL
);
1640 Status
= MmCopyFromCaller(&ModuleName
,
1642 sizeof(UNICODE_STRING
));
1643 if (!NT_SUCCESS(Status
))
1645 IntRemoveHook(Hook
);
1646 SetLastNtError(Status
);
1650 Hook
->ModuleName
.Buffer
= ExAllocatePoolWithTag( PagedPool
,
1651 ModuleName
.MaximumLength
,
1653 if (NULL
== Hook
->ModuleName
.Buffer
)
1655 IntRemoveHook(Hook
);
1656 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1660 Hook
->ModuleName
.MaximumLength
= ModuleName
.MaximumLength
;
1661 Status
= MmCopyFromCaller( Hook
->ModuleName
.Buffer
,
1663 ModuleName
.MaximumLength
);
1664 if (!NT_SUCCESS(Status
))
1666 ExFreePoolWithTag(Hook
->ModuleName
.Buffer
, TAG_HOOK
);
1667 Hook
->ModuleName
.Buffer
= NULL
;
1668 IntRemoveHook(Hook
);
1669 SetLastNtError(Status
);
1673 Hook
->ModuleName
.Length
= ModuleName
.Length
;
1674 //// FIXME: Need to load from user32 to verify hMod before calling hook with hMod set!!!!
1675 //// Mod + offPfn == new HookProc Justin Case module is from another process.
1676 FIXME("NtUserSetWindowsHookEx Setting process hMod instance addressing.\n");
1677 /* Make proc relative to the module base */
1678 Hook
->offPfn
= (ULONG_PTR
)((char *)HookProc
- (char *)Mod
);
1683 TRACE("Installing: HookId %d Global %s\n", HookId
, !ThreadId
? "TRUE" : "FALSE");
1687 TRACE("Leave NtUserSetWindowsHookEx, ret=%i\n",_ret_
);
1694 NtUserUnhookWindowsHookEx(HHOOK Hook
)
1697 DECLARE_RETURN(BOOL
);
1699 TRACE("Enter NtUserUnhookWindowsHookEx\n");
1700 UserEnterExclusive();
1702 if (!(HookObj
= IntGetHookObject(Hook
)))
1704 ERR("Invalid handle passed to NtUserUnhookWindowsHookEx\n");
1705 /* SetLastNtError(Status); */
1709 ASSERT(Hook
== UserHMGetHandle(HookObj
));
1711 IntRemoveHook(HookObj
);
1713 UserDereferenceObject(HookObj
);
1718 TRACE("Leave NtUserUnhookWindowsHookEx, ret=%i\n",_ret_
);
1725 NtUserRegisterUserApiHook(
1726 PUNICODE_STRING m_dllname1
,
1727 PUNICODE_STRING m_funname1
,
1732 UNICODE_STRING strDllNameSafe
;
1733 UNICODE_STRING strFuncNameSafe
;
1736 /* Probe and capture parameters */
1737 Status
= ProbeAndCaptureUnicodeString(&strDllNameSafe
, UserMode
, m_dllname1
);
1738 if(!NT_SUCCESS(Status
))
1740 EngSetLastError(RtlNtStatusToDosError(Status
));
1744 Status
= ProbeAndCaptureUnicodeString(&strFuncNameSafe
, UserMode
, m_funname1
);
1745 if(!NT_SUCCESS(Status
))
1747 ReleaseCapturedUnicodeString(&strDllNameSafe
, UserMode
);
1748 EngSetLastError(RtlNtStatusToDosError(Status
));
1752 UserEnterExclusive();
1754 /* Call internal function */
1755 ret
= UserRegisterUserApiHook(&strDllNameSafe
, &strFuncNameSafe
);
1759 /* Cleanup only in case of failure */
1762 ReleaseCapturedUnicodeString(&strDllNameSafe
, UserMode
);
1763 ReleaseCapturedUnicodeString(&strFuncNameSafe
, UserMode
);
1771 NtUserUnregisterUserApiHook(VOID
)
1775 UserEnterExclusive();
1776 ret
= UserUnregisterUserApiHook();