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 */
1032 IntRemoveHook(PVOID Object
)
1037 PHOOK Hook
= Object
;
1039 HookId
= Hook
->HookId
;
1041 if (Hook
->ptiHooked
) // Local
1043 pti
= Hook
->ptiHooked
;
1047 if ( IsListEmpty(&pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)]) )
1049 pti
->fsHooks
&= ~HOOKID_TO_FLAG(HookId
);
1052 pti
->pClientInfo
->fsHooks
= pti
->fsHooks
;
1054 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1066 pdo
= IntGetActiveDesktop();
1070 IsListEmpty(&pdo
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)]) )
1072 pdo
->pDeskInfo
->fsHooks
&= ~HOOKID_TO_FLAG(HookId
);
1080 Win32k Kernel Space Hook Caller.
1084 co_HOOK_CallHooks( INT HookId
,
1089 PHOOK Hook
, SaveHook
;
1091 PCLIENTINFO ClientInfo
;
1092 PLIST_ENTRY pLastHead
;
1094 BOOL Local
= FALSE
, Global
= FALSE
;
1096 USER_REFERENCE_ENTRY Ref
;
1098 ASSERT(WH_MINHOOK
<= HookId
&& HookId
<= WH_MAXHOOK
);
1100 pti
= PsGetCurrentThreadWin32Thread();
1101 if (!pti
|| !pti
->rpdesk
|| !pti
->rpdesk
->pDeskInfo
)
1103 pdo
= IntGetActiveDesktop();
1104 /* If KeyboardThread|MouseThread|(RawInputThread or RIT) aka system threads,
1105 pti->fsHooks most likely, is zero. So process KbT & MsT to "send" the message.
1107 if ( !pti
|| !pdo
|| (!(HookId
== WH_KEYBOARD_LL
) && !(HookId
== WH_MOUSE_LL
)) )
1109 TRACE("No PDO %d\n", HookId
);
1118 if ( pti
->TIF_flags
& (TIF_INCLEANUP
|TIF_DISABLEHOOKS
))
1120 TRACE("Hook Thread dead %d\n", HookId
);
1124 if ( ISITHOOKED(HookId
) )
1126 TRACE("Local Hooker %d\n", HookId
);
1130 if ( pdo
->pDeskInfo
->fsHooks
& HOOKID_TO_FLAG(HookId
) )
1132 TRACE("Global Hooker %d\n", HookId
);
1136 if ( !Local
&& !Global
) goto Exit
; // No work!
1140 /* SetWindowHookEx sorts out the Thread issue by placing the Hook to
1141 the correct Thread if not NULL.
1145 pLastHead
= &pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1146 if (IsListEmpty(pLastHead
))
1148 ERR("No Local Hook Found!\n");
1152 Hook
= CONTAINING_RECORD(pLastHead
->Flink
, HOOK
, Chain
);
1153 UserRefObjectCo(Hook
, &Ref
);
1155 ClientInfo
= pti
->pClientInfo
;
1156 SaveHook
= pti
->sphkCurrent
;
1157 /* Note: Setting pti->sphkCurrent will also lock the next hook to this
1158 * hook ID. So, the CallNextHookEx will only call to that hook ID
1159 * chain anyway. For Thread Hooks....
1162 /* Load it for the next call. */
1163 pti
->sphkCurrent
= Hook
;
1164 Hook
->phkNext
= IntGetNextHook(Hook
);
1169 ClientInfo
->phkCurrent
= Hook
;
1171 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1173 ClientInfo
= NULL
; // Don't bother next run.
1177 Result
= co_IntCallHookProc( HookId
,
1190 ClientInfo
->phkCurrent
= SaveHook
;
1192 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1199 pti
->sphkCurrent
= SaveHook
;
1200 Hook
->phkNext
= NULL
;
1201 UserDerefObjectCo(Hook
);
1206 PTHREADINFO ptiHook
;
1207 HHOOK
*pHookHandles
;
1210 /* Keep hooks in array because hooks can be destroyed in user world */
1211 pHookHandles
= IntGetGlobalHookHandles(pdo
, HookId
);
1215 /* Performance goes down the drain. If more hooks are associated to this
1216 * hook ID, this will have to post to each of the thread message queues
1217 * or make a direct call.
1219 for(i
= 0; pHookHandles
[i
]; ++i
)
1221 Hook
= (PHOOK
)UserGetObject(gHandleTable
, pHookHandles
[i
], TYPE_HOOK
);
1224 ERR("Invalid hook!\n");
1227 UserRefObjectCo(Hook
, &Ref
);
1229 /* Hook->Thread is null, we hax around this with Hook->head.pti. */
1230 ptiHook
= Hook
->head
.pti
;
1232 if ( (pti
->TIF_flags
& TIF_DISABLEHOOKS
) || (ptiHook
->TIF_flags
& TIF_INCLEANUP
))
1234 TRACE("Next Hook %p, %p\n", ptiHook
->rpdesk
, pdo
);
1238 if (ptiHook
!= pti
)
1241 if ( HookId
== WH_JOURNALPLAYBACK
|| // 1 | 0
1242 HookId
== WH_JOURNALRECORD
|| // 1 | 0
1243 HookId
== WH_KEYBOARD
|| // 1 | 200
1244 HookId
== WH_MOUSE
|| // 1 | 200
1245 HookId
== WH_KEYBOARD_LL
|| // 0 | 300
1246 HookId
== WH_MOUSE_LL
) // 0 | 300
1248 TRACE("\nGlobal Hook posting to another Thread! %d\n",HookId
);
1249 Result
= co_IntCallLowLevelHook(Hook
, Code
, wParam
, lParam
);
1253 { /* Make the direct call. */
1254 TRACE("Local Hook calling to Thread! %d\n",HookId
);
1255 Result
= co_IntCallHookProc( HookId
,
1265 UserDerefObjectCo(Hook
);
1267 ExFreePoolWithTag(pHookHandles
, TAG_HOOK
);
1268 TRACE("Ret: Global HookId %d Result 0x%x\n", HookId
,Result
);
1276 IntUnhookWindowsHook(int HookId
, HOOKPROC pfnFilterProc
)
1279 PLIST_ENTRY pLastHead
, pElement
;
1280 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
1282 if (HookId
< WH_MINHOOK
|| WH_MAXHOOK
< HookId
)
1284 EngSetLastError(ERROR_INVALID_HOOK_FILTER
);
1290 pLastHead
= &pti
->aphkStart
[HOOKID_TO_INDEX(HookId
)];
1292 pElement
= pLastHead
->Flink
;
1293 while (pElement
!= pLastHead
)
1295 Hook
= CONTAINING_RECORD(pElement
, HOOK
, Chain
);
1297 /* Get the next element now, we might free the hook in what follows */
1298 pElement
= Hook
->Chain
.Flink
;
1300 if (Hook
->Proc
== pfnFilterProc
)
1302 if (Hook
->head
.pti
== pti
)
1304 IntRemoveHook(Hook
);
1309 EngSetLastError(ERROR_ACCESS_DENIED
);
1319 * Support for compatibility only? Global hooks are processed in kernel space.
1320 * This is very thread specific! Never seeing applications with more than one
1321 * hook per thread installed. Most of the applications are Global hookers and
1322 * associated with just one hook Id. Maybe it's for diagnostic testing or a
1323 * throw back to 3.11?
1327 NtUserCallNextHookEx( int Code
,
1333 PHOOK HookObj
, NextObj
;
1334 PCLIENTINFO ClientInfo
;
1335 LRESULT lResult
= 0;
1336 DECLARE_RETURN(LRESULT
);
1338 TRACE("Enter NtUserCallNextHookEx\n");
1339 UserEnterExclusive();
1341 pti
= GetW32ThreadInfo();
1343 HookObj
= pti
->sphkCurrent
;
1345 if (!HookObj
) RETURN( 0);
1347 NextObj
= HookObj
->phkNext
;
1349 pti
->sphkCurrent
= NextObj
;
1350 ClientInfo
= pti
->pClientInfo
;
1353 ClientInfo
->phkCurrent
= NextObj
;
1355 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1361 /* Now in List run down. */
1362 if (ClientInfo
&& NextObj
)
1364 NextObj
->phkNext
= IntGetNextHook(NextObj
);
1365 lResult
= co_UserCallNextHookEx( NextObj
, Code
, wParam
, lParam
, NextObj
->Ansi
);
1370 TRACE("Leave NtUserCallNextHookEx, ret=%i\n",_ret_
);
1377 NtUserSetWindowsHookAW( int idHook
,
1382 UNICODE_STRING USModuleName
;
1384 RtlInitUnicodeString(&USModuleName
, NULL
);
1385 ThreadId
= PtrToUint(NtCurrentTeb()->ClientId
.UniqueThread
);
1387 return NtUserSetWindowsHookEx( NULL
,
1397 NtUserSetWindowsHookEx( HINSTANCE Mod
,
1398 PUNICODE_STRING UnsafeModuleName
,
1404 PWINSTATION_OBJECT WinStaObj
;
1406 UNICODE_STRING ModuleName
;
1409 PTHREADINFO pti
, ptiHook
= NULL
;
1410 DECLARE_RETURN(HHOOK
);
1412 TRACE("Enter NtUserSetWindowsHookEx\n");
1413 UserEnterExclusive();
1415 pti
= PsGetCurrentThreadWin32Thread();
1417 if (HookId
< WH_MINHOOK
|| WH_MAXHOOK
< HookId
)
1419 EngSetLastError(ERROR_INVALID_HOOK_FILTER
);
1425 EngSetLastError(ERROR_INVALID_FILTER_PROC
);
1429 if (ThreadId
) /* thread-local hook */
1431 if ( HookId
== WH_JOURNALRECORD
||
1432 HookId
== WH_JOURNALPLAYBACK
||
1433 HookId
== WH_KEYBOARD_LL
||
1434 HookId
== WH_MOUSE_LL
||
1435 HookId
== WH_SYSMSGFILTER
)
1437 ERR("Local hook installing Global HookId: %d\n",HookId
);
1438 /* these can only be global */
1439 EngSetLastError(ERROR_GLOBAL_ONLY_HOOK
);
1443 if ( !(ptiHook
= IntTID2PTI( (HANDLE
)ThreadId
)))
1445 ERR("Invalid thread id 0x%x\n", ThreadId
);
1446 EngSetLastError(ERROR_INVALID_PARAMETER
);
1450 if ( ptiHook
->rpdesk
!= pti
->rpdesk
) // gptiCurrent->rpdesk)
1452 ERR("Local hook wrong desktop HookId: %d\n",HookId
);
1453 EngSetLastError(ERROR_ACCESS_DENIED
);
1457 if (ptiHook
->ppi
!= pti
->ppi
)
1460 (HookId
== WH_GETMESSAGE
||
1461 HookId
== WH_CALLWNDPROC
||
1463 HookId
== WH_HARDWARE
||
1464 HookId
== WH_DEBUG
||
1465 HookId
== WH_SHELL
||
1466 HookId
== WH_FOREGROUNDIDLE
||
1467 HookId
== WH_CALLWNDPROCRET
) )
1469 ERR("Local hook needs hMod HookId: %d\n",HookId
);
1470 EngSetLastError(ERROR_HOOK_NEEDS_HMOD
);
1474 if ( (ptiHook
->TIF_flags
& (TIF_CSRSSTHREAD
|TIF_SYSTEMTHREAD
)) &&
1475 (HookId
== WH_GETMESSAGE
||
1476 HookId
== WH_CALLWNDPROC
||
1478 HookId
== WH_HARDWARE
||
1479 HookId
== WH_DEBUG
||
1480 HookId
== WH_SHELL
||
1481 HookId
== WH_FOREGROUNDIDLE
||
1482 HookId
== WH_CALLWNDPROCRET
) )
1484 EngSetLastError(ERROR_HOOK_TYPE_NOT_ALLOWED
);
1489 else /* System-global hook */
1491 ptiHook
= pti
; // gptiCurrent;
1493 (HookId
== WH_GETMESSAGE
||
1494 HookId
== WH_CALLWNDPROC
||
1496 HookId
== WH_SYSMSGFILTER
||
1497 HookId
== WH_HARDWARE
||
1498 HookId
== WH_DEBUG
||
1499 HookId
== WH_SHELL
||
1500 HookId
== WH_FOREGROUNDIDLE
||
1501 HookId
== WH_CALLWNDPROCRET
) )
1503 ERR("Global hook needs hMod HookId: %d\n",HookId
);
1504 EngSetLastError(ERROR_HOOK_NEEDS_HMOD
);
1509 Status
= IntValidateWindowStationHandle( PsGetCurrentProcess()->Win32WindowStation
,
1514 if (!NT_SUCCESS(Status
))
1516 SetLastNtError(Status
);
1519 ObDereferenceObject(WinStaObj
);
1521 Hook
= UserCreateObject(gHandleTable
, NULL
, ptiHook
, (PHANDLE
)&Handle
, TYPE_HOOK
, sizeof(HOOK
));
1528 Hook
->ihmod
= (INT
)Mod
; // Module Index from atom table, Do this for now.
1529 Hook
->HookId
= HookId
;
1530 Hook
->rpdesk
= ptiHook
->rpdesk
;
1531 Hook
->phkNext
= NULL
; /* Dont use as a chain! Use link lists for chaining. */
1532 Hook
->Proc
= HookProc
;
1535 TRACE("Set Hook Desk %p DeskInfo %p Handle Desk %p\n", pti
->rpdesk
, pti
->pDeskInfo
, Hook
->head
.rpdesk
);
1537 if (ThreadId
) /* Thread-local hook */
1539 InsertHeadList(&ptiHook
->aphkStart
[HOOKID_TO_INDEX(HookId
)], &Hook
->Chain
);
1540 ptiHook
->sphkCurrent
= NULL
;
1541 Hook
->ptiHooked
= ptiHook
;
1542 ptiHook
->fsHooks
|= HOOKID_TO_FLAG(HookId
);
1544 if (ptiHook
->pClientInfo
)
1546 if ( ptiHook
->ppi
== pti
->ppi
) /* gptiCurrent->ppi) */
1550 ptiHook
->pClientInfo
->fsHooks
= ptiHook
->fsHooks
;
1551 ptiHook
->pClientInfo
->phkCurrent
= NULL
;
1553 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1555 ERR("Problem writing to Local ClientInfo!\n");
1561 KeAttachProcess(&ptiHook
->ppi
->peProcess
->Pcb
);
1564 ptiHook
->pClientInfo
->fsHooks
= ptiHook
->fsHooks
;
1565 ptiHook
->pClientInfo
->phkCurrent
= NULL
;
1567 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
1569 ERR("Problem writing to Remote ClientInfo!\n");
1578 InsertHeadList(&ptiHook
->rpdesk
->pDeskInfo
->aphkStart
[HOOKID_TO_INDEX(HookId
)], &Hook
->Chain
);
1579 Hook
->ptiHooked
= NULL
;
1580 //gptiCurrent->pDeskInfo->fsHooks |= HOOKID_TO_FLAG(HookId);
1581 ptiHook
->rpdesk
->pDeskInfo
->fsHooks
|= HOOKID_TO_FLAG(HookId
);
1582 ptiHook
->sphkCurrent
= NULL
;
1583 ptiHook
->pClientInfo
->phkCurrent
= NULL
;
1586 RtlInitUnicodeString(&Hook
->ModuleName
, NULL
);
1590 Status
= MmCopyFromCaller(&ModuleName
,
1592 sizeof(UNICODE_STRING
));
1593 if (!NT_SUCCESS(Status
))
1595 IntRemoveHook(Hook
);
1596 SetLastNtError(Status
);
1600 Hook
->ModuleName
.Buffer
= ExAllocatePoolWithTag( PagedPool
,
1601 ModuleName
.MaximumLength
,
1603 if (NULL
== Hook
->ModuleName
.Buffer
)
1605 IntRemoveHook(Hook
);
1606 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1610 Hook
->ModuleName
.MaximumLength
= ModuleName
.MaximumLength
;
1611 Status
= MmCopyFromCaller( Hook
->ModuleName
.Buffer
,
1613 ModuleName
.MaximumLength
);
1614 if (!NT_SUCCESS(Status
))
1616 ExFreePoolWithTag(Hook
->ModuleName
.Buffer
, TAG_HOOK
);
1617 Hook
->ModuleName
.Buffer
= NULL
;
1618 IntRemoveHook(Hook
);
1619 SetLastNtError(Status
);
1623 Hook
->ModuleName
.Length
= ModuleName
.Length
;
1624 //// FIXME: Need to load from user32 to verify hMod before calling hook with hMod set!!!!
1625 //// Mod + offPfn == new HookProc Justin Case module is from another process.
1626 FIXME("NtUserSetWindowsHookEx Setting process hMod instance addressing.\n");
1627 /* Make proc relative to the module base */
1628 Hook
->offPfn
= (ULONG_PTR
)((char *)HookProc
- (char *)Mod
);
1633 TRACE("Installing: HookId %d Global %s\n", HookId
, !ThreadId
? "TRUE" : "FALSE");
1638 UserDereferenceObject(Hook
);
1639 TRACE("Leave NtUserSetWindowsHookEx, ret=%p\n", _ret_
);
1646 NtUserUnhookWindowsHookEx(HHOOK Hook
)
1649 DECLARE_RETURN(BOOL
);
1651 TRACE("Enter NtUserUnhookWindowsHookEx\n");
1652 UserEnterExclusive();
1654 if (!(HookObj
= IntGetHookObject(Hook
)))
1656 ERR("Invalid handle passed to NtUserUnhookWindowsHookEx\n");
1657 /* SetLastNtError(Status); */
1661 ASSERT(Hook
== UserHMGetHandle(HookObj
));
1663 IntRemoveHook(HookObj
);
1665 UserDereferenceObject(HookObj
);
1670 TRACE("Leave NtUserUnhookWindowsHookEx, ret=%i\n",_ret_
);
1677 NtUserRegisterUserApiHook(
1678 PUNICODE_STRING m_dllname1
,
1679 PUNICODE_STRING m_funname1
,
1684 UNICODE_STRING strDllNameSafe
;
1685 UNICODE_STRING strFuncNameSafe
;
1688 /* Probe and capture parameters */
1689 Status
= ProbeAndCaptureUnicodeString(&strDllNameSafe
, UserMode
, m_dllname1
);
1690 if(!NT_SUCCESS(Status
))
1692 EngSetLastError(RtlNtStatusToDosError(Status
));
1696 Status
= ProbeAndCaptureUnicodeString(&strFuncNameSafe
, UserMode
, m_funname1
);
1697 if(!NT_SUCCESS(Status
))
1699 ReleaseCapturedUnicodeString(&strDllNameSafe
, UserMode
);
1700 EngSetLastError(RtlNtStatusToDosError(Status
));
1704 UserEnterExclusive();
1706 /* Call internal function */
1707 ret
= UserRegisterUserApiHook(&strDllNameSafe
, &strFuncNameSafe
);
1711 /* Cleanup only in case of failure */
1714 ReleaseCapturedUnicodeString(&strDllNameSafe
, UserMode
);
1715 ReleaseCapturedUnicodeString(&strFuncNameSafe
, UserMode
);
1723 NtUserUnregisterUserApiHook(VOID
)
1727 UserEnterExclusive();
1728 ret
= UserUnregisterUserApiHook();