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 TRACE("IntLoadHookModule. Client PID: %p\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 %p, ppi=%p\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 TRACE("UserRegisterUserApiHook. Server PID: %p\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: %p\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
,
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
,
387 co_IntCallDebugHook(PHOOK Hook
,
396 PVOID HooklParam
= NULL
;
403 ProbeForRead((PVOID
)lParam
,
404 sizeof(DEBUGHOOKINFO
),
407 RtlCopyMemory(&Debug
,
409 sizeof(DEBUGHOOKINFO
));
411 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
419 ERR("HOOK WH_DEBUG read from lParam ERROR!\n");
424 return lResult
; /* Need lParam! */
432 case HCBT_CLICKSKIPPED
:
433 Size
= sizeof(MOUSEHOOKSTRUCTEX
);
441 Size
= sizeof(CBTACTIVATESTRUCT
);
444 case HCBT_CREATEWND
: /* Handle ANSI? */
445 Size
= sizeof(CBT_CREATEWND
);
446 /* What shall we do? Size += sizeof(HOOKPROC_CBT_CREATEWND_EXTRA_ARGUMENTS); same as CREATESTRUCTEX */
450 Size
= sizeof(LPARAM
);
456 Size
= sizeof(MSLLHOOKSTRUCT
);
460 Size
= sizeof(KBDLLHOOKSTRUCT
);
464 case WH_SYSMSGFILTER
:
469 case WH_JOURNALPLAYBACK
:
470 case WH_JOURNALRECORD
:
471 Size
= sizeof(EVENTMSG
);
474 case WH_FOREGROUNDIDLE
:
478 Size
= sizeof(LPARAM
);
481 if (Size
> sizeof(LPARAM
))
482 HooklParam
= ExAllocatePoolWithTag(NonPagedPool
, Size
, TAG_HOOK
);
488 ProbeForRead((PVOID
)Debug
.lParam
,
492 RtlCopyMemory(HooklParam
,
496 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
504 ERR("HOOK WH_DEBUG read from Debug.lParam ERROR!\n");
505 ExFreePoolWithTag(HooklParam
, TAG_HOOK
);
510 if (HooklParam
) Debug
.lParam
= (LPARAM
)HooklParam
;
511 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Debug
);
512 if (HooklParam
) ExFreePoolWithTag(HooklParam
, TAG_HOOK
);
520 co_UserCallNextHookEx(PHOOK Hook
,
529 /* Handle this one first. */
530 if ((Hook
->HookId
== WH_MOUSE
) ||
531 (Hook
->HookId
== WH_CBT
&& Code
== HCBT_CLICKSKIPPED
))
533 MOUSEHOOKSTRUCTEX Mouse
;
538 ProbeForRead((PVOID
)lParam
,
539 sizeof(MOUSEHOOKSTRUCTEX
),
542 RtlCopyMemory(&Mouse
,
544 sizeof(MOUSEHOOKSTRUCTEX
));
546 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
554 ERR("HOOK WH_MOUSE read from lParam ERROR!\n");
560 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Mouse
);
570 MSLLHOOKSTRUCT Mouse
;
576 ProbeForRead((PVOID
)lParam
,
577 sizeof(MSLLHOOKSTRUCT
),
580 RtlCopyMemory(&Mouse
,
582 sizeof(MSLLHOOKSTRUCT
));
584 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
592 ERR("HOOK WH_MOUSE_LL read from lParam ERROR!\n");
598 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Mouse
);
605 KBDLLHOOKSTRUCT Keyboard
;
611 ProbeForRead((PVOID
)lParam
,
612 sizeof(KBDLLHOOKSTRUCT
),
615 RtlCopyMemory(&Keyboard
,
617 sizeof(KBDLLHOOKSTRUCT
));
619 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
627 ERR("HOOK WH_KEYBORD_LL read from lParam ERROR!\n");
633 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Keyboard
);
639 case WH_SYSMSGFILTER
:
648 ProbeForRead((PVOID
)lParam
,
656 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
664 ERR("HOOK WH_XMESSAGEX read from lParam ERROR!\n");
670 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Msg
);
672 if (lParam
&& (Hook
->HookId
== WH_GETMESSAGE
))
676 ProbeForWrite((PVOID
)lParam
,
680 RtlCopyMemory((PVOID
)lParam
,
684 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
692 ERR("HOOK WH_GETMESSAGE write to lParam ERROR!\n");
700 TRACE("HOOK WH_CBT!\n");
705 LPCBT_CREATEWNDW pcbtcww
= (LPCBT_CREATEWNDW
)lParam
;
707 TRACE("HOOK HCBT_CREATEWND\n");
712 ProbeForRead( pcbtcww
,
713 sizeof(CBT_CREATEWNDA
),
715 ProbeForWrite(pcbtcww
->lpcs
,
716 sizeof(CREATESTRUCTA
),
718 ProbeForRead( pcbtcww
->lpcs
->lpszName
,
722 if (!IS_ATOM(pcbtcww
->lpcs
->lpszClass
))
724 ProbeForRead( pcbtcww
->lpcs
->lpszClass
,
731 ProbeForRead( pcbtcww
,
732 sizeof(CBT_CREATEWNDW
),
734 ProbeForWrite(pcbtcww
->lpcs
,
735 sizeof(CREATESTRUCTW
),
737 ProbeForRead( pcbtcww
->lpcs
->lpszName
,
741 if (!IS_ATOM(pcbtcww
->lpcs
->lpszClass
))
743 ProbeForRead( pcbtcww
->lpcs
->lpszClass
,
749 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
757 ERR("HOOK HCBT_CREATEWND write ERROR!\n");
759 /* The next call handles the structures. */
760 if (!BadChk
&& Hook
->Proc
)
762 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, lParam
);
771 TRACE("HOOK HCBT_MOVESIZE\n");
777 ProbeForRead((PVOID
)lParam
,
785 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
793 ERR("HOOK HCBT_MOVESIZE read from lParam ERROR!\n");
799 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&rt
);
806 CBTACTIVATESTRUCT CbAs
;
808 TRACE("HOOK HCBT_ACTIVATE\n");
813 ProbeForRead((PVOID
)lParam
,
814 sizeof(CBTACTIVATESTRUCT
),
819 sizeof(CBTACTIVATESTRUCT
));
821 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
829 ERR("HOOK HCBT_ACTIVATE read from lParam ERROR!\n");
835 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&CbAs
);
840 /* The rest just use default. */
842 TRACE("HOOK HCBT_ %d\n",Code
);
843 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, lParam
);
848 Note WH_JOURNALPLAYBACK,
849 "To have the system wait before processing the message, the return value
850 must be the amount of time, in clock ticks, that the system should wait."
852 case WH_JOURNALPLAYBACK
:
853 case WH_JOURNALRECORD
:
861 ProbeForRead((PVOID
)lParam
,
865 RtlCopyMemory(&EventMsg
,
869 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
877 ERR("HOOK WH_JOURNAL read from lParam ERROR!\n");
883 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)(lParam
? &EventMsg
: NULL
));
889 ProbeForWrite((PVOID
)lParam
,
893 RtlCopyMemory((PVOID
)lParam
,
897 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
905 ERR("HOOK WH_JOURNAL write to lParam ERROR!\n");
913 lResult
= co_IntCallDebugHook(Hook
, Code
, wParam
, lParam
, Ansi
);
917 * Default the rest like, WH_FOREGROUNDIDLE, WH_KEYBOARD and WH_SHELL.
919 case WH_FOREGROUNDIDLE
:
922 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, lParam
);
926 ERR("Unsupported HOOK Id -> %d\n",Hook
->HookId
);
934 IntGetHookObject(HHOOK hHook
)
940 EngSetLastError(ERROR_INVALID_HOOK_HANDLE
);
944 Hook
= (PHOOK
)UserGetObject(gHandleTable
, hHook
, TYPE_HOOK
);
947 EngSetLastError(ERROR_INVALID_HOOK_HANDLE
);
951 UserReferenceObject(Hook
);
959 IntGetGlobalHookHandles(PDESKTOP pdo
, int HookId
)
961 PLIST_ENTRY pLastHead
, pElem
;
967 pLastHead
= &pdo
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
968 for (pElem
= pLastHead
->Flink
; pElem
!= pLastHead
; pElem
= pElem
->Flink
)
971 pList
= ExAllocatePoolWithTag(PagedPool
, (cHooks
+ 1) * sizeof(HHOOK
), TAG_HOOK
);
974 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
978 for (pElem
= pLastHead
->Flink
; pElem
!= pLastHead
; pElem
= pElem
->Flink
)
980 pHook
= CONTAINING_RECORD(pElem
, HOOK
, Chain
);
981 pList
[i
++] = pHook
->head
.h
;
988 /* Find the next hook in the chain */
991 IntGetNextHook(PHOOK Hook
)
993 int HookId
= Hook
->HookId
;
994 PLIST_ENTRY pLastHead
, pElem
;
999 pti
= Hook
->ptiHooked
;
1000 pLastHead
= &pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1004 pti
= PsGetCurrentThreadWin32Thread();
1005 pLastHead
= &pti
->rpdesk
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1008 pElem
= Hook
->Chain
.Flink
;
1009 if (pElem
!= pLastHead
)
1010 return CONTAINING_RECORD(pElem
, HOOK
, Chain
);
1014 /* Free a hook, removing it from its chain */
1018 IntFreeHook(PHOOK Hook
)
1020 RemoveEntryList(&Hook
->Chain
);
1021 if (Hook
->ModuleName
.Buffer
)
1023 ExFreePoolWithTag(Hook
->ModuleName
.Buffer
, TAG_HOOK
);
1024 Hook
->ModuleName
.Buffer
= NULL
;
1027 UserDeleteObject(UserHMGetHandle(Hook
), TYPE_HOOK
);
1030 /* Remove a hook, freeing it from the chain */
1034 IntRemoveHook(PHOOK Hook
)
1040 HookId
= Hook
->HookId
;
1042 if (Hook
->ptiHooked
) // Local
1044 pti
= Hook
->ptiHooked
;
1048 if ( IsListEmpty(&pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)]) )
1050 pti
->fsHooks
&= ~HOOKID_TO_FLAG(HookId
);
1053 pti
->pClientInfo
->fsHooks
= pti
->fsHooks
;
1055 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1067 pdo
= IntGetActiveDesktop();
1071 IsListEmpty(&pdo
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)]) )
1073 pdo
->pDeskInfo
->fsHooks
&= ~HOOKID_TO_FLAG(HookId
);
1080 HOOK_DestroyThreadHooks(PETHREAD Thread
)
1088 pti
= Thread
->Tcb
.Win32Thread
;
1089 pdo
= IntGetActiveDesktop();
1093 ERR("Kill Thread Hooks pti %p pdo %p\n", pti
, pdo
);
1097 // Local Thread cleanup.
1100 for (HookId
= WH_MINHOOK
; HookId
<= WH_MAXHOOK
; HookId
++)
1102 PLIST_ENTRY pLastHead
= &pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1104 pElem
= pLastHead
->Flink
;
1105 while (pElem
!= pLastHead
)
1107 HookObj
= CONTAINING_RECORD(pElem
, HOOK
, Chain
);
1108 pElem
= HookObj
->Chain
.Flink
; // get next element before hook is destroyed
1109 IntRemoveHook(HookObj
);
1113 pti
->pClientInfo
->fsHooks
= 0;
1115 // Global search based on Thread and cleanup.
1116 if (pdo
->pDeskInfo
->fsHooks
)
1118 for (HookId
= WH_MINHOOK
; HookId
<= WH_MAXHOOK
; HookId
++)
1120 PLIST_ENTRY pGLE
= &pdo
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1122 pElem
= pGLE
->Flink
;
1123 while (pElem
!= pGLE
)
1125 HookObj
= CONTAINING_RECORD(pElem
, HOOK
, Chain
);
1126 pElem
= HookObj
->Chain
.Flink
; // Get next element before hook is destroyed
1127 if (HookObj
->head
.pti
== pti
)
1129 IntRemoveHook(HookObj
);
1138 Win32k Kernel Space Hook Caller.
1142 co_HOOK_CallHooks( INT HookId
,
1147 PHOOK Hook
, SaveHook
;
1149 PCLIENTINFO ClientInfo
;
1150 PLIST_ENTRY pLastHead
;
1152 BOOL Local
= FALSE
, Global
= FALSE
;
1154 USER_REFERENCE_ENTRY Ref
;
1156 ASSERT(WH_MINHOOK
<= HookId
&& HookId
<= WH_MAXHOOK
);
1158 pti
= PsGetCurrentThreadWin32Thread();
1159 if (!pti
|| !pti
->rpdesk
|| !pti
->rpdesk
->pDeskInfo
)
1161 pdo
= IntGetActiveDesktop();
1162 /* If KeyboardThread|MouseThread|(RawInputThread or RIT) aka system threads,
1163 pti->fsHooks most likely, is zero. So process KbT & MsT to "send" the message.
1165 if ( !pti
|| !pdo
|| (!(HookId
== WH_KEYBOARD_LL
) && !(HookId
== WH_MOUSE_LL
)) )
1167 TRACE("No PDO %d\n", HookId
);
1176 if ( pti
->TIF_flags
& (TIF_INCLEANUP
|TIF_DISABLEHOOKS
))
1178 TRACE("Hook Thread dead %d\n", HookId
);
1182 if ( ISITHOOKED(HookId
) )
1184 TRACE("Local Hooker %d\n", HookId
);
1188 if ( pdo
->pDeskInfo
->fsHooks
& HOOKID_TO_FLAG(HookId
) )
1190 TRACE("Global Hooker %d\n", HookId
);
1194 if ( !Local
&& !Global
) goto Exit
; // No work!
1198 /* SetWindowHookEx sorts out the Thread issue by placing the Hook to
1199 the correct Thread if not NULL.
1203 pLastHead
= &pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1204 if (IsListEmpty(pLastHead
))
1206 ERR("No Local Hook Found!\n");
1210 Hook
= CONTAINING_RECORD(pLastHead
->Flink
, HOOK
, Chain
);
1211 UserRefObjectCo(Hook
, &Ref
);
1213 ClientInfo
= pti
->pClientInfo
;
1214 SaveHook
= pti
->sphkCurrent
;
1215 /* Note: Setting pti->sphkCurrent will also lock the next hook to this
1216 * hook ID. So, the CallNextHookEx will only call to that hook ID
1217 * chain anyway. For Thread Hooks....
1220 /* Load it for the next call. */
1221 pti
->sphkCurrent
= Hook
;
1222 Hook
->phkNext
= IntGetNextHook(Hook
);
1227 ClientInfo
->phkCurrent
= Hook
;
1229 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1231 ClientInfo
= NULL
; // Don't bother next run.
1235 Result
= co_IntCallHookProc( HookId
,
1248 ClientInfo
->phkCurrent
= SaveHook
;
1250 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1257 pti
->sphkCurrent
= SaveHook
;
1258 Hook
->phkNext
= NULL
;
1259 UserDerefObjectCo(Hook
);
1264 PTHREADINFO ptiHook
;
1265 HHOOK
*pHookHandles
;
1268 /* Keep hooks in array because hooks can be destroyed in user world */
1269 pHookHandles
= IntGetGlobalHookHandles(pdo
, HookId
);
1273 /* Performance goes down the drain. If more hooks are associated to this
1274 * hook ID, this will have to post to each of the thread message queues
1275 * or make a direct call.
1277 for(i
= 0; pHookHandles
[i
]; ++i
)
1279 Hook
= (PHOOK
)UserGetObject(gHandleTable
, pHookHandles
[i
], TYPE_HOOK
);
1282 ERR("Invalid hook!\n");
1285 UserRefObjectCo(Hook
, &Ref
);
1287 /* Hook->Thread is null, we hax around this with Hook->head.pti. */
1288 ptiHook
= Hook
->head
.pti
;
1290 if ( (pti
->TIF_flags
& TIF_DISABLEHOOKS
) || (ptiHook
->TIF_flags
& TIF_INCLEANUP
))
1292 TRACE("Next Hook %p, %p\n", ptiHook
->rpdesk
, pdo
);
1296 if (ptiHook
!= pti
)
1299 if ( HookId
== WH_JOURNALPLAYBACK
|| // 1 | 0
1300 HookId
== WH_JOURNALRECORD
|| // 1 | 0
1301 HookId
== WH_KEYBOARD
|| // 1 | 200
1302 HookId
== WH_MOUSE
|| // 1 | 200
1303 HookId
== WH_KEYBOARD_LL
|| // 0 | 300
1304 HookId
== WH_MOUSE_LL
) // 0 | 300
1306 TRACE("\nGlobal Hook posting to another Thread! %d\n",HookId
);
1307 Result
= co_IntCallLowLevelHook(Hook
, Code
, wParam
, lParam
);
1311 { /* Make the direct call. */
1312 TRACE("Local Hook calling to Thread! %d\n",HookId
);
1313 Result
= co_IntCallHookProc( HookId
,
1323 UserDerefObjectCo(Hook
);
1325 ExFreePoolWithTag(pHookHandles
, TAG_HOOK
);
1326 TRACE("Ret: Global HookId %d Result 0x%x\n", HookId
,Result
);
1334 IntUnhookWindowsHook(int HookId
, HOOKPROC pfnFilterProc
)
1337 PLIST_ENTRY pLastHead
, pElement
;
1338 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
1340 if (HookId
< WH_MINHOOK
|| WH_MAXHOOK
< HookId
)
1342 EngSetLastError(ERROR_INVALID_HOOK_FILTER
);
1348 pLastHead
= &pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1350 pElement
= pLastHead
->Flink
;
1351 while (pElement
!= pLastHead
)
1353 Hook
= CONTAINING_RECORD(pElement
, HOOK
, Chain
);
1355 if (Hook
->Proc
== pfnFilterProc
)
1357 if (Hook
->head
.pti
== pti
)
1359 IntRemoveHook(Hook
);
1360 UserDereferenceObject(Hook
);
1365 EngSetLastError(ERROR_ACCESS_DENIED
);
1370 pElement
= Hook
->Chain
.Flink
;
1377 * Support for compatibility only? Global hooks are processed in kernel space.
1378 * This is very thread specific! Never seeing applications with more than one
1379 * hook per thread installed. Most of the applications are Global hookers and
1380 * associated with just one hook Id. Maybe it's for diagnostic testing or a
1381 * throw back to 3.11?
1385 NtUserCallNextHookEx( int Code
,
1391 PHOOK HookObj
, NextObj
;
1392 PCLIENTINFO ClientInfo
;
1393 LRESULT lResult
= 0;
1394 DECLARE_RETURN(LRESULT
);
1396 TRACE("Enter NtUserCallNextHookEx\n");
1397 UserEnterExclusive();
1399 pti
= GetW32ThreadInfo();
1401 HookObj
= pti
->sphkCurrent
;
1403 if (!HookObj
) RETURN( 0);
1405 NextObj
= HookObj
->phkNext
;
1407 pti
->sphkCurrent
= NextObj
;
1408 ClientInfo
= pti
->pClientInfo
;
1411 ClientInfo
->phkCurrent
= NextObj
;
1413 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1419 /* Now in List run down. */
1420 if (ClientInfo
&& NextObj
)
1422 NextObj
->phkNext
= IntGetNextHook(NextObj
);
1423 lResult
= co_UserCallNextHookEx( NextObj
, Code
, wParam
, lParam
, NextObj
->Ansi
);
1428 TRACE("Leave NtUserCallNextHookEx, ret=%i\n",_ret_
);
1435 NtUserSetWindowsHookAW( int idHook
,
1440 UNICODE_STRING USModuleName
;
1442 RtlInitUnicodeString(&USModuleName
, NULL
);
1443 ThreadId
= PtrToUint(NtCurrentTeb()->ClientId
.UniqueThread
);
1445 return NtUserSetWindowsHookEx( NULL
,
1455 NtUserSetWindowsHookEx( HINSTANCE Mod
,
1456 PUNICODE_STRING UnsafeModuleName
,
1462 PWINSTATION_OBJECT WinStaObj
;
1464 UNICODE_STRING ModuleName
;
1467 PTHREADINFO pti
, ptiHook
= NULL
;
1468 DECLARE_RETURN(HHOOK
);
1470 TRACE("Enter NtUserSetWindowsHookEx\n");
1471 UserEnterExclusive();
1473 pti
= PsGetCurrentThreadWin32Thread();
1475 if (HookId
< WH_MINHOOK
|| WH_MAXHOOK
< HookId
)
1477 EngSetLastError(ERROR_INVALID_HOOK_FILTER
);
1483 EngSetLastError(ERROR_INVALID_FILTER_PROC
);
1487 if (ThreadId
) /* thread-local hook */
1489 if ( HookId
== WH_JOURNALRECORD
||
1490 HookId
== WH_JOURNALPLAYBACK
||
1491 HookId
== WH_KEYBOARD_LL
||
1492 HookId
== WH_MOUSE_LL
||
1493 HookId
== WH_SYSMSGFILTER
)
1495 ERR("Local hook installing Global HookId: %d\n",HookId
);
1496 /* these can only be global */
1497 EngSetLastError(ERROR_GLOBAL_ONLY_HOOK
);
1501 if ( !(ptiHook
= IntTID2PTI( (HANDLE
)ThreadId
)))
1503 ERR("Invalid thread id 0x%x\n", ThreadId
);
1504 EngSetLastError(ERROR_INVALID_PARAMETER
);
1508 if ( ptiHook
->rpdesk
!= pti
->rpdesk
) // gptiCurrent->rpdesk)
1510 ERR("Local hook wrong desktop HookId: %d\n",HookId
);
1511 EngSetLastError(ERROR_ACCESS_DENIED
);
1515 if (ptiHook
->ppi
!= pti
->ppi
)
1518 (HookId
== WH_GETMESSAGE
||
1519 HookId
== WH_CALLWNDPROC
||
1521 HookId
== WH_HARDWARE
||
1522 HookId
== WH_DEBUG
||
1523 HookId
== WH_SHELL
||
1524 HookId
== WH_FOREGROUNDIDLE
||
1525 HookId
== WH_CALLWNDPROCRET
) )
1527 ERR("Local hook needs hMod HookId: %d\n",HookId
);
1528 EngSetLastError(ERROR_HOOK_NEEDS_HMOD
);
1532 if ( (ptiHook
->TIF_flags
& (TIF_CSRSSTHREAD
|TIF_SYSTEMTHREAD
)) &&
1533 (HookId
== WH_GETMESSAGE
||
1534 HookId
== WH_CALLWNDPROC
||
1536 HookId
== WH_HARDWARE
||
1537 HookId
== WH_DEBUG
||
1538 HookId
== WH_SHELL
||
1539 HookId
== WH_FOREGROUNDIDLE
||
1540 HookId
== WH_CALLWNDPROCRET
) )
1542 EngSetLastError(ERROR_HOOK_TYPE_NOT_ALLOWED
);
1547 else /* System-global hook */
1549 ptiHook
= pti
; // gptiCurrent;
1551 (HookId
== WH_GETMESSAGE
||
1552 HookId
== WH_CALLWNDPROC
||
1554 HookId
== WH_SYSMSGFILTER
||
1555 HookId
== WH_HARDWARE
||
1556 HookId
== WH_DEBUG
||
1557 HookId
== WH_SHELL
||
1558 HookId
== WH_FOREGROUNDIDLE
||
1559 HookId
== WH_CALLWNDPROCRET
) )
1561 ERR("Global hook needs hMod HookId: %d\n",HookId
);
1562 EngSetLastError(ERROR_HOOK_NEEDS_HMOD
);
1567 Status
= IntValidateWindowStationHandle( PsGetCurrentProcess()->Win32WindowStation
,
1572 if (!NT_SUCCESS(Status
))
1574 SetLastNtError(Status
);
1577 ObDereferenceObject(WinStaObj
);
1579 Hook
= UserCreateObject(gHandleTable
, NULL
, NULL
, (PHANDLE
)&Handle
, TYPE_HOOK
, sizeof(HOOK
));
1586 Hook
->ihmod
= (INT
)Mod
; // Module Index from atom table, Do this for now.
1587 Hook
->HookId
= HookId
;
1588 Hook
->rpdesk
= ptiHook
->rpdesk
;
1589 Hook
->phkNext
= NULL
; /* Dont use as a chain! Use link lists for chaining. */
1590 Hook
->Proc
= HookProc
;
1593 TRACE("Set Hook Desk %p DeskInfo %p Handle Desk %p\n", pti
->rpdesk
, pti
->pDeskInfo
, Hook
->head
.rpdesk
);
1595 if (ThreadId
) /* Thread-local hook */
1597 InsertHeadList(&ptiHook
->aphkStart
[HOOKID_TO_INDEX(HookId
)], &Hook
->Chain
);
1598 ptiHook
->sphkCurrent
= NULL
;
1599 Hook
->ptiHooked
= ptiHook
;
1600 ptiHook
->fsHooks
|= HOOKID_TO_FLAG(HookId
);
1602 if (ptiHook
->pClientInfo
)
1604 if ( ptiHook
->ppi
== pti
->ppi
) /* gptiCurrent->ppi) */
1608 ptiHook
->pClientInfo
->fsHooks
= ptiHook
->fsHooks
;
1609 ptiHook
->pClientInfo
->phkCurrent
= NULL
;
1611 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1613 ERR("Problem writing to Local ClientInfo!\n");
1619 KeAttachProcess(&ptiHook
->ppi
->peProcess
->Pcb
);
1622 ptiHook
->pClientInfo
->fsHooks
= ptiHook
->fsHooks
;
1623 ptiHook
->pClientInfo
->phkCurrent
= NULL
;
1625 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1627 ERR("Problem writing to Remote ClientInfo!\n");
1636 InsertHeadList(&ptiHook
->rpdesk
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)], &Hook
->Chain
);
1637 Hook
->ptiHooked
= NULL
;
1638 //gptiCurrent->pDeskInfo->fsHooks |= HOOKID_TO_FLAG(HookId);
1639 ptiHook
->rpdesk
->pDeskInfo
->fsHooks
|= HOOKID_TO_FLAG(HookId
);
1640 ptiHook
->sphkCurrent
= NULL
;
1641 ptiHook
->pClientInfo
->phkCurrent
= NULL
;
1644 RtlInitUnicodeString(&Hook
->ModuleName
, NULL
);
1648 Status
= MmCopyFromCaller(&ModuleName
,
1650 sizeof(UNICODE_STRING
));
1651 if (!NT_SUCCESS(Status
))
1653 IntRemoveHook(Hook
);
1654 SetLastNtError(Status
);
1658 Hook
->ModuleName
.Buffer
= ExAllocatePoolWithTag( PagedPool
,
1659 ModuleName
.MaximumLength
,
1661 if (NULL
== Hook
->ModuleName
.Buffer
)
1663 IntRemoveHook(Hook
);
1664 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1668 Hook
->ModuleName
.MaximumLength
= ModuleName
.MaximumLength
;
1669 Status
= MmCopyFromCaller( Hook
->ModuleName
.Buffer
,
1671 ModuleName
.MaximumLength
);
1672 if (!NT_SUCCESS(Status
))
1674 ExFreePoolWithTag(Hook
->ModuleName
.Buffer
, TAG_HOOK
);
1675 Hook
->ModuleName
.Buffer
= NULL
;
1676 IntRemoveHook(Hook
);
1677 SetLastNtError(Status
);
1681 Hook
->ModuleName
.Length
= ModuleName
.Length
;
1682 //// FIXME: Need to load from user32 to verify hMod before calling hook with hMod set!!!!
1683 //// Mod + offPfn == new HookProc Justin Case module is from another process.
1684 FIXME("NtUserSetWindowsHookEx Setting process hMod instance addressing.\n");
1685 /* Make proc relative to the module base */
1686 Hook
->offPfn
= (ULONG_PTR
)((char *)HookProc
- (char *)Mod
);
1691 TRACE("Installing: HookId %d Global %s\n", HookId
, !ThreadId
? "TRUE" : "FALSE");
1695 TRACE("Leave NtUserSetWindowsHookEx, ret=%p\n", _ret_
);
1702 NtUserUnhookWindowsHookEx(HHOOK Hook
)
1705 DECLARE_RETURN(BOOL
);
1707 TRACE("Enter NtUserUnhookWindowsHookEx\n");
1708 UserEnterExclusive();
1710 if (!(HookObj
= IntGetHookObject(Hook
)))
1712 ERR("Invalid handle passed to NtUserUnhookWindowsHookEx\n");
1713 /* SetLastNtError(Status); */
1717 ASSERT(Hook
== UserHMGetHandle(HookObj
));
1719 IntRemoveHook(HookObj
);
1721 UserDereferenceObject(HookObj
);
1726 TRACE("Leave NtUserUnhookWindowsHookEx, ret=%i\n",_ret_
);
1733 NtUserRegisterUserApiHook(
1734 PUNICODE_STRING m_dllname1
,
1735 PUNICODE_STRING m_funname1
,
1740 UNICODE_STRING strDllNameSafe
;
1741 UNICODE_STRING strFuncNameSafe
;
1744 /* Probe and capture parameters */
1745 Status
= ProbeAndCaptureUnicodeString(&strDllNameSafe
, UserMode
, m_dllname1
);
1746 if(!NT_SUCCESS(Status
))
1748 EngSetLastError(RtlNtStatusToDosError(Status
));
1752 Status
= ProbeAndCaptureUnicodeString(&strFuncNameSafe
, UserMode
, m_funname1
);
1753 if(!NT_SUCCESS(Status
))
1755 ReleaseCapturedUnicodeString(&strDllNameSafe
, UserMode
);
1756 EngSetLastError(RtlNtStatusToDosError(Status
));
1760 UserEnterExclusive();
1762 /* Call internal function */
1763 ret
= UserRegisterUserApiHook(&strDllNameSafe
, &strFuncNameSafe
);
1767 /* Cleanup only in case of failure */
1770 ReleaseCapturedUnicodeString(&strDllNameSafe
, UserMode
);
1771 ReleaseCapturedUnicodeString(&strFuncNameSafe
, UserMode
);
1779 NtUserUnregisterUserApiHook(VOID
)
1783 UserEnterExclusive();
1784 ret
= UserUnregisterUserApiHook();