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: 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
;
345 /* The odds are high for this to be a Global call. */
346 Result
= co_IntCallHookProc( HookId
,
356 /* The odds so high, no one is waiting for the results. */
357 if (pHP
->pHookStructs
) ExFreePoolWithTag(pHP
->pHookStructs
, TAG_HOOK
);
358 ExFreePoolWithTag(pHP
, TAG_HOOK
);
365 co_HOOK_CallHookNext( PHOOK Hook
,
370 TRACE("Calling Next HOOK %d\n", Hook
->HookId
);
372 return co_IntCallHookProc( Hook
->HookId
,
386 co_IntCallDebugHook(PHOOK Hook
,
395 PVOID HooklParam
= NULL
;
402 ProbeForRead((PVOID
)lParam
,
403 sizeof(DEBUGHOOKINFO
),
406 RtlCopyMemory(&Debug
,
408 sizeof(DEBUGHOOKINFO
));
410 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
418 ERR("HOOK WH_DEBUG read from lParam ERROR!\n");
423 return lResult
; /* Need lParam! */
431 case HCBT_CLICKSKIPPED
:
432 Size
= sizeof(MOUSEHOOKSTRUCTEX
);
440 Size
= sizeof(CBTACTIVATESTRUCT
);
443 case HCBT_CREATEWND
: /* Handle ANSI? */
444 Size
= sizeof(CBT_CREATEWND
);
445 /* What shall we do? Size += sizeof(HOOKPROC_CBT_CREATEWND_EXTRA_ARGUMENTS); same as CREATESTRUCTEX */
449 Size
= sizeof(LPARAM
);
455 Size
= sizeof(MSLLHOOKSTRUCT
);
459 Size
= sizeof(KBDLLHOOKSTRUCT
);
463 case WH_SYSMSGFILTER
:
468 case WH_JOURNALPLAYBACK
:
469 case WH_JOURNALRECORD
:
470 Size
= sizeof(EVENTMSG
);
473 case WH_FOREGROUNDIDLE
:
477 Size
= sizeof(LPARAM
);
480 if (Size
> sizeof(LPARAM
))
481 HooklParam
= ExAllocatePoolWithTag(NonPagedPool
, Size
, TAG_HOOK
);
487 ProbeForRead((PVOID
)Debug
.lParam
,
491 RtlCopyMemory(HooklParam
,
495 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
503 ERR("HOOK WH_DEBUG read from Debug.lParam ERROR!\n");
504 ExFreePoolWithTag(HooklParam
, TAG_HOOK
);
509 if (HooklParam
) Debug
.lParam
= (LPARAM
)HooklParam
;
510 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Debug
);
511 if (HooklParam
) ExFreePoolWithTag(HooklParam
, TAG_HOOK
);
519 co_UserCallNextHookEx(PHOOK Hook
,
528 /* Handle this one first. */
529 if ((Hook
->HookId
== WH_MOUSE
) ||
530 (Hook
->HookId
== WH_CBT
&& Code
== HCBT_CLICKSKIPPED
))
532 MOUSEHOOKSTRUCTEX Mouse
;
537 ProbeForRead((PVOID
)lParam
,
538 sizeof(MOUSEHOOKSTRUCTEX
),
541 RtlCopyMemory(&Mouse
,
543 sizeof(MOUSEHOOKSTRUCTEX
));
545 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
553 ERR("HOOK WH_MOUSE read from lParam ERROR!\n");
559 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Mouse
);
569 MSLLHOOKSTRUCT Mouse
;
575 ProbeForRead((PVOID
)lParam
,
576 sizeof(MSLLHOOKSTRUCT
),
579 RtlCopyMemory(&Mouse
,
581 sizeof(MSLLHOOKSTRUCT
));
583 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
591 ERR("HOOK WH_MOUSE_LL read from lParam ERROR!\n");
597 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Mouse
);
604 KBDLLHOOKSTRUCT Keyboard
;
610 ProbeForRead((PVOID
)lParam
,
611 sizeof(KBDLLHOOKSTRUCT
),
614 RtlCopyMemory(&Keyboard
,
616 sizeof(KBDLLHOOKSTRUCT
));
618 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
626 ERR("HOOK WH_KEYBORD_LL read from lParam ERROR!\n");
632 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Keyboard
);
638 case WH_SYSMSGFILTER
:
647 ProbeForRead((PVOID
)lParam
,
655 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
663 ERR("HOOK WH_XMESSAGEX read from lParam ERROR!\n");
669 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&Msg
);
671 if (lParam
&& (Hook
->HookId
== WH_GETMESSAGE
))
675 ProbeForWrite((PVOID
)lParam
,
679 RtlCopyMemory((PVOID
)lParam
,
683 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
691 ERR("HOOK WH_GETMESSAGE write to lParam ERROR!\n");
699 TRACE("HOOK WH_CBT!\n");
704 LPCBT_CREATEWNDW pcbtcww
= (LPCBT_CREATEWNDW
)lParam
;
706 TRACE("HOOK HCBT_CREATEWND\n");
711 ProbeForRead( pcbtcww
,
712 sizeof(CBT_CREATEWNDA
),
714 ProbeForWrite(pcbtcww
->lpcs
,
715 sizeof(CREATESTRUCTA
),
717 ProbeForRead( pcbtcww
->lpcs
->lpszName
,
721 if (!IS_ATOM(pcbtcww
->lpcs
->lpszClass
))
723 _Analysis_assume_(pcbtcww
->lpcs
->lpszClass
!= NULL
);
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 _Analysis_assume_(pcbtcww
->lpcs
->lpszClass
!= NULL
);
744 ProbeForRead(pcbtcww
->lpcs
->lpszClass
,
750 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
758 ERR("HOOK HCBT_CREATEWND write ERROR!\n");
760 /* The next call handles the structures. */
761 if (!BadChk
&& Hook
->Proc
)
763 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, lParam
);
772 TRACE("HOOK HCBT_MOVESIZE\n");
778 ProbeForRead((PVOID
)lParam
,
786 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
794 ERR("HOOK HCBT_MOVESIZE read from lParam ERROR!\n");
800 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&rt
);
807 CBTACTIVATESTRUCT CbAs
;
809 TRACE("HOOK HCBT_ACTIVATE\n");
814 ProbeForRead((PVOID
)lParam
,
815 sizeof(CBTACTIVATESTRUCT
),
820 sizeof(CBTACTIVATESTRUCT
));
822 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
830 ERR("HOOK HCBT_ACTIVATE read from lParam ERROR!\n");
836 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)&CbAs
);
841 /* The rest just use default. */
843 TRACE("HOOK HCBT_ %d\n",Code
);
844 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, lParam
);
849 Note WH_JOURNALPLAYBACK,
850 "To have the system wait before processing the message, the return value
851 must be the amount of time, in clock ticks, that the system should wait."
853 case WH_JOURNALPLAYBACK
:
854 case WH_JOURNALRECORD
:
862 ProbeForRead((PVOID
)lParam
,
866 RtlCopyMemory(&EventMsg
,
870 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
878 ERR("HOOK WH_JOURNAL read from lParam ERROR!\n");
884 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, (LPARAM
)(lParam
? &EventMsg
: NULL
));
890 ProbeForWrite((PVOID
)lParam
,
894 RtlCopyMemory((PVOID
)lParam
,
898 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
906 ERR("HOOK WH_JOURNAL write to lParam ERROR!\n");
914 lResult
= co_IntCallDebugHook(Hook
, Code
, wParam
, lParam
, Ansi
);
918 * Default the rest like, WH_FOREGROUNDIDLE, WH_KEYBOARD and WH_SHELL.
920 case WH_FOREGROUNDIDLE
:
923 lResult
= co_HOOK_CallHookNext(Hook
, Code
, wParam
, lParam
);
927 ERR("Unsupported HOOK Id -> %d\n",Hook
->HookId
);
935 IntGetHookObject(HHOOK hHook
)
941 EngSetLastError(ERROR_INVALID_HOOK_HANDLE
);
945 Hook
= (PHOOK
)UserGetObject(gHandleTable
, hHook
, TYPE_HOOK
);
948 EngSetLastError(ERROR_INVALID_HOOK_HANDLE
);
952 UserReferenceObject(Hook
);
960 IntGetGlobalHookHandles(PDESKTOP pdo
, int HookId
)
962 PLIST_ENTRY pLastHead
, pElem
;
968 pLastHead
= &pdo
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
969 for (pElem
= pLastHead
->Flink
; pElem
!= pLastHead
; pElem
= pElem
->Flink
)
972 pList
= ExAllocatePoolWithTag(PagedPool
, (cHooks
+ 1) * sizeof(HHOOK
), TAG_HOOK
);
975 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
979 for (pElem
= pLastHead
->Flink
; pElem
!= pLastHead
; pElem
= pElem
->Flink
)
981 pHook
= CONTAINING_RECORD(pElem
, HOOK
, Chain
);
982 NT_ASSERT(i
< cHooks
);
983 pList
[i
++] = pHook
->head
.h
;
990 /* Find the next hook in the chain */
993 IntGetNextHook(PHOOK Hook
)
995 int HookId
= Hook
->HookId
;
996 PLIST_ENTRY pLastHead
, pElem
;
1001 pti
= Hook
->ptiHooked
;
1002 pLastHead
= &pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1006 pti
= PsGetCurrentThreadWin32Thread();
1007 pLastHead
= &pti
->rpdesk
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1010 pElem
= Hook
->Chain
.Flink
;
1011 if (pElem
!= pLastHead
)
1012 return CONTAINING_RECORD(pElem
, HOOK
, Chain
);
1016 /* Free a hook, removing it from its chain */
1020 IntFreeHook(PHOOK Hook
)
1022 RemoveEntryList(&Hook
->Chain
);
1023 if (Hook
->ModuleName
.Buffer
)
1025 ExFreePoolWithTag(Hook
->ModuleName
.Buffer
, TAG_HOOK
);
1026 Hook
->ModuleName
.Buffer
= NULL
;
1029 UserDeleteObject(UserHMGetHandle(Hook
), TYPE_HOOK
);
1032 /* Remove a hook, freeing it from the chain */
1034 IntRemoveHook(PVOID Object
)
1039 PHOOK Hook
= Object
;
1041 HookId
= Hook
->HookId
;
1043 if (Hook
->ptiHooked
) // Local
1045 pti
= Hook
->ptiHooked
;
1049 if ( IsListEmpty(&pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)]) )
1051 pti
->fsHooks
&= ~HOOKID_TO_FLAG(HookId
);
1054 pti
->pClientInfo
->fsHooks
= pti
->fsHooks
;
1056 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1068 pdo
= IntGetActiveDesktop();
1072 IsListEmpty(&pdo
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)]) )
1074 pdo
->pDeskInfo
->fsHooks
&= ~HOOKID_TO_FLAG(HookId
);
1082 Win32k Kernel Space Hook Caller.
1086 co_HOOK_CallHooks( INT HookId
,
1091 PHOOK Hook
, SaveHook
;
1093 PCLIENTINFO ClientInfo
;
1094 PLIST_ENTRY pLastHead
;
1096 BOOL Local
= FALSE
, Global
= FALSE
;
1098 USER_REFERENCE_ENTRY Ref
;
1100 ASSERT(WH_MINHOOK
<= HookId
&& HookId
<= WH_MAXHOOK
);
1102 pti
= PsGetCurrentThreadWin32Thread();
1103 if (!pti
|| !pti
->rpdesk
|| !pti
->rpdesk
->pDeskInfo
)
1105 pdo
= IntGetActiveDesktop();
1106 /* If KeyboardThread|MouseThread|(RawInputThread or RIT) aka system threads,
1107 pti->fsHooks most likely, is zero. So process KbT & MsT to "send" the message.
1109 if ( !pti
|| !pdo
|| (!(HookId
== WH_KEYBOARD_LL
) && !(HookId
== WH_MOUSE_LL
)) )
1111 TRACE("No PDO %d\n", HookId
);
1120 if ( pti
->TIF_flags
& (TIF_INCLEANUP
|TIF_DISABLEHOOKS
))
1122 TRACE("Hook Thread dead %d\n", HookId
);
1126 if ( ISITHOOKED(HookId
) )
1128 TRACE("Local Hooker %d\n", HookId
);
1132 if ( pdo
->pDeskInfo
->fsHooks
& HOOKID_TO_FLAG(HookId
) )
1134 TRACE("Global Hooker %d\n", HookId
);
1138 if ( !Local
&& !Global
) goto Exit
; // No work!
1142 /* SetWindowHookEx sorts out the Thread issue by placing the Hook to
1143 the correct Thread if not NULL.
1147 pLastHead
= &pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1148 if (IsListEmpty(pLastHead
))
1150 ERR("No Local Hook Found!\n");
1154 Hook
= CONTAINING_RECORD(pLastHead
->Flink
, HOOK
, Chain
);
1155 ObReferenceObject(pti
->pEThread
);
1156 IntReferenceThreadInfo(pti
);
1157 UserRefObjectCo(Hook
, &Ref
);
1159 ClientInfo
= pti
->pClientInfo
;
1160 SaveHook
= pti
->sphkCurrent
;
1161 /* Note: Setting pti->sphkCurrent will also lock the next hook to this
1162 * hook ID. So, the CallNextHookEx will only call to that hook ID
1163 * chain anyway. For Thread Hooks....
1166 /* Load it for the next call. */
1167 pti
->sphkCurrent
= Hook
;
1168 Hook
->phkNext
= IntGetNextHook(Hook
);
1173 ClientInfo
->phkCurrent
= Hook
;
1175 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1177 ClientInfo
= NULL
; // Don't bother next run.
1181 Result
= co_IntCallHookProc( HookId
,
1194 ClientInfo
->phkCurrent
= SaveHook
;
1196 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1203 pti
->sphkCurrent
= SaveHook
;
1204 Hook
->phkNext
= NULL
;
1205 UserDerefObjectCo(Hook
);
1206 IntDereferenceThreadInfo(pti
);
1207 ObDereferenceObject(pti
->pEThread
);
1212 PTHREADINFO ptiHook
;
1213 HHOOK
*pHookHandles
;
1216 /* Keep hooks in array because hooks can be destroyed in user world */
1217 pHookHandles
= IntGetGlobalHookHandles(pdo
, HookId
);
1221 /* Performance goes down the drain. If more hooks are associated to this
1222 * hook ID, this will have to post to each of the thread message queues
1223 * or make a direct call.
1225 for(i
= 0; pHookHandles
[i
]; ++i
)
1227 Hook
= (PHOOK
)UserGetObject(gHandleTable
, pHookHandles
[i
], TYPE_HOOK
);
1230 ERR("Invalid hook!\n");
1234 /* Hook->Thread is null, we hax around this with Hook->head.pti. */
1235 ptiHook
= Hook
->head
.pti
;
1237 if ( (pti
->TIF_flags
& TIF_DISABLEHOOKS
) || (ptiHook
->TIF_flags
& TIF_INCLEANUP
))
1239 TRACE("Next Hook %p, %p\n", ptiHook
->rpdesk
, pdo
);
1242 UserRefObjectCo(Hook
, &Ref
);
1244 if (ptiHook
!= pti
)
1247 if ( HookId
== WH_JOURNALPLAYBACK
|| // 1 | 0
1248 HookId
== WH_JOURNALRECORD
|| // 1 | 0
1249 HookId
== WH_KEYBOARD
|| // 1 | 200
1250 HookId
== WH_MOUSE
|| // 1 | 200
1251 HookId
== WH_KEYBOARD_LL
|| // 0 | 300
1252 HookId
== WH_MOUSE_LL
) // 0 | 300
1254 TRACE("\nGlobal Hook posting to another Thread! %d\n",HookId
);
1255 Result
= co_IntCallLowLevelHook(Hook
, Code
, wParam
, lParam
);
1257 else if (ptiHook
->ppi
== pti
->ppi
)
1259 TRACE("\nGlobal Hook calling to another Thread! %d\n",HookId
);
1260 ObReferenceObject(ptiHook
->pEThread
);
1261 IntReferenceThreadInfo(ptiHook
);
1262 Result
= co_IntCallHookProc( HookId
,
1271 IntDereferenceThreadInfo(ptiHook
);
1272 ObDereferenceObject(ptiHook
->pEThread
);
1276 { /* Make the direct call. */
1277 TRACE("Global going Local Hook calling to Thread! %d\n",HookId
);
1278 ObReferenceObject(pti
->pEThread
);
1279 IntReferenceThreadInfo(pti
);
1280 Result
= co_IntCallHookProc( HookId
,
1289 IntDereferenceThreadInfo(pti
);
1290 ObDereferenceObject(pti
->pEThread
);
1292 UserDerefObjectCo(Hook
);
1294 ExFreePoolWithTag(pHookHandles
, TAG_HOOK
);
1295 TRACE("Ret: Global HookId %d Result 0x%x\n", HookId
,Result
);
1303 IntUnhookWindowsHook(int HookId
, HOOKPROC pfnFilterProc
)
1306 PLIST_ENTRY pLastHead
, pElement
;
1307 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
1309 if (HookId
< WH_MINHOOK
|| WH_MAXHOOK
< HookId
)
1311 EngSetLastError(ERROR_INVALID_HOOK_FILTER
);
1317 pLastHead
= &pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1319 pElement
= pLastHead
->Flink
;
1320 while (pElement
!= pLastHead
)
1322 Hook
= CONTAINING_RECORD(pElement
, HOOK
, Chain
);
1324 /* Get the next element now, we might free the hook in what follows */
1325 pElement
= Hook
->Chain
.Flink
;
1327 if (Hook
->Proc
== pfnFilterProc
)
1329 if (Hook
->head
.pti
== pti
)
1331 IntRemoveHook(Hook
);
1336 EngSetLastError(ERROR_ACCESS_DENIED
);
1346 * Support for compatibility only? Global hooks are processed in kernel space.
1347 * This is very thread specific! Never seeing applications with more than one
1348 * hook per thread installed. Most of the applications are Global hookers and
1349 * associated with just one hook Id. Maybe it's for diagnostic testing or a
1350 * throw back to 3.11?
1354 NtUserCallNextHookEx( int Code
,
1360 PHOOK HookObj
, NextObj
;
1361 PCLIENTINFO ClientInfo
;
1362 LRESULT lResult
= 0;
1363 DECLARE_RETURN(LRESULT
);
1365 TRACE("Enter NtUserCallNextHookEx\n");
1366 UserEnterExclusive();
1368 pti
= GetW32ThreadInfo();
1370 HookObj
= pti
->sphkCurrent
;
1372 if (!HookObj
) RETURN( 0);
1374 NextObj
= HookObj
->phkNext
;
1376 pti
->sphkCurrent
= NextObj
;
1377 ClientInfo
= pti
->pClientInfo
;
1380 ClientInfo
->phkCurrent
= NextObj
;
1382 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1388 /* Now in List run down. */
1389 if (ClientInfo
&& NextObj
)
1391 NextObj
->phkNext
= IntGetNextHook(NextObj
);
1392 lResult
= co_UserCallNextHookEx( NextObj
, Code
, wParam
, lParam
, NextObj
->Ansi
);
1397 TRACE("Leave NtUserCallNextHookEx, ret=%i\n",_ret_
);
1404 NtUserSetWindowsHookAW( int idHook
,
1409 UNICODE_STRING USModuleName
;
1411 RtlInitUnicodeString(&USModuleName
, NULL
);
1412 ThreadId
= PtrToUint(NtCurrentTeb()->ClientId
.UniqueThread
);
1414 return NtUserSetWindowsHookEx( NULL
,
1424 NtUserSetWindowsHookEx( HINSTANCE Mod
,
1425 PUNICODE_STRING UnsafeModuleName
,
1431 PWINSTATION_OBJECT WinStaObj
;
1433 UNICODE_STRING ModuleName
;
1436 PTHREADINFO pti
, ptiHook
= NULL
;
1437 DECLARE_RETURN(HHOOK
);
1439 TRACE("Enter NtUserSetWindowsHookEx\n");
1440 UserEnterExclusive();
1442 pti
= PsGetCurrentThreadWin32Thread();
1444 if (HookId
< WH_MINHOOK
|| WH_MAXHOOK
< HookId
)
1446 EngSetLastError(ERROR_INVALID_HOOK_FILTER
);
1452 EngSetLastError(ERROR_INVALID_FILTER_PROC
);
1456 if (ThreadId
) /* thread-local hook */
1458 if ( HookId
== WH_JOURNALRECORD
||
1459 HookId
== WH_JOURNALPLAYBACK
||
1460 HookId
== WH_KEYBOARD_LL
||
1461 HookId
== WH_MOUSE_LL
||
1462 HookId
== WH_SYSMSGFILTER
)
1464 TRACE("Local hook installing Global HookId: %d\n",HookId
);
1465 /* these can only be global */
1466 EngSetLastError(ERROR_GLOBAL_ONLY_HOOK
);
1470 if ( !(ptiHook
= IntTID2PTI( (HANDLE
)ThreadId
)))
1472 ERR("Invalid thread id 0x%x\n", ThreadId
);
1473 EngSetLastError(ERROR_INVALID_PARAMETER
);
1477 if ( ptiHook
->rpdesk
!= pti
->rpdesk
) // gptiCurrent->rpdesk)
1479 ERR("Local hook wrong desktop HookId: %d\n",HookId
);
1480 EngSetLastError(ERROR_ACCESS_DENIED
);
1484 if (ptiHook
->ppi
!= pti
->ppi
)
1487 (HookId
== WH_GETMESSAGE
||
1488 HookId
== WH_CALLWNDPROC
||
1490 HookId
== WH_HARDWARE
||
1491 HookId
== WH_DEBUG
||
1492 HookId
== WH_SHELL
||
1493 HookId
== WH_FOREGROUNDIDLE
||
1494 HookId
== WH_CALLWNDPROCRET
) )
1496 ERR("Local hook needs hMod HookId: %d\n",HookId
);
1497 EngSetLastError(ERROR_HOOK_NEEDS_HMOD
);
1501 if ( (ptiHook
->TIF_flags
& (TIF_CSRSSTHREAD
|TIF_SYSTEMTHREAD
)) &&
1502 (HookId
== WH_GETMESSAGE
||
1503 HookId
== WH_CALLWNDPROC
||
1505 HookId
== WH_HARDWARE
||
1506 HookId
== WH_DEBUG
||
1507 HookId
== WH_SHELL
||
1508 HookId
== WH_FOREGROUNDIDLE
||
1509 HookId
== WH_CALLWNDPROCRET
) )
1511 EngSetLastError(ERROR_HOOK_TYPE_NOT_ALLOWED
);
1516 else /* System-global hook */
1518 ptiHook
= pti
; // gptiCurrent;
1520 (HookId
== WH_GETMESSAGE
||
1521 HookId
== WH_CALLWNDPROC
||
1523 HookId
== WH_SYSMSGFILTER
||
1524 HookId
== WH_HARDWARE
||
1525 HookId
== WH_DEBUG
||
1526 HookId
== WH_SHELL
||
1527 HookId
== WH_FOREGROUNDIDLE
||
1528 HookId
== WH_CALLWNDPROCRET
) )
1530 ERR("Global hook needs hMod HookId: %d\n",HookId
);
1531 EngSetLastError(ERROR_HOOK_NEEDS_HMOD
);
1536 Status
= IntValidateWindowStationHandle( PsGetCurrentProcess()->Win32WindowStation
,
1542 if (!NT_SUCCESS(Status
))
1544 SetLastNtError(Status
);
1547 ObDereferenceObject(WinStaObj
);
1549 Hook
= UserCreateObject(gHandleTable
, NULL
, ptiHook
, (PHANDLE
)&Handle
, TYPE_HOOK
, sizeof(HOOK
));
1556 Hook
->ihmod
= (INT
)Mod
; // Module Index from atom table, Do this for now.
1557 Hook
->HookId
= HookId
;
1558 Hook
->rpdesk
= ptiHook
->rpdesk
;
1559 Hook
->phkNext
= NULL
; /* Dont use as a chain! Use link lists for chaining. */
1560 Hook
->Proc
= HookProc
;
1563 TRACE("Set Hook Desk %p DeskInfo %p Handle Desk %p\n", pti
->rpdesk
, pti
->pDeskInfo
, Hook
->head
.rpdesk
);
1565 if (ThreadId
) /* Thread-local hook */
1567 InsertHeadList(&ptiHook
->aphkStart
[HOOKID_TO_INDEX(HookId
)], &Hook
->Chain
);
1568 ptiHook
->sphkCurrent
= NULL
;
1569 Hook
->ptiHooked
= ptiHook
;
1570 ptiHook
->fsHooks
|= HOOKID_TO_FLAG(HookId
);
1572 if (ptiHook
->pClientInfo
)
1574 if ( ptiHook
->ppi
== pti
->ppi
) /* gptiCurrent->ppi) */
1578 ptiHook
->pClientInfo
->fsHooks
= ptiHook
->fsHooks
;
1579 ptiHook
->pClientInfo
->phkCurrent
= NULL
;
1581 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1583 ERR("Problem writing to Local ClientInfo!\n");
1589 KeAttachProcess(&ptiHook
->ppi
->peProcess
->Pcb
);
1592 ptiHook
->pClientInfo
->fsHooks
= ptiHook
->fsHooks
;
1593 ptiHook
->pClientInfo
->phkCurrent
= NULL
;
1595 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1597 ERR("Problem writing to Remote ClientInfo!\n");
1606 InsertHeadList(&ptiHook
->rpdesk
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)], &Hook
->Chain
);
1607 Hook
->ptiHooked
= NULL
;
1608 //gptiCurrent->pDeskInfo->fsHooks |= HOOKID_TO_FLAG(HookId);
1609 ptiHook
->rpdesk
->pDeskInfo
->fsHooks
|= HOOKID_TO_FLAG(HookId
);
1610 ptiHook
->sphkCurrent
= NULL
;
1611 ptiHook
->pClientInfo
->phkCurrent
= NULL
;
1614 RtlInitUnicodeString(&Hook
->ModuleName
, NULL
);
1618 Status
= MmCopyFromCaller(&ModuleName
,
1620 sizeof(UNICODE_STRING
));
1621 if (!NT_SUCCESS(Status
))
1623 IntRemoveHook(Hook
);
1624 SetLastNtError(Status
);
1628 Hook
->ModuleName
.Buffer
= ExAllocatePoolWithTag( PagedPool
,
1629 ModuleName
.MaximumLength
,
1631 if (NULL
== Hook
->ModuleName
.Buffer
)
1633 IntRemoveHook(Hook
);
1634 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1638 Hook
->ModuleName
.MaximumLength
= ModuleName
.MaximumLength
;
1639 Status
= MmCopyFromCaller( Hook
->ModuleName
.Buffer
,
1641 ModuleName
.MaximumLength
);
1642 if (!NT_SUCCESS(Status
))
1644 ExFreePoolWithTag(Hook
->ModuleName
.Buffer
, TAG_HOOK
);
1645 Hook
->ModuleName
.Buffer
= NULL
;
1646 IntRemoveHook(Hook
);
1647 SetLastNtError(Status
);
1651 Hook
->ModuleName
.Length
= ModuleName
.Length
;
1652 //// FIXME: Need to load from user32 to verify hMod before calling hook with hMod set!!!!
1653 //// Mod + offPfn == new HookProc Justin Case module is from another process.
1654 FIXME("NtUserSetWindowsHookEx Setting process hMod instance addressing.\n");
1655 /* Make proc relative to the module base */
1656 Hook
->offPfn
= (ULONG_PTR
)((char *)HookProc
- (char *)Mod
);
1661 TRACE("Installing: HookId %d Global %s\n", HookId
, !ThreadId
? "TRUE" : "FALSE");
1666 UserDereferenceObject(Hook
);
1667 TRACE("Leave NtUserSetWindowsHookEx, ret=%p\n", _ret_
);
1674 NtUserUnhookWindowsHookEx(HHOOK Hook
)
1677 DECLARE_RETURN(BOOL
);
1679 TRACE("Enter NtUserUnhookWindowsHookEx\n");
1680 UserEnterExclusive();
1682 if (!(HookObj
= IntGetHookObject(Hook
)))
1684 ERR("Invalid handle passed to NtUserUnhookWindowsHookEx\n");
1685 /* SetLastNtError(Status); */
1689 ASSERT(Hook
== UserHMGetHandle(HookObj
));
1691 IntRemoveHook(HookObj
);
1693 UserDereferenceObject(HookObj
);
1698 TRACE("Leave NtUserUnhookWindowsHookEx, ret=%i\n",_ret_
);
1705 NtUserRegisterUserApiHook(
1706 PUNICODE_STRING m_dllname1
,
1707 PUNICODE_STRING m_funname1
,
1712 UNICODE_STRING strDllNameSafe
;
1713 UNICODE_STRING strFuncNameSafe
;
1716 /* Probe and capture parameters */
1717 Status
= ProbeAndCaptureUnicodeString(&strDllNameSafe
, UserMode
, m_dllname1
);
1718 if(!NT_SUCCESS(Status
))
1720 EngSetLastError(RtlNtStatusToDosError(Status
));
1724 Status
= ProbeAndCaptureUnicodeString(&strFuncNameSafe
, UserMode
, m_funname1
);
1725 if(!NT_SUCCESS(Status
))
1727 ReleaseCapturedUnicodeString(&strDllNameSafe
, UserMode
);
1728 EngSetLastError(RtlNtStatusToDosError(Status
));
1732 UserEnterExclusive();
1734 /* Call internal function */
1735 ret
= UserRegisterUserApiHook(&strDllNameSafe
, &strFuncNameSafe
);
1739 /* Cleanup only in case of failure */
1742 ReleaseCapturedUnicodeString(&strDllNameSafe
, UserMode
);
1743 ReleaseCapturedUnicodeString(&strFuncNameSafe
, UserMode
);
1751 NtUserUnregisterUserApiHook(VOID
)
1755 UserEnterExclusive();
1756 ret
= UserUnregisterUserApiHook();