2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Window hooks
5 * FILE: win32ss/user/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 TRACE("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: HACK: 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 TRACE("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
:
341 lParam
= (LPARAM
)pHP
->pHookStructs
;
346 if (!UserObjectInDestroy(UserHMGetHandle(phk
))) //// Fix CORE-10549.
348 /* The odds are high for this to be a Global call. */
349 Result
= co_IntCallHookProc( HookId
,
359 /* The odds so high, no one is waiting for the results. */
360 if (pHP
->pHookStructs
) ExFreePoolWithTag(pHP
->pHookStructs
, TAG_HOOK
);
361 ExFreePoolWithTag(pHP
, TAG_HOOK
);
368 co_HOOK_CallHookNext( PHOOK Hook
,
373 TRACE("Calling Next HOOK %d\n", Hook
->HookId
);
375 return co_IntCallHookProc( Hook
->HookId
,
389 co_IntCallDebugHook(PHOOK Hook
,
398 PVOID HooklParam
= NULL
;
405 ProbeForRead((PVOID
)lParam
,
406 sizeof(DEBUGHOOKINFO
),
409 RtlCopyMemory(&Debug
,
411 sizeof(DEBUGHOOKINFO
));
413 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
421 ERR("HOOK WH_DEBUG read from lParam ERROR!\n");
426 return lResult
; /* Need lParam! */
434 case HCBT_CLICKSKIPPED
:
435 Size
= sizeof(MOUSEHOOKSTRUCTEX
);
443 Size
= sizeof(CBTACTIVATESTRUCT
);
446 case HCBT_CREATEWND
: /* Handle ANSI? */
447 Size
= sizeof(CBT_CREATEWND
);
448 /* What shall we do? Size += sizeof(HOOKPROC_CBT_CREATEWND_EXTRA_ARGUMENTS); same as CREATESTRUCTEX */
452 Size
= sizeof(LPARAM
);
458 Size
= sizeof(MSLLHOOKSTRUCT
);
462 Size
= sizeof(KBDLLHOOKSTRUCT
);
466 case WH_SYSMSGFILTER
:
471 case WH_JOURNALPLAYBACK
:
472 case WH_JOURNALRECORD
:
473 Size
= sizeof(EVENTMSG
);
476 case WH_FOREGROUNDIDLE
:
480 Size
= sizeof(LPARAM
);
483 if (Size
> sizeof(LPARAM
))
484 HooklParam
= ExAllocatePoolWithTag(NonPagedPool
, Size
, TAG_HOOK
);
490 ProbeForRead((PVOID
)Debug
.lParam
,
494 RtlCopyMemory(HooklParam
,
498 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
506 ERR("HOOK WH_DEBUG read from Debug.lParam ERROR!\n");
507 ExFreePoolWithTag(HooklParam
, TAG_HOOK
);
512 if (HooklParam
) Debug
.lParam
= (LPARAM
)HooklParam
;
513 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Debug
);
514 if (HooklParam
) ExFreePoolWithTag(HooklParam
, TAG_HOOK
);
522 co_UserCallNextHookEx(PHOOK Hook
,
531 /* Handle this one first. */
532 if ((Hook
->HookId
== WH_MOUSE
) ||
533 (Hook
->HookId
== WH_CBT
&& Code
== HCBT_CLICKSKIPPED
))
535 MOUSEHOOKSTRUCTEX Mouse
;
540 ProbeForRead((PVOID
)lParam
,
541 sizeof(MOUSEHOOKSTRUCTEX
),
544 RtlCopyMemory(&Mouse
,
546 sizeof(MOUSEHOOKSTRUCTEX
));
548 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
556 ERR("HOOK WH_MOUSE read from lParam ERROR!\n");
562 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Mouse
);
572 MSLLHOOKSTRUCT Mouse
;
578 ProbeForRead((PVOID
)lParam
,
579 sizeof(MSLLHOOKSTRUCT
),
582 RtlCopyMemory(&Mouse
,
584 sizeof(MSLLHOOKSTRUCT
));
586 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
594 ERR("HOOK WH_MOUSE_LL read from lParam ERROR!\n");
600 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Mouse
);
607 KBDLLHOOKSTRUCT Keyboard
;
613 ProbeForRead((PVOID
)lParam
,
614 sizeof(KBDLLHOOKSTRUCT
),
617 RtlCopyMemory(&Keyboard
,
619 sizeof(KBDLLHOOKSTRUCT
));
621 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
629 ERR("HOOK WH_KEYBORD_LL read from lParam ERROR!\n");
635 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Keyboard
);
641 case WH_SYSMSGFILTER
:
650 ProbeForRead((PVOID
)lParam
,
658 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
666 ERR("HOOK WH_XMESSAGEX read from lParam ERROR!\n");
672 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Msg
);
674 if (lParam
&& (Hook
->HookId
== WH_GETMESSAGE
))
678 ProbeForWrite((PVOID
)lParam
,
682 RtlCopyMemory((PVOID
)lParam
,
686 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
694 ERR("HOOK WH_GETMESSAGE write to lParam ERROR!\n");
702 TRACE("HOOK WH_CBT!\n");
707 LPCBT_CREATEWNDW pcbtcww
= (LPCBT_CREATEWNDW
)lParam
;
709 TRACE("HOOK HCBT_CREATEWND\n");
714 ProbeForRead( pcbtcww
,
715 sizeof(CBT_CREATEWNDA
),
717 ProbeForWrite(pcbtcww
->lpcs
,
718 sizeof(CREATESTRUCTA
),
720 ProbeForRead( pcbtcww
->lpcs
->lpszName
,
724 if (!IS_ATOM(pcbtcww
->lpcs
->lpszClass
))
726 _Analysis_assume_(pcbtcww
->lpcs
->lpszClass
!= NULL
);
727 ProbeForRead(pcbtcww
->lpcs
->lpszClass
,
734 ProbeForRead( pcbtcww
,
735 sizeof(CBT_CREATEWNDW
),
737 ProbeForWrite(pcbtcww
->lpcs
,
738 sizeof(CREATESTRUCTW
),
740 ProbeForRead( pcbtcww
->lpcs
->lpszName
,
744 if (!IS_ATOM(pcbtcww
->lpcs
->lpszClass
))
746 _Analysis_assume_(pcbtcww
->lpcs
->lpszClass
!= NULL
);
747 ProbeForRead(pcbtcww
->lpcs
->lpszClass
,
753 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
761 ERR("HOOK HCBT_CREATEWND write ERROR!\n");
763 /* The next call handles the structures. */
764 if (!BadChk
&& Hook
->Proc
)
766 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, lParam
);
775 TRACE("HOOK HCBT_MOVESIZE\n");
781 ProbeForRead((PVOID
)lParam
,
789 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
797 ERR("HOOK HCBT_MOVESIZE read from lParam ERROR!\n");
803 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&rt
);
810 CBTACTIVATESTRUCT CbAs
;
812 TRACE("HOOK HCBT_ACTIVATE\n");
817 ProbeForRead((PVOID
)lParam
,
818 sizeof(CBTACTIVATESTRUCT
),
823 sizeof(CBTACTIVATESTRUCT
));
825 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
833 ERR("HOOK HCBT_ACTIVATE read from lParam ERROR!\n");
839 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&CbAs
);
844 /* The rest just use default. */
846 TRACE("HOOK HCBT_ %d\n",Code
);
847 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, lParam
);
852 Note WH_JOURNALPLAYBACK,
853 "To have the system wait before processing the message, the return value
854 must be the amount of time, in clock ticks, that the system should wait."
856 case WH_JOURNALPLAYBACK
:
857 case WH_JOURNALRECORD
:
865 ProbeForRead((PVOID
)lParam
,
869 RtlCopyMemory(&EventMsg
,
873 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
881 ERR("HOOK WH_JOURNAL read from lParam ERROR!\n");
887 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)(lParam
? &EventMsg
: NULL
));
893 ProbeForWrite((PVOID
)lParam
,
897 RtlCopyMemory((PVOID
)lParam
,
901 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
909 ERR("HOOK WH_JOURNAL write to lParam ERROR!\n");
917 lResult
= co_IntCallDebugHook(Hook
, Code
, wParam
, lParam
, Ansi
);
921 * Default the rest like, WH_FOREGROUNDIDLE, WH_KEYBOARD and WH_SHELL.
923 case WH_FOREGROUNDIDLE
:
926 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, lParam
);
930 ERR("Unsupported HOOK Id -> %d\n",Hook
->HookId
);
938 IntGetHookObject(HHOOK hHook
)
944 EngSetLastError(ERROR_INVALID_HOOK_HANDLE
);
948 Hook
= (PHOOK
)UserGetObject(gHandleTable
, hHook
, TYPE_HOOK
);
951 EngSetLastError(ERROR_INVALID_HOOK_HANDLE
);
955 UserReferenceObject(Hook
);
963 IntGetGlobalHookHandles(PDESKTOP pdo
, int HookId
)
965 PLIST_ENTRY pLastHead
, pElem
;
971 pLastHead
= &pdo
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
972 for (pElem
= pLastHead
->Flink
; pElem
!= pLastHead
; pElem
= pElem
->Flink
)
975 pList
= ExAllocatePoolWithTag(PagedPool
, (cHooks
+ 1) * sizeof(HHOOK
), TAG_HOOK
);
978 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
982 for (pElem
= pLastHead
->Flink
; pElem
!= pLastHead
; pElem
= pElem
->Flink
)
984 pHook
= CONTAINING_RECORD(pElem
, HOOK
, Chain
);
985 NT_ASSERT(i
< cHooks
);
986 pList
[i
++] = pHook
->head
.h
;
993 /* Find the next hook in the chain */
996 IntGetNextHook(PHOOK Hook
)
998 int HookId
= Hook
->HookId
;
999 PLIST_ENTRY pLastHead
, pElem
;
1002 if (Hook
->ptiHooked
)
1004 pti
= Hook
->ptiHooked
;
1005 pLastHead
= &pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1009 pti
= PsGetCurrentThreadWin32Thread();
1010 pLastHead
= &pti
->rpdesk
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1013 pElem
= Hook
->Chain
.Flink
;
1014 if (pElem
!= pLastHead
)
1015 return CONTAINING_RECORD(pElem
, HOOK
, Chain
);
1019 /* Free a hook, removing it from its chain */
1023 IntFreeHook(PHOOK Hook
)
1025 RemoveEntryList(&Hook
->Chain
);
1026 if (Hook
->ModuleName
.Buffer
)
1028 ExFreePoolWithTag(Hook
->ModuleName
.Buffer
, TAG_HOOK
);
1029 Hook
->ModuleName
.Buffer
= NULL
;
1032 UserDeleteObject(UserHMGetHandle(Hook
), TYPE_HOOK
);
1035 /* Remove a hook, freeing it from the chain */
1037 IntRemoveHook(PVOID Object
)
1042 PHOOK Hook
= Object
;
1044 HookId
= Hook
->HookId
;
1046 if (Hook
->ptiHooked
) // Local
1048 pti
= Hook
->ptiHooked
;
1052 if ( IsListEmpty(&pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)]) )
1054 pti
->fsHooks
&= ~HOOKID_TO_FLAG(HookId
);
1057 pti
->pClientInfo
->fsHooks
= pti
->fsHooks
;
1059 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1071 pdo
= IntGetActiveDesktop();
1075 IsListEmpty(&pdo
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)]) )
1077 pdo
->pDeskInfo
->fsHooks
&= ~HOOKID_TO_FLAG(HookId
);
1085 Win32k Kernel Space Hook Caller.
1089 co_HOOK_CallHooks( INT HookId
,
1094 PHOOK Hook
, SaveHook
;
1096 PCLIENTINFO ClientInfo
;
1097 PLIST_ENTRY pLastHead
;
1099 BOOL Local
= FALSE
, Global
= FALSE
;
1101 USER_REFERENCE_ENTRY Ref
;
1103 ASSERT(WH_MINHOOK
<= HookId
&& HookId
<= WH_MAXHOOK
);
1105 pti
= PsGetCurrentThreadWin32Thread();
1106 if (!pti
|| !pti
->rpdesk
|| !pti
->rpdesk
->pDeskInfo
)
1108 pdo
= IntGetActiveDesktop();
1109 /* If KeyboardThread|MouseThread|(RawInputThread or RIT) aka system threads,
1110 pti->fsHooks most likely, is zero. So process KbT & MsT to "send" the message.
1112 if ( !pti
|| !pdo
|| (!(HookId
== WH_KEYBOARD_LL
) && !(HookId
== WH_MOUSE_LL
)) )
1114 TRACE("No PDO %d\n", HookId
);
1123 if ( pti
->TIF_flags
& (TIF_INCLEANUP
|TIF_DISABLEHOOKS
))
1125 TRACE("Hook Thread dead %d\n", HookId
);
1129 if ( ISITHOOKED(HookId
) )
1131 TRACE("Local Hooker %d\n", HookId
);
1135 if ( pdo
->pDeskInfo
->fsHooks
& HOOKID_TO_FLAG(HookId
) )
1137 TRACE("Global Hooker %d\n", HookId
);
1141 if ( !Local
&& !Global
) goto Exit
; // No work!
1145 /* SetWindowHookEx sorts out the Thread issue by placing the Hook to
1146 the correct Thread if not NULL.
1150 pLastHead
= &pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1151 if (IsListEmpty(pLastHead
))
1153 ERR("No Local Hook Found!\n");
1157 Hook
= CONTAINING_RECORD(pLastHead
->Flink
, HOOK
, Chain
);
1158 ObReferenceObject(pti
->pEThread
);
1159 IntReferenceThreadInfo(pti
);
1160 UserRefObjectCo(Hook
, &Ref
);
1162 ClientInfo
= pti
->pClientInfo
;
1163 SaveHook
= pti
->sphkCurrent
;
1164 /* Note: Setting pti->sphkCurrent will also lock the next hook to this
1165 * hook ID. So, the CallNextHookEx will only call to that hook ID
1166 * chain anyway. For Thread Hooks....
1169 /* Load it for the next call. */
1170 pti
->sphkCurrent
= Hook
;
1171 Hook
->phkNext
= IntGetNextHook(Hook
);
1176 ClientInfo
->phkCurrent
= Hook
;
1178 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1180 ClientInfo
= NULL
; // Don't bother next run.
1184 Result
= co_IntCallHookProc( HookId
,
1197 ClientInfo
->phkCurrent
= SaveHook
;
1199 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1206 pti
->sphkCurrent
= SaveHook
;
1207 Hook
->phkNext
= NULL
;
1208 UserDerefObjectCo(Hook
);
1209 IntDereferenceThreadInfo(pti
);
1210 ObDereferenceObject(pti
->pEThread
);
1215 PTHREADINFO ptiHook
;
1216 HHOOK
*pHookHandles
;
1219 /* Keep hooks in array because hooks can be destroyed in user world */
1220 pHookHandles
= IntGetGlobalHookHandles(pdo
, HookId
);
1224 /* Performance goes down the drain. If more hooks are associated to this
1225 * hook ID, this will have to post to each of the thread message queues
1226 * or make a direct call.
1228 for(i
= 0; pHookHandles
[i
]; ++i
)
1230 Hook
= (PHOOK
)UserGetObject(gHandleTable
, pHookHandles
[i
], TYPE_HOOK
);
1233 ERR("Invalid hook!\n");
1237 /* Hook->Thread is null, we hax around this with Hook->head.pti. */
1238 ptiHook
= Hook
->head
.pti
;
1240 if ( (pti
->TIF_flags
& TIF_DISABLEHOOKS
) || (ptiHook
->TIF_flags
& TIF_INCLEANUP
))
1242 TRACE("Next Hook %p, %p\n", ptiHook
->rpdesk
, pdo
);
1245 UserRefObjectCo(Hook
, &Ref
);
1247 if (ptiHook
!= pti
)
1250 if ( HookId
== WH_JOURNALPLAYBACK
|| // 1 | 0
1251 HookId
== WH_JOURNALRECORD
|| // 1 | 0
1252 HookId
== WH_KEYBOARD
|| // 1 | 200
1253 HookId
== WH_MOUSE
|| // 1 | 200
1254 HookId
== WH_KEYBOARD_LL
|| // 0 | 300
1255 HookId
== WH_MOUSE_LL
) // 0 | 300
1257 TRACE("\nGlobal Hook posting to another Thread! %d\n",HookId
);
1258 Result
= co_IntCallLowLevelHook(Hook
, Code
, wParam
, lParam
);
1260 else if (ptiHook
->ppi
== pti
->ppi
)
1262 TRACE("\nGlobal Hook calling to another Thread! %d\n",HookId
);
1263 ObReferenceObject(ptiHook
->pEThread
);
1264 IntReferenceThreadInfo(ptiHook
);
1265 Result
= co_IntCallHookProc( HookId
,
1274 IntDereferenceThreadInfo(ptiHook
);
1275 ObDereferenceObject(ptiHook
->pEThread
);
1279 { /* Make the direct call. */
1280 TRACE("Global going Local Hook calling to Thread! %d\n",HookId
);
1281 ObReferenceObject(pti
->pEThread
);
1282 IntReferenceThreadInfo(pti
);
1283 Result
= co_IntCallHookProc( HookId
,
1292 IntDereferenceThreadInfo(pti
);
1293 ObDereferenceObject(pti
->pEThread
);
1295 UserDerefObjectCo(Hook
);
1297 ExFreePoolWithTag(pHookHandles
, TAG_HOOK
);
1298 TRACE("Ret: Global HookId %d Result 0x%x\n", HookId
,Result
);
1306 IntUnhookWindowsHook(int HookId
, HOOKPROC pfnFilterProc
)
1309 PLIST_ENTRY pLastHead
, pElement
;
1310 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
1312 if (HookId
< WH_MINHOOK
|| WH_MAXHOOK
< HookId
)
1314 EngSetLastError(ERROR_INVALID_HOOK_FILTER
);
1320 pLastHead
= &pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1322 pElement
= pLastHead
->Flink
;
1323 while (pElement
!= pLastHead
)
1325 Hook
= CONTAINING_RECORD(pElement
, HOOK
, Chain
);
1327 /* Get the next element now, we might free the hook in what follows */
1328 pElement
= Hook
->Chain
.Flink
;
1330 if (Hook
->Proc
== pfnFilterProc
)
1332 if (Hook
->head
.pti
== pti
)
1334 IntRemoveHook(Hook
);
1339 EngSetLastError(ERROR_ACCESS_DENIED
);
1349 * Support for compatibility only? Global hooks are processed in kernel space.
1350 * This is very thread specific! Never seeing applications with more than one
1351 * hook per thread installed. Most of the applications are Global hookers and
1352 * associated with just one hook Id. Maybe it's for diagnostic testing or a
1353 * throw back to 3.11?
1357 NtUserCallNextHookEx( int Code
,
1363 PHOOK HookObj
, NextObj
;
1364 PCLIENTINFO ClientInfo
;
1365 LRESULT lResult
= 0;
1366 DECLARE_RETURN(LRESULT
);
1368 TRACE("Enter NtUserCallNextHookEx\n");
1369 UserEnterExclusive();
1371 pti
= GetW32ThreadInfo();
1373 HookObj
= pti
->sphkCurrent
;
1375 if (!HookObj
) RETURN( 0);
1377 NextObj
= HookObj
->phkNext
;
1379 pti
->sphkCurrent
= NextObj
;
1380 ClientInfo
= pti
->pClientInfo
;
1383 ClientInfo
->phkCurrent
= NextObj
;
1385 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1391 /* Now in List run down. */
1392 if (ClientInfo
&& NextObj
)
1394 NextObj
->phkNext
= IntGetNextHook(NextObj
);
1395 lResult
= co_UserCallNextHookEx( NextObj
, Code
, wParam
, lParam
, NextObj
->Ansi
);
1400 TRACE("Leave NtUserCallNextHookEx, ret=%i\n",_ret_
);
1407 NtUserSetWindowsHookAW( int idHook
,
1412 UNICODE_STRING USModuleName
;
1414 RtlInitUnicodeString(&USModuleName
, NULL
);
1415 ThreadId
= PtrToUint(NtCurrentTeb()->ClientId
.UniqueThread
);
1417 return NtUserSetWindowsHookEx( NULL
,
1427 NtUserSetWindowsHookEx( HINSTANCE Mod
,
1428 PUNICODE_STRING UnsafeModuleName
,
1434 PWINSTATION_OBJECT WinStaObj
;
1436 UNICODE_STRING ModuleName
;
1439 PTHREADINFO pti
, ptiHook
= NULL
;
1440 DECLARE_RETURN(HHOOK
);
1442 TRACE("Enter NtUserSetWindowsHookEx\n");
1443 UserEnterExclusive();
1445 pti
= PsGetCurrentThreadWin32Thread();
1447 if (HookId
< WH_MINHOOK
|| WH_MAXHOOK
< HookId
)
1449 EngSetLastError(ERROR_INVALID_HOOK_FILTER
);
1455 EngSetLastError(ERROR_INVALID_FILTER_PROC
);
1459 if (ThreadId
) /* thread-local hook */
1461 if ( HookId
== WH_JOURNALRECORD
||
1462 HookId
== WH_JOURNALPLAYBACK
||
1463 HookId
== WH_KEYBOARD_LL
||
1464 HookId
== WH_MOUSE_LL
||
1465 HookId
== WH_SYSMSGFILTER
)
1467 TRACE("Local hook installing Global HookId: %d\n",HookId
);
1468 /* these can only be global */
1469 EngSetLastError(ERROR_GLOBAL_ONLY_HOOK
);
1473 if ( !(ptiHook
= IntTID2PTI( (HANDLE
)ThreadId
)))
1475 ERR("Invalid thread id 0x%x\n", ThreadId
);
1476 EngSetLastError(ERROR_INVALID_PARAMETER
);
1480 if ( ptiHook
->rpdesk
!= pti
->rpdesk
) // gptiCurrent->rpdesk)
1482 ERR("Local hook wrong desktop HookId: %d\n",HookId
);
1483 EngSetLastError(ERROR_ACCESS_DENIED
);
1487 if (ptiHook
->ppi
!= pti
->ppi
)
1490 (HookId
== WH_GETMESSAGE
||
1491 HookId
== WH_CALLWNDPROC
||
1493 HookId
== WH_HARDWARE
||
1494 HookId
== WH_DEBUG
||
1495 HookId
== WH_SHELL
||
1496 HookId
== WH_FOREGROUNDIDLE
||
1497 HookId
== WH_CALLWNDPROCRET
) )
1499 ERR("Local hook needs hMod HookId: %d\n",HookId
);
1500 EngSetLastError(ERROR_HOOK_NEEDS_HMOD
);
1504 if ( (ptiHook
->TIF_flags
& (TIF_CSRSSTHREAD
|TIF_SYSTEMTHREAD
)) &&
1505 (HookId
== WH_GETMESSAGE
||
1506 HookId
== WH_CALLWNDPROC
||
1508 HookId
== WH_HARDWARE
||
1509 HookId
== WH_DEBUG
||
1510 HookId
== WH_SHELL
||
1511 HookId
== WH_FOREGROUNDIDLE
||
1512 HookId
== WH_CALLWNDPROCRET
) )
1514 EngSetLastError(ERROR_HOOK_TYPE_NOT_ALLOWED
);
1519 else /* System-global hook */
1521 ptiHook
= pti
; // gptiCurrent;
1523 (HookId
== WH_GETMESSAGE
||
1524 HookId
== WH_CALLWNDPROC
||
1526 HookId
== WH_SYSMSGFILTER
||
1527 HookId
== WH_HARDWARE
||
1528 HookId
== WH_DEBUG
||
1529 HookId
== WH_SHELL
||
1530 HookId
== WH_FOREGROUNDIDLE
||
1531 HookId
== WH_CALLWNDPROCRET
) )
1533 ERR("Global hook needs hMod HookId: %d\n",HookId
);
1534 EngSetLastError(ERROR_HOOK_NEEDS_HMOD
);
1539 Status
= IntValidateWindowStationHandle( PsGetCurrentProcess()->Win32WindowStation
,
1545 if (!NT_SUCCESS(Status
))
1547 SetLastNtError(Status
);
1550 ObDereferenceObject(WinStaObj
);
1552 Hook
= UserCreateObject(gHandleTable
, NULL
, ptiHook
, (PHANDLE
)&Handle
, TYPE_HOOK
, sizeof(HOOK
));
1559 Hook
->ihmod
= (INT
)Mod
; // Module Index from atom table, Do this for now.
1560 Hook
->HookId
= HookId
;
1561 Hook
->rpdesk
= ptiHook
->rpdesk
;
1562 Hook
->phkNext
= NULL
; /* Dont use as a chain! Use link lists for chaining. */
1563 Hook
->Proc
= HookProc
;
1566 TRACE("Set Hook Desk %p DeskInfo %p Handle Desk %p\n", pti
->rpdesk
, pti
->pDeskInfo
, Hook
->head
.rpdesk
);
1568 if (ThreadId
) /* Thread-local hook */
1570 InsertHeadList(&ptiHook
->aphkStart
[HOOKID_TO_INDEX(HookId
)], &Hook
->Chain
);
1571 ptiHook
->sphkCurrent
= NULL
;
1572 Hook
->ptiHooked
= ptiHook
;
1573 ptiHook
->fsHooks
|= HOOKID_TO_FLAG(HookId
);
1575 if (ptiHook
->pClientInfo
)
1577 if ( ptiHook
->ppi
== pti
->ppi
) /* gptiCurrent->ppi) */
1581 ptiHook
->pClientInfo
->fsHooks
= ptiHook
->fsHooks
;
1582 ptiHook
->pClientInfo
->phkCurrent
= NULL
;
1584 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1586 ERR("Problem writing to Local ClientInfo!\n");
1592 KeAttachProcess(&ptiHook
->ppi
->peProcess
->Pcb
);
1595 ptiHook
->pClientInfo
->fsHooks
= ptiHook
->fsHooks
;
1596 ptiHook
->pClientInfo
->phkCurrent
= NULL
;
1598 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1600 ERR("Problem writing to Remote ClientInfo!\n");
1609 InsertHeadList(&ptiHook
->rpdesk
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)], &Hook
->Chain
);
1610 Hook
->ptiHooked
= NULL
;
1611 //gptiCurrent->pDeskInfo->fsHooks |= HOOKID_TO_FLAG(HookId);
1612 ptiHook
->rpdesk
->pDeskInfo
->fsHooks
|= HOOKID_TO_FLAG(HookId
);
1613 ptiHook
->sphkCurrent
= NULL
;
1614 ptiHook
->pClientInfo
->phkCurrent
= NULL
;
1617 RtlInitUnicodeString(&Hook
->ModuleName
, NULL
);
1621 Status
= MmCopyFromCaller(&ModuleName
,
1623 sizeof(UNICODE_STRING
));
1624 if (!NT_SUCCESS(Status
))
1626 IntRemoveHook(Hook
);
1627 SetLastNtError(Status
);
1631 Hook
->ModuleName
.Buffer
= ExAllocatePoolWithTag( PagedPool
,
1632 ModuleName
.MaximumLength
,
1634 if (NULL
== Hook
->ModuleName
.Buffer
)
1636 IntRemoveHook(Hook
);
1637 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1641 Hook
->ModuleName
.MaximumLength
= ModuleName
.MaximumLength
;
1642 Status
= MmCopyFromCaller( Hook
->ModuleName
.Buffer
,
1644 ModuleName
.MaximumLength
);
1645 if (!NT_SUCCESS(Status
))
1647 ExFreePoolWithTag(Hook
->ModuleName
.Buffer
, TAG_HOOK
);
1648 Hook
->ModuleName
.Buffer
= NULL
;
1649 IntRemoveHook(Hook
);
1650 SetLastNtError(Status
);
1654 Hook
->ModuleName
.Length
= ModuleName
.Length
;
1655 //// FIXME: Need to load from user32 to verify hMod before calling hook with hMod set!!!!
1656 //// Mod + offPfn == new HookProc Justin Case module is from another process.
1657 FIXME("NtUserSetWindowsHookEx Setting process hMod instance addressing.\n");
1658 /* Make proc relative to the module base */
1659 Hook
->offPfn
= (ULONG_PTR
)((char *)HookProc
- (char *)Mod
);
1664 TRACE("Installing: HookId %d Global %s\n", HookId
, !ThreadId
? "TRUE" : "FALSE");
1669 UserDereferenceObject(Hook
);
1670 TRACE("Leave NtUserSetWindowsHookEx, ret=%p\n", _ret_
);
1677 NtUserUnhookWindowsHookEx(HHOOK Hook
)
1680 DECLARE_RETURN(BOOL
);
1682 TRACE("Enter NtUserUnhookWindowsHookEx\n");
1683 UserEnterExclusive();
1685 if (!(HookObj
= IntGetHookObject(Hook
)))
1687 ERR("Invalid handle passed to NtUserUnhookWindowsHookEx\n");
1688 /* SetLastNtError(Status); */
1692 ASSERT(Hook
== UserHMGetHandle(HookObj
));
1694 IntRemoveHook(HookObj
);
1696 UserDereferenceObject(HookObj
);
1701 TRACE("Leave NtUserUnhookWindowsHookEx, ret=%i\n",_ret_
);
1708 NtUserRegisterUserApiHook(
1709 PUNICODE_STRING m_dllname1
,
1710 PUNICODE_STRING m_funname1
,
1715 UNICODE_STRING strDllNameSafe
;
1716 UNICODE_STRING strFuncNameSafe
;
1719 /* Probe and capture parameters */
1720 Status
= ProbeAndCaptureUnicodeString(&strDllNameSafe
, UserMode
, m_dllname1
);
1721 if(!NT_SUCCESS(Status
))
1723 EngSetLastError(RtlNtStatusToDosError(Status
));
1727 Status
= ProbeAndCaptureUnicodeString(&strFuncNameSafe
, UserMode
, m_funname1
);
1728 if(!NT_SUCCESS(Status
))
1730 ReleaseCapturedUnicodeString(&strDllNameSafe
, UserMode
);
1731 EngSetLastError(RtlNtStatusToDosError(Status
));
1735 UserEnterExclusive();
1737 /* Call internal function */
1738 ret
= UserRegisterUserApiHook(&strDllNameSafe
, &strFuncNameSafe
);
1742 /* Cleanup only in case of failure */
1745 ReleaseCapturedUnicodeString(&strDllNameSafe
, UserMode
);
1746 ReleaseCapturedUnicodeString(&strFuncNameSafe
, UserMode
);
1754 NtUserUnregisterUserApiHook(VOID
)
1758 UserEnterExclusive();
1759 ret
= UserUnregisterUserApiHook();