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 hmod
= co_IntClientLoadLibrary(&strUahModule
, &strUahInitFunc
, Unload
, TRUE
);
53 TRACE("co_IntClientLoadLibrary returned %d\n", hmod
);
56 /* Remove the flag we set before */
57 ppi
->W32PF_flags
&= ~W32PF_APIHOOKLOADED
;
62 else if(Unload
&& (ppi
->W32PF_flags
& W32PF_APIHOOKLOADED
))
64 /* Call ClientLoadLibrary in user32 */
65 hmod
= co_IntClientLoadLibrary(NULL
, NULL
, Unload
, TRUE
);
68 ppi
->W32PF_flags
&= ~W32PF_APIHOOKLOADED
;
83 IntHookModuleUnloaded:
84 Sends a internal message to all threads of the requested desktop
85 and notifies them that a global hook was destroyed
86 and an injected module must be unloaded.
87 As a result, IntLoadHookModule will be called for all the threads that
88 will receive the special purpose internal message.
91 IntHookModuleUnloaded(PDESKTOP pdesk
, int iHookID
, HHOOK hHook
)
93 PTHREADINFO ptiCurrent
;
94 PLIST_ENTRY ListEntry
;
97 ERR("IntHookModuleUnloaded: iHookID=%d\n", iHookID
);
99 ppiCsr
= PsGetProcessWin32Process(CsrProcess
);
101 ListEntry
= pdesk
->PtiList
.Flink
;
102 while(ListEntry
!= &pdesk
->PtiList
)
104 ptiCurrent
= CONTAINING_RECORD(ListEntry
, THREADINFO
, PtiLink
);
106 /* FIXME: Do some more security checks here */
108 /* FIXME: The first check is a reactos specific hack for system threads */
109 if(!PsIsSystemProcess(ptiCurrent
->ppi
->peProcess
) &&
110 ptiCurrent
->ppi
!= ppiCsr
)
112 if(ptiCurrent
->ppi
->W32PF_flags
& W32PF_APIHOOKLOADED
)
114 TRACE("IntHookModuleUnloaded: sending message to PID %d, ppi=0x%x\n", PsGetProcessId(ptiCurrent
->ppi
->peProcess
), ptiCurrent
->ppi
);
115 co_MsqSendMessageAsync( ptiCurrent
,
126 ListEntry
= ListEntry
->Flink
;
136 return IntLoadHookModule(WH_APIHOOK
, 0, FALSE
);
141 UserRegisterUserApiHook(
142 PUNICODE_STRING pstrDllName
,
143 PUNICODE_STRING pstrFuncName
)
145 PTHREADINFO pti
, ptiCurrent
;
147 PWND DesktopWindow
, pwndCurrent
;
151 pti
= PsGetCurrentThreadWin32Thread();
152 ppiCsr
= PsGetProcessWin32Process(CsrProcess
);
154 /* Fail if the api hook is already registered */
155 if(gpsi
->dwSRVIFlags
& SRVINFO_APIHOOK
)
160 ERR("UserRegisterUserApiHook. Server PID: %d\n", PsGetProcessId(pti
->ppi
->peProcess
));
162 /* Register the api hook */
163 gpsi
->dwSRVIFlags
|= SRVINFO_APIHOOK
;
165 strUahModule
= *pstrDllName
;
166 strUahInitFunc
= *pstrFuncName
;
167 ppiUahServer
= pti
->ppi
;
169 /* Broadcast an internal message to every top level window */
170 DesktopWindow
= UserGetWindowObject(IntGetDesktopWindow());
171 List
= IntWinListChildren(DesktopWindow
);
175 for (i
= 0; List
[i
]; i
++)
177 pwndCurrent
= UserGetWindowObject(List
[i
]);
178 if(pwndCurrent
== NULL
)
182 ptiCurrent
= pwndCurrent
->head
.pti
;
184 /* FIXME: The first check is a reactos specific hack for system threads */
185 if(PsIsSystemProcess(ptiCurrent
->ppi
->peProcess
) ||
186 ptiCurrent
->ppi
== ppiCsr
)
191 co_MsqSendMessageAsync( ptiCurrent
,
194 FALSE
, /* Load the module */
201 ExFreePoolWithTag(List
, USERTAG_WINDOWLIST
);
209 UserUnregisterUserApiHook()
213 pti
= PsGetCurrentThreadWin32Thread();
215 /* Fail if the api hook is not registered */
216 if(!(gpsi
->dwSRVIFlags
& SRVINFO_APIHOOK
))
221 /* Only the process that registered the api hook can uregister it */
222 if(ppiUahServer
!= PsGetCurrentProcessWin32Process())
227 ERR("UserUnregisterUserApiHook. Server PID: %d\n", PsGetProcessId(pti
->ppi
->peProcess
));
229 /* Unregister the api hook */
230 gpsi
->dwSRVIFlags
&= ~SRVINFO_APIHOOK
;
232 ReleaseCapturedUnicodeString(&strUahModule
, UserMode
);
233 ReleaseCapturedUnicodeString(&strUahInitFunc
, UserMode
);
235 /* Notify all applications that the api hook module must be unloaded */
236 return IntHookModuleUnloaded(pti
->rpdesk
, WH_APIHOOK
, 0);
242 co_IntCallLowLevelHook(PHOOK Hook
,
253 ULONG_PTR uResult
= 0;
256 pti
= Hook
->Thread
->Tcb
.Win32Thread
;
258 pti
= Hook
->head
.pti
;
260 pHP
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(HOOKPACK
), TAG_HOOK
);
264 pHP
->lParam
= lParam
;
265 pHP
->pHookStructs
= NULL
;
267 // This prevents stack corruption from the caller.
270 case WH_JOURNALPLAYBACK
:
271 case WH_JOURNALRECORD
:
273 Size
= sizeof(EVENTMSG
);
276 Size
= sizeof(KBDLLHOOKSTRUCT
);
279 Size
= sizeof(MSLLHOOKSTRUCT
);
284 Size
= sizeof(MOUSEHOOKSTRUCT
);
294 pHP
->pHookStructs
= ExAllocatePoolWithTag(NonPagedPool
, Size
, TAG_HOOK
);
295 if (pHP
->pHookStructs
) RtlCopyMemory(pHP
->pHookStructs
, (PVOID
)lParam
, Size
);
298 /* FIXME: Should get timeout from
299 * HKEY_CURRENT_USER\Control Panel\Desktop\LowLevelHooksTimeout */
300 Status
= co_MsqSendMessage( pti
->MessageQueue
,
301 IntToPtr(Code
), // hWnd
309 if (!NT_SUCCESS(Status
))
311 ERR("Error Hook Call SendMsg. %d Status: 0x%x\n", Hook
->HookId
, Status
);
312 if (pHP
->pHookStructs
) ExFreePoolWithTag(pHP
->pHookStructs
, TAG_HOOK
);
313 ExFreePoolWithTag(pHP
, TAG_HOOK
);
315 return NT_SUCCESS(Status
) ? uResult
: 0;
320 // Dispatch MsgQueue Hook Call processor!
324 co_CallHook( INT HookId
,
331 PHOOKPACK pHP
= (PHOOKPACK
)lParam
;
334 lParam
= pHP
->lParam
;
338 case WH_JOURNALPLAYBACK
:
339 case WH_JOURNALRECORD
:
344 lParam
= (LPARAM
)pHP
->pHookStructs
;
348 /* The odds are high for this to be a Global call. */
349 Result
= co_IntCallHookProc( HookId
,
357 /* The odds so high, no one is waiting for the results. */
358 if (pHP
->pHookStructs
) ExFreePoolWithTag(pHP
->pHookStructs
, TAG_HOOK
);
359 ExFreePoolWithTag(pHP
, TAG_HOOK
);
366 co_HOOK_CallHookNext( PHOOK Hook
,
371 TRACE("Calling Next HOOK %d\n", Hook
->HookId
);
373 return co_IntCallHookProc( Hook
->HookId
,
385 co_IntCallDebugHook(PHOOK Hook
,
394 PVOID HooklParam
= NULL
;
401 ProbeForRead((PVOID
)lParam
,
402 sizeof(DEBUGHOOKINFO
),
405 RtlCopyMemory(&Debug
,
407 sizeof(DEBUGHOOKINFO
));
409 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
417 ERR("HOOK WH_DEBUG read from lParam ERROR!\n");
422 return lResult
; /* Need lParam! */
430 case HCBT_CLICKSKIPPED
:
431 Size
= sizeof(MOUSEHOOKSTRUCTEX
);
439 Size
= sizeof(CBTACTIVATESTRUCT
);
442 case HCBT_CREATEWND
: /* Handle ANSI? */
443 Size
= sizeof(CBT_CREATEWND
);
444 /* What shall we do? Size += sizeof(HOOKPROC_CBT_CREATEWND_EXTRA_ARGUMENTS); same as CREATESTRUCTEX */
448 Size
= sizeof(LPARAM
);
454 Size
= sizeof(MSLLHOOKSTRUCT
);
458 Size
= sizeof(KBDLLHOOKSTRUCT
);
462 case WH_SYSMSGFILTER
:
467 case WH_JOURNALPLAYBACK
:
468 case WH_JOURNALRECORD
:
469 Size
= sizeof(EVENTMSG
);
472 case WH_FOREGROUNDIDLE
:
476 Size
= sizeof(LPARAM
);
479 if (Size
> sizeof(LPARAM
))
480 HooklParam
= ExAllocatePoolWithTag(NonPagedPool
, Size
, TAG_HOOK
);
486 ProbeForRead((PVOID
)Debug
.lParam
,
490 RtlCopyMemory(HooklParam
,
494 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
502 ERR("HOOK WH_DEBUG read from Debug.lParam ERROR!\n");
503 ExFreePool(HooklParam
);
508 if (HooklParam
) Debug
.lParam
= (LPARAM
)HooklParam
;
509 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Debug
);
510 if (HooklParam
) ExFreePoolWithTag(HooklParam
, TAG_HOOK
);
518 co_UserCallNextHookEx(PHOOK Hook
,
527 /* Handle this one first. */
528 if ((Hook
->HookId
== WH_MOUSE
) ||
529 (Hook
->HookId
== WH_CBT
&& Code
== HCBT_CLICKSKIPPED
))
531 MOUSEHOOKSTRUCTEX Mouse
;
536 ProbeForRead((PVOID
)lParam
,
537 sizeof(MOUSEHOOKSTRUCTEX
),
540 RtlCopyMemory(&Mouse
,
542 sizeof(MOUSEHOOKSTRUCTEX
));
544 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
552 ERR("HOOK WH_MOUSE read from lParam ERROR!\n");
558 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Mouse
);
568 MSLLHOOKSTRUCT Mouse
;
574 ProbeForRead((PVOID
)lParam
,
575 sizeof(MSLLHOOKSTRUCT
),
578 RtlCopyMemory(&Mouse
,
580 sizeof(MSLLHOOKSTRUCT
));
582 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
590 ERR("HOOK WH_MOUSE_LL read from lParam ERROR!\n");
596 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Mouse
);
603 KBDLLHOOKSTRUCT Keyboard
;
609 ProbeForRead((PVOID
)lParam
,
610 sizeof(KBDLLHOOKSTRUCT
),
613 RtlCopyMemory(&Keyboard
,
615 sizeof(KBDLLHOOKSTRUCT
));
617 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
625 ERR("HOOK WH_KEYBORD_LL read from lParam ERROR!\n");
631 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Keyboard
);
637 case WH_SYSMSGFILTER
:
646 ProbeForRead((PVOID
)lParam
,
654 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
662 ERR("HOOK WH_XMESSAGEX read from lParam ERROR!\n");
668 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Msg
);
670 if (lParam
&& (Hook
->HookId
== WH_GETMESSAGE
))
674 ProbeForWrite((PVOID
)lParam
,
678 RtlCopyMemory((PVOID
)lParam
,
682 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
690 ERR("HOOK WH_GETMESSAGE write to lParam ERROR!\n");
698 TRACE("HOOK WH_CBT!\n");
703 LPCBT_CREATEWNDW pcbtcww
= (LPCBT_CREATEWNDW
)lParam
;
705 TRACE("HOOK HCBT_CREATEWND\n");
710 ProbeForRead( pcbtcww
,
711 sizeof(CBT_CREATEWNDA
),
713 ProbeForWrite(pcbtcww
->lpcs
,
714 sizeof(CREATESTRUCTA
),
716 ProbeForRead( pcbtcww
->lpcs
->lpszName
,
720 if (!IS_ATOM(pcbtcww
->lpcs
->lpszClass
))
722 ProbeForRead( pcbtcww
->lpcs
->lpszClass
,
729 ProbeForRead( pcbtcww
,
730 sizeof(CBT_CREATEWNDW
),
732 ProbeForWrite(pcbtcww
->lpcs
,
733 sizeof(CREATESTRUCTW
),
735 ProbeForRead( pcbtcww
->lpcs
->lpszName
,
739 if (!IS_ATOM(pcbtcww
->lpcs
->lpszClass
))
741 ProbeForRead( pcbtcww
->lpcs
->lpszClass
,
747 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
755 ERR("HOOK HCBT_CREATEWND write ERROR!\n");
757 /* The next call handles the structures. */
758 if (!BadChk
&& Hook
->Proc
)
760 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, lParam
);
769 TRACE("HOOK HCBT_MOVESIZE\n");
775 ProbeForRead((PVOID
)lParam
,
783 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
791 ERR("HOOK HCBT_MOVESIZE read from lParam ERROR!\n");
797 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&rt
);
804 CBTACTIVATESTRUCT CbAs
;
806 TRACE("HOOK HCBT_ACTIVATE\n");
811 ProbeForRead((PVOID
)lParam
,
812 sizeof(CBTACTIVATESTRUCT
),
817 sizeof(CBTACTIVATESTRUCT
));
819 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
827 ERR("HOOK HCBT_ACTIVATE read from lParam ERROR!\n");
833 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&CbAs
);
838 /* The rest just use default. */
840 TRACE("HOOK HCBT_ %d\n",Code
);
841 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, lParam
);
846 Note WH_JOURNALPLAYBACK,
847 "To have the system wait before processing the message, the return value
848 must be the amount of time, in clock ticks, that the system should wait."
850 case WH_JOURNALPLAYBACK
:
851 case WH_JOURNALRECORD
:
859 ProbeForRead((PVOID
)lParam
,
863 RtlCopyMemory(&EventMsg
,
867 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
875 ERR("HOOK WH_JOURNAL read from lParam ERROR!\n");
881 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)(lParam
? &EventMsg
: NULL
));
887 ProbeForWrite((PVOID
)lParam
,
891 RtlCopyMemory((PVOID
)lParam
,
895 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
903 ERR("HOOK WH_JOURNAL write to lParam ERROR!\n");
911 lResult
= co_IntCallDebugHook(Hook
, Code
, wParam
, lParam
, Ansi
);
915 * Default the rest like, WH_FOREGROUNDIDLE, WH_KEYBOARD and WH_SHELL.
917 case WH_FOREGROUNDIDLE
:
920 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, lParam
);
924 ERR("Unsupported HOOK Id -> %d\n",Hook
->HookId
);
932 IntGetHookObject(HHOOK hHook
)
938 EngSetLastError(ERROR_INVALID_HOOK_HANDLE
);
942 Hook
= (PHOOK
)UserGetObject(gHandleTable
, hHook
, otHook
);
945 EngSetLastError(ERROR_INVALID_HOOK_HANDLE
);
949 UserReferenceObject(Hook
);
957 IntGetGlobalHookHandles(PDESKTOP pdo
, int HookId
)
959 PLIST_ENTRY pLastHead
, pElem
;
965 pLastHead
= &pdo
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
966 for (pElem
= pLastHead
->Flink
; pElem
!= pLastHead
; pElem
= pElem
->Flink
)
969 pList
= ExAllocatePoolWithTag(PagedPool
, (cHooks
+ 1) * sizeof(HHOOK
), TAG_HOOK
);
972 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
976 for (pElem
= pLastHead
->Flink
; pElem
!= pLastHead
; pElem
= pElem
->Flink
)
978 pHook
= CONTAINING_RECORD(pElem
, HOOK
, Chain
);
979 pList
[i
++] = pHook
->head
.h
;
986 /* Find the next hook in the chain */
989 IntGetNextHook(PHOOK Hook
)
991 int HookId
= Hook
->HookId
;
992 PLIST_ENTRY pLastHead
, pElem
;
997 pti
= ((PTHREADINFO
)Hook
->Thread
->Tcb
.Win32Thread
);
998 pLastHead
= &pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1002 pti
= PsGetCurrentThreadWin32Thread();
1003 pLastHead
= &pti
->rpdesk
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1006 pElem
= Hook
->Chain
.Flink
;
1007 if (pElem
!= pLastHead
)
1008 return CONTAINING_RECORD(pElem
, HOOK
, Chain
);
1012 /* Free a hook, removing it from its chain */
1016 IntFreeHook(PHOOK Hook
)
1018 RemoveEntryList(&Hook
->Chain
);
1019 if (Hook
->ModuleName
.Buffer
)
1021 ExFreePoolWithTag(Hook
->ModuleName
.Buffer
, TAG_HOOK
);
1022 Hook
->ModuleName
.Buffer
= NULL
;
1025 UserDeleteObject(UserHMGetHandle(Hook
), otHook
);
1028 /* Remove a hook, freeing it from the chain */
1032 IntRemoveHook(PHOOK Hook
)
1038 HookId
= Hook
->HookId
;
1040 if (Hook
->Thread
) // Local
1042 pti
= ((PTHREADINFO
)Hook
->Thread
->Tcb
.Win32Thread
);
1046 if ( IsListEmpty(&pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)]) )
1048 pti
->fsHooks
&= ~HOOKID_TO_FLAG(HookId
);
1051 GetWin32ClientInfo()->fsHooks
= pti
->fsHooks
;
1053 _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
);
1110 // Global search based on Thread and cleanup.
1111 if (pdo
->pDeskInfo
->fsHooks
)
1113 for (HookId
= WH_MINHOOK
; HookId
<= WH_MAXHOOK
; HookId
++)
1115 PLIST_ENTRY pGLE
= &pdo
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1117 pElem
= pGLE
->Flink
;
1118 while (pElem
!= pGLE
)
1120 HookObj
= CONTAINING_RECORD(pElem
, HOOK
, Chain
);
1121 pElem
= HookObj
->Chain
.Flink
; // Get next element before hook is destroyed
1122 if (HookObj
->head
.pti
== pti
)
1124 IntRemoveHook(HookObj
);
1133 Win32k Kernel Space Hook Caller.
1137 co_HOOK_CallHooks( INT HookId
,
1142 PHOOK Hook
, SaveHook
;
1144 PCLIENTINFO ClientInfo
;
1145 PLIST_ENTRY pLastHead
;
1147 BOOL Local
= FALSE
, Global
= FALSE
;
1149 USER_REFERENCE_ENTRY Ref
;
1151 ASSERT(WH_MINHOOK
<= HookId
&& HookId
<= WH_MAXHOOK
);
1153 pti
= PsGetCurrentThreadWin32Thread();
1154 if (!pti
|| !pti
->rpdesk
|| !pti
->rpdesk
->pDeskInfo
)
1156 pdo
= IntGetActiveDesktop();
1157 /* If KeyboardThread|MouseThread|(RawInputThread or RIT) aka system threads,
1158 pti->fsHooks most likely, is zero. So process KbT & MsT to "send" the message.
1160 if ( !pti
|| !pdo
|| (!(HookId
== WH_KEYBOARD_LL
) && !(HookId
== WH_MOUSE_LL
)) )
1162 TRACE("No PDO %d\n", HookId
);
1171 if ( pti
->TIF_flags
& (TIF_INCLEANUP
|TIF_DISABLEHOOKS
))
1173 TRACE("Hook Thread dead %d\n", HookId
);
1177 if ( ISITHOOKED(HookId
) )
1179 TRACE("Local Hooker %d\n", HookId
);
1183 if ( pdo
->pDeskInfo
->fsHooks
& HOOKID_TO_FLAG(HookId
) )
1185 TRACE("Global Hooker %d\n", HookId
);
1189 if ( !Local
&& !Global
) goto Exit
; // No work!
1193 /* SetWindowHookEx sorts out the Thread issue by placing the Hook to
1194 the correct Thread if not NULL.
1198 pLastHead
= &pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1199 if (IsListEmpty(pLastHead
))
1201 ERR("No Local Hook Found!\n");
1205 Hook
= CONTAINING_RECORD(pLastHead
->Flink
, HOOK
, Chain
);
1206 UserRefObjectCo(Hook
, &Ref
);
1208 ClientInfo
= pti
->pClientInfo
;
1209 SaveHook
= pti
->sphkCurrent
;
1210 /* Note: Setting pti->sphkCurrent will also lock the next hook to this
1211 * hook ID. So, the CallNextHookEx will only call to that hook ID
1212 * chain anyway. For Thread Hooks....
1215 /* Load it for the next call. */
1216 pti
->sphkCurrent
= Hook
;
1217 Hook
->phkNext
= IntGetNextHook(Hook
);
1222 ClientInfo
->phkCurrent
= Hook
;
1224 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1226 ClientInfo
= NULL
; // Don't bother next run.
1230 Result
= co_IntCallHookProc( HookId
,
1241 ClientInfo
->phkCurrent
= SaveHook
;
1243 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1248 pti
->sphkCurrent
= SaveHook
;
1249 Hook
->phkNext
= NULL
;
1250 UserDerefObjectCo(Hook
);
1255 PTHREADINFO ptiHook
;
1256 HHOOK
*pHookHandles
;
1259 /* Keep hooks in array because hooks can be destroyed in user world */
1260 pHookHandles
= IntGetGlobalHookHandles(pdo
, HookId
);
1264 /* Performance goes down the drain. If more hooks are associated to this
1265 * hook ID, this will have to post to each of the thread message queues
1266 * or make a direct call.
1268 for(i
= 0; pHookHandles
[i
]; ++i
)
1270 Hook
= (PHOOK
)UserGetObject(gHandleTable
, pHookHandles
[i
], otHook
);
1273 ERR("Invalid hook!\n");
1276 UserRefObjectCo(Hook
, &Ref
);
1278 /* Hook->Thread is null, we hax around this with Hook->head.pti. */
1279 ptiHook
= Hook
->head
.pti
;
1281 if ( (pti
->TIF_flags
& TIF_DISABLEHOOKS
) || (ptiHook
->TIF_flags
& TIF_INCLEANUP
))
1283 TRACE("Next Hook 0x%x, 0x%x\n",ptiHook
->rpdesk
,pdo
);
1287 if (ptiHook
!= pti
)
1290 if ( HookId
== WH_JOURNALPLAYBACK
|| // 1 | 0
1291 HookId
== WH_JOURNALRECORD
|| // 1 | 0
1292 HookId
== WH_KEYBOARD
|| // 1 | 200
1293 HookId
== WH_MOUSE
|| // 1 | 200
1294 HookId
== WH_KEYBOARD_LL
|| // 0 | 300
1295 HookId
== WH_MOUSE_LL
) // 0 | 300
1297 TRACE("\nGlobal Hook posting to another Thread! %d\n",HookId
);
1298 Result
= co_IntCallLowLevelHook(Hook
, Code
, wParam
, lParam
);
1302 { /* Make the direct call. */
1303 TRACE("Local Hook calling to Thread! %d\n",HookId
);
1304 Result
= co_IntCallHookProc( HookId
,
1312 UserDerefObjectCo(Hook
);
1314 ExFreePoolWithTag(pHookHandles
, TAG_HOOK
);
1315 TRACE("Ret: Global HookId %d Result 0x%x\n", HookId
,Result
);
1323 IntUnhookWindowsHook(int HookId
, HOOKPROC pfnFilterProc
)
1326 PLIST_ENTRY pLastHead
, pElement
;
1327 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
1329 if (HookId
< WH_MINHOOK
|| WH_MAXHOOK
< HookId
)
1331 EngSetLastError(ERROR_INVALID_HOOK_FILTER
);
1337 pLastHead
= &pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1339 pElement
= pLastHead
->Flink
;
1340 while (pElement
!= pLastHead
)
1342 Hook
= CONTAINING_RECORD(pElement
, HOOK
, Chain
);
1344 if (Hook
->Proc
== pfnFilterProc
)
1346 if (Hook
->head
.pti
== pti
)
1348 IntRemoveHook(Hook
);
1349 UserDereferenceObject(Hook
);
1354 EngSetLastError(ERROR_ACCESS_DENIED
);
1359 pElement
= Hook
->Chain
.Flink
;
1366 * Support for compatibility only? Global hooks are processed in kernel space.
1367 * This is very thread specific! Never seeing applications with more than one
1368 * hook per thread installed. Most of the applications are Global hookers and
1369 * associated with just one hook Id. Maybe it's for diagnostic testing or a
1370 * throw back to 3.11?
1374 NtUserCallNextHookEx( int Code
,
1380 PHOOK HookObj
, NextObj
;
1381 PCLIENTINFO ClientInfo
;
1382 LRESULT lResult
= 0;
1383 DECLARE_RETURN(LRESULT
);
1385 TRACE("Enter NtUserCallNextHookEx\n");
1386 UserEnterExclusive();
1388 pti
= GetW32ThreadInfo();
1390 HookObj
= pti
->sphkCurrent
;
1392 if (!HookObj
) RETURN( 0);
1394 NextObj
= HookObj
->phkNext
;
1396 pti
->sphkCurrent
= NextObj
;
1397 ClientInfo
= pti
->pClientInfo
;
1400 ClientInfo
->phkCurrent
= NextObj
;
1402 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1408 /* Now in List run down. */
1409 if (ClientInfo
&& NextObj
)
1411 NextObj
->phkNext
= IntGetNextHook(NextObj
);
1412 lResult
= co_UserCallNextHookEx( NextObj
, Code
, wParam
, lParam
, NextObj
->Ansi
);
1417 TRACE("Leave NtUserCallNextHookEx, ret=%i\n",_ret_
);
1424 NtUserSetWindowsHookAW( int idHook
,
1429 UNICODE_STRING USModuleName
;
1431 RtlInitUnicodeString(&USModuleName
, NULL
);
1432 ThreadId
= PtrToUint(NtCurrentTeb()->ClientId
.UniqueThread
);
1434 return NtUserSetWindowsHookEx( NULL
,
1444 NtUserSetWindowsHookEx( HINSTANCE Mod
,
1445 PUNICODE_STRING UnsafeModuleName
,
1451 PWINSTATION_OBJECT WinStaObj
;
1453 UNICODE_STRING ModuleName
;
1456 PETHREAD Thread
= NULL
;
1457 PTHREADINFO pti
, ptiHook
= NULL
;
1458 DECLARE_RETURN(HHOOK
);
1460 TRACE("Enter NtUserSetWindowsHookEx\n");
1461 UserEnterExclusive();
1463 pti
= PsGetCurrentThreadWin32Thread();
1465 if (HookId
< WH_MINHOOK
|| WH_MAXHOOK
< HookId
)
1467 EngSetLastError(ERROR_INVALID_HOOK_FILTER
);
1473 EngSetLastError(ERROR_INVALID_FILTER_PROC
);
1477 if (ThreadId
) /* thread-local hook */
1479 if ( HookId
== WH_JOURNALRECORD
||
1480 HookId
== WH_JOURNALPLAYBACK
||
1481 HookId
== WH_KEYBOARD_LL
||
1482 HookId
== WH_MOUSE_LL
||
1483 HookId
== WH_SYSMSGFILTER
)
1485 ERR("Local hook installing Global HookId: %d\n",HookId
);
1486 /* these can only be global */
1487 EngSetLastError(ERROR_GLOBAL_ONLY_HOOK
);
1491 if (!NT_SUCCESS(PsLookupThreadByThreadId((HANDLE
)(DWORD_PTR
) ThreadId
, &Thread
)))
1493 ERR("Invalid thread id 0x%x\n", ThreadId
);
1494 EngSetLastError(ERROR_INVALID_PARAMETER
);
1498 ptiHook
= Thread
->Tcb
.Win32Thread
;
1500 ObDereferenceObject(Thread
);
1502 if ( ptiHook
->rpdesk
!= pti
->rpdesk
) // gptiCurrent->rpdesk)
1504 ERR("Local hook wrong desktop HookId: %d\n",HookId
);
1505 EngSetLastError(ERROR_ACCESS_DENIED
);
1509 if (Thread
->ThreadsProcess
!= PsGetCurrentProcess())
1512 (HookId
== WH_GETMESSAGE
||
1513 HookId
== WH_CALLWNDPROC
||
1515 HookId
== WH_HARDWARE
||
1516 HookId
== WH_DEBUG
||
1517 HookId
== WH_SHELL
||
1518 HookId
== WH_FOREGROUNDIDLE
||
1519 HookId
== WH_CALLWNDPROCRET
) )
1521 ERR("Local hook needs hMod HookId: %d\n",HookId
);
1522 EngSetLastError(ERROR_HOOK_NEEDS_HMOD
);
1526 if ( (ptiHook
->TIF_flags
& (TIF_CSRSSTHREAD
|TIF_SYSTEMTHREAD
)) &&
1527 (HookId
== WH_GETMESSAGE
||
1528 HookId
== WH_CALLWNDPROC
||
1530 HookId
== WH_HARDWARE
||
1531 HookId
== WH_DEBUG
||
1532 HookId
== WH_SHELL
||
1533 HookId
== WH_FOREGROUNDIDLE
||
1534 HookId
== WH_CALLWNDPROCRET
) )
1536 EngSetLastError(ERROR_HOOK_TYPE_NOT_ALLOWED
);
1541 else /* System-global hook */
1543 ptiHook
= pti
; // gptiCurrent;
1545 (HookId
== WH_GETMESSAGE
||
1546 HookId
== WH_CALLWNDPROC
||
1548 HookId
== WH_SYSMSGFILTER
||
1549 HookId
== WH_HARDWARE
||
1550 HookId
== WH_DEBUG
||
1551 HookId
== WH_SHELL
||
1552 HookId
== WH_FOREGROUNDIDLE
||
1553 HookId
== WH_CALLWNDPROCRET
) )
1555 ERR("Global hook needs hMod HookId: %d\n",HookId
);
1556 EngSetLastError(ERROR_HOOK_NEEDS_HMOD
);
1561 Status
= IntValidateWindowStationHandle( PsGetCurrentProcess()->Win32WindowStation
,
1566 if (!NT_SUCCESS(Status
))
1568 SetLastNtError(Status
);
1571 ObDereferenceObject(WinStaObj
);
1573 Hook
= UserCreateObject(gHandleTable
, NULL
, &Handle
, otHook
, sizeof(HOOK
));
1580 Hook
->ihmod
= (INT
)Mod
; // Module Index from atom table, Do this for now.
1581 Hook
->Thread
= Thread
; /* Set Thread, Null is Global. */
1582 Hook
->HookId
= HookId
;
1583 Hook
->rpdesk
= ptiHook
->rpdesk
;
1584 Hook
->phkNext
= NULL
; /* Dont use as a chain! Use link lists for chaining. */
1585 Hook
->Proc
= HookProc
;
1588 TRACE("Set Hook Desk 0x%x DeskInfo 0x%x Handle Desk 0x%x\n",pti
->rpdesk
, pti
->pDeskInfo
,Hook
->head
.rpdesk
);
1590 if (ThreadId
) /* Thread-local hook */
1592 InsertHeadList(&ptiHook
->aphkStart
[HOOKID_TO_INDEX(HookId
)], &Hook
->Chain
);
1593 ptiHook
->sphkCurrent
= NULL
;
1594 Hook
->ptiHooked
= ptiHook
;
1595 ptiHook
->fsHooks
|= HOOKID_TO_FLAG(HookId
);
1597 if (ptiHook
->pClientInfo
)
1599 if ( ptiHook
->ppi
== pti
->ppi
) /* gptiCurrent->ppi) */
1603 ptiHook
->pClientInfo
->fsHooks
= ptiHook
->fsHooks
;
1604 ptiHook
->pClientInfo
->phkCurrent
= NULL
;
1606 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1608 ERR("Problem writing to Local ClientInfo!\n");
1614 KeAttachProcess(&ptiHook
->ppi
->peProcess
->Pcb
);
1617 ptiHook
->pClientInfo
->fsHooks
= ptiHook
->fsHooks
;
1618 ptiHook
->pClientInfo
->phkCurrent
= NULL
;
1620 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1622 ERR("Problem writing to Remote ClientInfo!\n");
1631 InsertHeadList(&ptiHook
->rpdesk
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)], &Hook
->Chain
);
1632 Hook
->ptiHooked
= NULL
;
1633 //gptiCurrent->pDeskInfo->fsHooks |= HOOKID_TO_FLAG(HookId);
1634 ptiHook
->rpdesk
->pDeskInfo
->fsHooks
|= HOOKID_TO_FLAG(HookId
);
1635 ptiHook
->sphkCurrent
= NULL
;
1636 ptiHook
->pClientInfo
->phkCurrent
= NULL
;
1639 RtlInitUnicodeString(&Hook
->ModuleName
, NULL
);
1643 Status
= MmCopyFromCaller(&ModuleName
,
1645 sizeof(UNICODE_STRING
));
1646 if (!NT_SUCCESS(Status
))
1648 IntRemoveHook(Hook
);
1649 SetLastNtError(Status
);
1653 Hook
->ModuleName
.Buffer
= ExAllocatePoolWithTag( PagedPool
,
1654 ModuleName
.MaximumLength
,
1656 if (NULL
== Hook
->ModuleName
.Buffer
)
1658 IntRemoveHook(Hook
);
1659 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1663 Hook
->ModuleName
.MaximumLength
= ModuleName
.MaximumLength
;
1664 Status
= MmCopyFromCaller( Hook
->ModuleName
.Buffer
,
1666 ModuleName
.MaximumLength
);
1667 if (!NT_SUCCESS(Status
))
1669 ExFreePoolWithTag(Hook
->ModuleName
.Buffer
, TAG_HOOK
);
1670 Hook
->ModuleName
.Buffer
= NULL
;
1671 IntRemoveHook(Hook
);
1672 SetLastNtError(Status
);
1676 Hook
->ModuleName
.Length
= ModuleName
.Length
;
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();