Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1894 lines
57 KiB

/****************************** Module Header ******************************\
* Module Name: hooks.c
*
* Copyright (c) 1985-91, Microsoft Corporation
*
* This module contains the user hook APIs and support routines.
*
* History:
* 01-28-91 DavidPe Created.
* 08 Feb 1992 IanJa Unicode/ANSI aware & neutral
\***************************************************************************/
#include "precomp.h"
#pragma hdrstop
/*
* This table is used to determine whether a particular hook
* can be set for the system or a task, and other hook-ID specific things.
*/
#define HKF_SYSTEM 0x01
#define HKF_TASK 0x02
#define HKF_JOURNAL 0x04 // JOURNAL the mouse on set
#define HKF_NZRET 0x08 // Always return NZ hook for <=3.0 compatibility
#define HKF_INTRSND 0x10 // OK to call hookproc in context of hooking thread
CONST int ampiHookError[CWINHOOKS] = {
0, // WH_MSGFILTER (-1)
0, // WH_JOURNALRECORD 0
-1, // WH_JOURNALPLAYBACK 1
0, // WH_KEYBOARD 2
0, // WH_GETMESSAGE 3
0, // WH_CALLWNDPROC 4
0, // WH_CBT 5
0, // WH_SYSMSGFILTER 6
0, // WH_MOUSE 7
0, // WH_HARDWARE 8
0, // WH_DEBUG 9
0, // WH_SHELL 10
0, // WH_FOREGROUNDIDLE 11
0 // WH_CALLWNDPROCRET 12
};
CONST BYTE abHookFlags[CWINHOOKS] = {
HKF_SYSTEM | HKF_TASK | HKF_NZRET , // WH_MSGFILTER (-1)
HKF_SYSTEM | HKF_JOURNAL | HKF_INTRSND , // WH_JOURNALRECORD 0
HKF_SYSTEM | HKF_JOURNAL | HKF_INTRSND , // WH_JOURNALPLAYBACK 1
HKF_SYSTEM | HKF_TASK | HKF_NZRET | HKF_INTRSND , // WH_KEYBOARD 2
HKF_SYSTEM | HKF_TASK , // WH_GETMESSAGE 3
HKF_SYSTEM | HKF_TASK , // WH_CALLWNDPROC 4
HKF_SYSTEM | HKF_TASK , // WH_CBT 5
HKF_SYSTEM , // WH_SYSMSGFILTER 6
HKF_SYSTEM | HKF_TASK | HKF_INTRSND , // WH_MOUSE 7
HKF_SYSTEM | HKF_TASK , // WH_HARDWARE 8
HKF_SYSTEM | HKF_TASK , // WH_DEBUG 9
HKF_SYSTEM | HKF_TASK , // WH_SHELL 10
HKF_SYSTEM | HKF_TASK , // WH_FOREGROUNDIDLE 11
HKF_SYSTEM | HKF_TASK // WH_CALLWNDPROCRET 12
};
void UnlinkHook(PHOOK phkFree);
/***************************************************************************\
* JournalAttach
*
* This attaches/detaches threads to one input queue so input is synchronized.
* Journalling requires this.
*
* 12-10-92 ScottLu Created.
\***************************************************************************/
BOOL JournalAttach(
PTHREADINFO pti,
BOOL fAttach)
{
PTHREADINFO ptiT;
PQ pq;
PLIST_ENTRY pHead, pEntry;
/*
* If we're attaching, calculate the pqAttach for all threads journalling.
* If we're unattaching, just call ReattachThreads() and it will calculate
* the non-journalling queues to attach to.
*/
if (fAttach) {
if ((pq = AllocQueue(pti, NULL)) == NULL)
return FALSE;
pHead = &pti->rpdesk->PtiList;
for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) {
ptiT = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink);
/*
* This is the Q to attach to for all threads that will do
* journalling.
*/
if (!(ptiT->TIF_flags & (TIF_DONTJOURNALATTACH | TIF_INCLEANUP))) {
ptiT->pqAttach = pq;
ptiT->pqAttach->cThreads++;
}
}
}
return ReattachThreads(fAttach);
}
/***************************************************************************\
* InterQueueMsgCleanup
*
* Walk gpsmsList looking for inter queue messages with a hung receiver;
* if one is found and it's a message that would have been an async event or
* intra queue if not journalling, then it cleans it up.
*
* While Journalling most threads are attached to the same queue. This causes
* activation and input stuff to be synchronous; if a thread hangs or dies,
* any other thread sending a message to the hung/dead thread will be
* blocked for good.
* This is critical when the blocked thread is cssr; this can happen with
* console windows or when some one requests a hard error box, specially
* during window activation.
*
* This function must be called when all queues have been detached (unless previously attached),
* so we can take care of hung/dead receivers with pending SMSs.
*
* 03-28-96 GerardoB Created
\***************************************************************************/
void InterQueueMsgCleanup (DWORD dwTimeFromLastRead)
{
PSMS *ppsms;
PSMS psmsNext;
CheckCritIn();
/*
* Walk gpsmsList
*/
for (ppsms = &gpsmsList; *ppsms; ) {
psmsNext = (*ppsms)->psmsNext;
/*
* If this is an inter queue message
*/
if (((*ppsms)->ptiSender != NULL)
&& ((*ppsms)->ptiReceiver != NULL)
&& ((*ppsms)->ptiSender->pq != (*ppsms)->ptiReceiver->pq)) {
/*
* If the receiver has been hung for a while
*/
if (FHungApp ((*ppsms)->ptiReceiver, dwTimeFromLastRead)) {
switch ((*ppsms)->message) {
/*
* Activation messages
*/
case WM_NCACTIVATE:
case WM_ACTIVATEAPP:
case WM_ACTIVATE:
case WM_SETFOCUS:
case WM_KILLFOCUS:
case WM_QUERYNEWPALETTE:
/*
* Sent to spwndFocus, which now can be in a different queue
*/
case WM_INPUTLANGCHANGE:
RIPMSG3 (RIP_WARNING, "InterQueueMsgCleanup: ptiSender:%#lx ptiReceiver:%#lx message:%#lx",
(*ppsms)->ptiSender, (*ppsms)->ptiReceiver, (*ppsms)->message);
ReceiverDied(*ppsms, ppsms);
break;
} /* switch */
} /* If hung receiver */
} /* If inter queue message */
/*
* If the message was not unlinked, go to the next one.
*/
if (*ppsms != psmsNext)
ppsms = &(*ppsms)->psmsNext;
} /* for */
}
/***************************************************************************\
* CancelJournalling
*
* Journalling is cancelled with control-escape is pressed, or when the desktop
* is switched.
*
* 01-27-93 ScottLu Created.
\***************************************************************************/
void CancelJournalling(void)
{
PTHREADINFO ptiCancelJournal;
PHOOK phook;
PHOOK phookNext;
/*
* Mouse buttons sometimes get stuck down due to hardware glitches,
* usually due to input concentrator switchboxes or faulty serial
* mouse COM ports, so clear the global button state here just in case,
* otherwise we may not be able to change focus with the mouse.
* Also do this in Alt-Tab processing.
*/
#ifdef DEBUG
if (wMouseOwnerButton)
RIPMSG1(RIP_WARNING,
"wMouseOwnerButton=%x, being cleared forcibly\n",
wMouseOwnerButton);
#endif
wMouseOwnerButton = 0;
/*
* Remove journal hooks. This'll cause threads to associate with
* different queues.
*/
phook = grpdeskRitInput->pDeskInfo->asphkStart[WH_JOURNALPLAYBACK + 1];
while (phook != NULL) {
ptiCancelJournal = phook->head.pti;
if (ptiCancelJournal != NULL) {
/*
* Let the thread that set the journal hook know this is happening.
*/
_PostThreadMessage(ptiCancelJournal, WM_CANCELJOURNAL, 0, 0);
/*
* If there was an app waiting for a response back from the journal
* application, cancel that request so the app can continue running
* (for example, we don't want winlogon or console to wait for an
* app that may be hung!)
*/
SendMsgCleanup(ptiCancelJournal);
}
phookNext = phook->sphkNext;
_UnhookWindowsHookEx(phook); // May free phook memory
phook = phookNext;
}
phook = grpdeskRitInput->pDeskInfo->asphkStart[WH_JOURNALRECORD + 1];
while (phook != NULL) {
ptiCancelJournal = phook->head.pti;
if (ptiCancelJournal != NULL) {
/*
* Let the thread that set the journal hook know this is happening.
*/
_PostThreadMessage(ptiCancelJournal, WM_CANCELJOURNAL, 0, 0);
/*
* If there was an app waiting for a response back from the journal
* application, cancel that request so the app can continue running
* (for example, we don't want winlogon or console to wait for an
* app that may be hung!)
*/
SendMsgCleanup(ptiCancelJournal);
}
phookNext = phook->sphkNext;
_UnhookWindowsHookEx(phook); // May free phook memory
phook = phookNext;
}
/*
* Make sure journalling ssync mode didn't hose any one
*/
InterQueueMsgCleanup(CMSWAITTOKILLTIMEOUT);
}
/***************************************************************************\
* _SetWindowsHookAW (API)
*
* This is the Win32 version of the SetWindowsHook() call. It has the
* same characteristics as far as return values, but only sets 'local'
* hooks. This is because we weren't provided a DLL we can load into
* other processes. Because of this WH_SYSMSGFILTER is no longer a
* valid hook. Apps will either need to call with WH_MSGFILTER or call
* the new API SetWindowsHookEx(). Essentially this API is obsolete and
* everyone should call SetWindowsHookEx().
*
* History:
* 10-Feb-1991 DavidPe Created.
* 30-Jan-1992 IanJa Added bAnsi parameter
\***************************************************************************/
PROC _SetWindowsHookAW(
int nFilterType,
PROC pfnFilterProc,
BOOL bAnsi)
{
PHOOK phk;
phk = _SetWindowsHookEx(NULL, NULL, PtiCurrent(),
nFilterType, pfnFilterProc, bAnsi);
/*
* If we get an error from SetWindowsHookEx() then we return
* 0xFFFFFFFF to be compatible with older version of Windows.
*/
if (phk == NULL) {
return (PROC)0xFFFFFFFF;
}
/*
* Handle the backwards compatibility return value cases for
* SetWindowsHook. If this was the first hook in the chain,
* then return NULL, else return something non-zero. HKF_NZRET
* is a special case where SetWindowsHook would always return
* something because there was a default hook installed. Some
* apps relied on a non-zero return value in those cases.
*/
if ((phk->sphkNext != NULL) || (abHookFlags[nFilterType + 1] & HKF_NZRET)) {
return (PROC)phk;
}
return NULL;
}
/***************************************************************************\
* _SetWindowsHookEx
*
* SetWindowsHookEx() is the updated version of SetWindowsHook(). It allows
* applications to set hooks on specific threads or throughout the entire
* system. The function returns a hook handle to the application if
* successful and NULL if a failure occured.
*
* History:
* 28-Jan-1991 DavidPe Created.
* 15-May-1991 ScottLu Changed to work client/server.
* 30-Jan-1992 IanJa Added bAnsi parameter
\***************************************************************************/
PHOOK _SetWindowsHookEx(
HANDLE hmod,
PUNICODE_STRING pstrLib,
PTHREADINFO ptiThread,
int nFilterType,
PROC pfnFilterProc,
BOOL bAnsi)
{
ACCESS_MASK amDesired;
HHOOK hhkNew;
PHOOK phkNew;
PHOOK *pphkStart;
PTHREADINFO ptiCurrent;
/*
* Check to see if filter type is valid.
*/
if ((nFilterType < WH_MIN) || (nFilterType > WH_MAX)) {
RIPERR0(ERROR_INVALID_HOOK_FILTER, RIP_VERBOSE, "");
return NULL;
}
/*
* Check to see if filter proc is valid.
*/
if (pfnFilterProc == NULL) {
RIPERR0(ERROR_INVALID_FILTER_PROC, RIP_VERBOSE, "");
return NULL;
}
ptiCurrent = PtiCurrent();
if (ptiThread == NULL) {
/*
* Is the app trying to set a global hook without a library?
* If so return an error.
*/
if (hmod == NULL) {
RIPERR0(ERROR_HOOK_NEEDS_HMOD, RIP_VERBOSE, "");
return NULL;
}
} else {
/*
* Is the app trying to set a local hook that is global-only?
* If so return an error.
*/
if (!(abHookFlags[nFilterType + 1] & HKF_TASK)) {
RIPERR0(ERROR_GLOBAL_ONLY_HOOK, RIP_VERBOSE, "");
return NULL;
}
/*
* Can't hook outside our own desktop.
*/
if (ptiThread->rpdesk != ptiCurrent->rpdesk) {
RIPERR0(ERROR_ACCESS_DENIED,
RIP_WARNING,
"Access denied to desktop in _SetWindowsHookEx - can't hook other desktops");
return NULL;
}
if (ptiCurrent->ppi != ptiThread->ppi) {
/*
* Is the app trying to set hook in another process without a library?
* If so return an error.
*/
if (hmod == NULL) {
RIPERR0(ERROR_HOOK_NEEDS_HMOD, RIP_VERBOSE, "");
return NULL;
}
/*
* Is the app hooking another user without access?
* If so return an error. Note that this check is done
* for global hooks every time the hook is called.
*/
if ((!RtlEqualLuid(&ptiThread->ppi->luidSession,
&ptiCurrent->ppi->luidSession)) &&
!(ptiThread->TIF_flags & TIF_ALLOWOTHERACCOUNTHOOK)) {
RIPERR0(ERROR_ACCESS_DENIED,
RIP_WARNING,
"Access denied to other user in _SetWindowsHookEx");
return NULL;
}
if ((ptiThread->TIF_flags & (TIF_CSRSSTHREAD | TIF_SYSTEMTHREAD)) &&
!(abHookFlags[nFilterType + 1] & HKF_INTRSND)) {
/*
* Can't hook console or GUI system thread if inter-thread
* calling isn't implemented for this hook type.
*/
RIPERR1(ERROR_HOOK_TYPE_NOT_ALLOWED,
RIP_WARNING,
"nFilterType (%ld) not allowed in _SetWindowsHookEx",
nFilterType);
return NULL;
}
}
}
/*
* Check if this thread has access to hook its desktop.
*/
switch( nFilterType ) {
case WH_JOURNALRECORD:
amDesired = DESKTOP_JOURNALRECORD;
break;
case WH_JOURNALPLAYBACK:
amDesired = DESKTOP_JOURNALPLAYBACK;
break;
default:
amDesired = DESKTOP_HOOKCONTROL;
break;
}
if (!RtlAreAllAccessesGranted(ptiCurrent->amdesk, amDesired)) {
RIPERR0(ERROR_ACCESS_DENIED,
RIP_WARNING,
"Access denied to desktop in _SetWindowsHookEx");
return NULL;
}
if (amDesired != DESKTOP_HOOKCONTROL &&
(ptiCurrent->rpdesk->rpwinstaParent->dwFlags & WSF_NOIO)) {
RIPERR0(ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION,
RIP_WARNING,
"Journal hooks invalid on a desktop belonging to a non-interactive WindowStation.");
return NULL;
}
#if 0
/*
* Is this a journal hook?
*/
if (abHookFlags[nFilterType + 1] & HKF_JOURNAL) {
/*
* Is a journal hook of this type already installed?
* If so it's an error.
*/
if (ptiCurrent->pDeskInfo->asphkStart[nFilterType + 1] != NULL) {
RIPERR0(ERROR_JOURNAL_HOOK_SET, RIP_VERBOSE, "");
return NULL;
}
}
#endif
/*
* Allocate the new HOOK structure.
*/
phkNew = (PHOOK)HMAllocObject(ptiCurrent, ptiCurrent->rpdesk,
TYPE_HOOK, sizeof(HOOK));
if (phkNew == NULL) {
return NULL;
}
/*
* If a DLL is required for this hook, register the library with
* the library management routines so we can assure it's loaded
* into all the processes necessary.
*/
phkNew->ihmod = -1;
if (hmod != NULL) {
#if defined(WX86)
if (Wx86CurrentTib()) {
NTSTATUS Status = STATUS_SUCCESS;
try {
if (RtlImageNtHeader(hmod)->FileHeader.Machine == IMAGE_FILE_MACHINE_I386) {
phkNew->flags |= HF_WX86KNOWNDLL;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
RIPMSG2(RIP_WARNING,
"Wx86Hooks: hmod = %x ExCode %x\n",
hmod,
Status);
HMFreeObject((PVOID)phkNew);
}
if (!NT_SUCCESS(Status)) {
return NULL;
}
}
#endif
phkNew->ihmod = GetHmodTableIndex(pstrLib);
if (phkNew->ihmod == -1) {
RIPERR0(ERROR_MOD_NOT_FOUND, RIP_VERBOSE, "");
HMFreeObject((PVOID)phkNew);
return NULL;
}
/*
* Add a dependency on this module - meaning, increment a count
* that simply counts the number of hooks set into this module.
*/
if (phkNew->ihmod >= 0) {
AddHmodDependency(phkNew->ihmod);
}
}
/*
* Depending on whether we're setting a global or local hook,
* get the start of the appropriate linked-list of HOOKs. Also
* set the HF_GLOBAL flag if it's a global hook.
*/
if (ptiThread != NULL) {
pphkStart = &ptiThread->asphkStart[nFilterType + 1];
/*
* Set the WHF_* in the THREADINFO so we know it's hooked.
*/
ptiThread->fsHooks |= WHF_FROM_WH(nFilterType);
/*
* Set the flags in the thread's TEB
*/
if (ptiThread->pClientInfo) {
BOOL fAttached;
/*
* If the thread being hooked is in another process, attach
* to that process.
*/
if (ptiThread->ppi != ptiCurrent->ppi) {
KeAttachProcess(&ptiThread->ppi->Process->Pcb);
fAttached = TRUE;
} else
fAttached = FALSE;
ptiThread->pClientInfo->fsHooks = ptiThread->fsHooks;
if (fAttached)
KeDetachProcess();
}
/*
* Remember which thread we're hooking.
*/
phkNew->ptiHooked = ptiThread;
} else {
pphkStart = &ptiCurrent->pDeskInfo->asphkStart[nFilterType + 1];
phkNew->flags |= HF_GLOBAL;
/*
* Set the WHF_* in the SERVERINFO so we know it's hooked.
*/
ptiCurrent->pDeskInfo->fsHooks |= WHF_FROM_WH(nFilterType);
phkNew->ptiHooked = NULL;
}
/*
* Does the hook function expect ANSI or Unicode text?
*/
if (bAnsi) {
phkNew->flags |= HF_ANSI;
}
/*
* Initialize the HOOK structure. Unreferenced parameters are assumed
* to be initialized to zero by LocalAlloc().
*/
phkNew->iHook = nFilterType;
/*
* Libraries are loaded at different linear addresses in different
* process contexts. For this reason, we need to convert the filter
* proc address into an offset while setting the hook, and then convert
* it back to a real per-process function pointer when calling a
* hook. Do this by subtracting the 'hmod' (which is a pointer to the
* linear and contiguous .exe header) from the function index.
*/
phkNew->offPfn = ((DWORD)pfnFilterProc) - ((DWORD)hmod);
#ifdef HOOKBATCH
phkNew->cEventMessages = 0;
phkNew->iCurrentEvent = 0;
phkNew->CacheTimeOut = 0;
phkNew->aEventCache = NULL;
#endif //HOOKBATCH
/*
* Link this hook into the front of the hook-list.
*/
Lock(&phkNew->sphkNext, *pphkStart);
Lock(pphkStart, phkNew);
/*
* If this is a journal hook, setup synchronized input processing
* AFTER we set the hook - so this synchronization can be cancelled
* with control-esc.
*/
if (abHookFlags[nFilterType + 1] & HKF_JOURNAL) {
/*
* Attach everyone to us so journal-hook processing
* will be synchronized.
*/
hhkNew = PtoHq(phkNew);
if (!JournalAttach(ptiCurrent, TRUE)) {
_UnhookWindowsHookEx(phkNew);
}
phkNew = (PHOOK)HMValidateHandleNoRip(hhkNew, TYPE_HOOK);
}
if (abHookFlags[nFilterType + 1] & HKF_JOURNAL) {
/*
* If we're changing the journal hooks, jiggle the mouse.
* This way the first event will always be a mouse move, which
* will ensure that the cursor is set properly.
*/
SetFMouseMoved();
}
/*
* Can't allow a process that has set a global hook that works
* on server-side winprocs to run at background priority!
*/
if (phkNew != NULL &&
phkNew->flags & HF_GLOBAL &&
(abHookFlags[nFilterType + 1] & HKF_INTRSND) &&
ptiCurrent->Thread && THREAD_TO_PROCESS(ptiCurrent->Thread)) {
ptiCurrent->TIF_flags |= TIF_GLOBALHOOKER;
//
// Now that bg/fg is really just a quantum issue, this is not needed !
//
//
}
/*
* Return pointer to our internal hook structure so we know
* which hook to call next in CallNextHookEx().
*/
return phkNew;
}
/***************************************************************************\
* xxxCallNextHookEx
*
* In the new world DefHookProc() is a bit deceptive since SetWindowsHook()
* isn't returning the actual address of the next hook to call, but instead
* a hook handle. CallNextHookEx() is a slightly clearer picture of what's
* going on so apps don't get tempted to try and call the value we return.
*
* As a side note we don't actually use the hook handle passed in. We keep
* track of which hooks is currently being called on a thread in the Q
* structure and use that. This is because SetWindowsHook() will sometimes
* return NULL to be compatible with the way it used to work, but even though
* we may be dealing with the last 'local' hook, there may be further 'global'
* hooks we need to call. PhkNext() is smart enough to jump over to the
* 'global' hook chain if it reaches the end of the 'local' hook chain.
*
* History:
* 01-30-91 DavidPe Created.
\***************************************************************************/
DWORD xxxCallNextHookEx(
int nCode,
DWORD wParam,
DWORD lParam)
{
BOOL bAnsiHook;
if (PtiCurrent()->sphkCurrent == NULL) {
return 0;
}
return xxxCallHook2(_PhkNext(PtiCurrent()->sphkCurrent), nCode, wParam, lParam, &bAnsiHook);
}
/***************************************************************************\
* CheckWHFBits
*
* This routine checks to see if any hooks for nFilterType exist, and clear
* the appropriate WHF_ in the THREADINFO and SERVERINFO.
*
* History:
* 08-17-92 DavidPe Created.
\***************************************************************************/
VOID CheckWHFBits(
PTHREADINFO pti,
int nFilterType)
{
if (pti->asphkStart[nFilterType + 1] == NULL) {
pti->fsHooks &= ~(WHF_FROM_WH(nFilterType));
/*
* Set the flags in the thread's TEB
*/
if (pti->pClientInfo) {
BOOL fAttached;
/*
* If the hooked thread is in another process, attach
* to that process.
*/
if (pti->ppi != PpiCurrent()) {
KeAttachProcess(&pti->ppi->Process->Pcb);
fAttached = TRUE;
} else
fAttached = FALSE;
pti->pClientInfo->fsHooks = pti->fsHooks;
if (fAttached)
KeDetachProcess();
}
}
if (pti->pDeskInfo->asphkStart[nFilterType + 1] == NULL) {
pti->pDeskInfo->fsHooks &= ~(WHF_FROM_WH(nFilterType));
}
}
/***************************************************************************\
* _UnhookWindowsHook (API)
*
* This is the old version of the Unhook API. It does the same thing as
* UnhookWindowsHookEx(), but takes a filter-type and filter-proc to
* identify which hook to unhook.
*
* History:
* 01-28-91 DavidPe Created.
\***************************************************************************/
BOOL _UnhookWindowsHook(
int nFilterType,
PROC pfnFilterProc)
{
PHOOK phk;
PTHREADINFO pti;
if ((nFilterType < WH_MIN) || (nFilterType > WH_MAX)) {
RIPERR0(ERROR_INVALID_HOOK_FILTER, RIP_VERBOSE, "");
return FALSE;
}
pti = PtiCurrent();
for (phk = PhkFirst(pti, nFilterType); phk != NULL; phk = _PhkNext(phk)) {
/*
* Is this the hook we're looking for?
*/
if (PFNHOOK(phk) == pfnFilterProc) {
/*
* Are we on the thread that set the hook?
* If not return an error.
*/
if (GETPTI(phk) != pti) {
RIPERR0(ERROR_ACCESS_DENIED,
RIP_WARNING,
"Access denied in _UnhookWindowsHook: "
"this thread is not the same as that which set the hook");
return FALSE;
}
return _UnhookWindowsHookEx( phk );
}
}
/*
* Didn't find the hook we were looking for so return FALSE.
*/
RIPERR0(ERROR_HOOK_NOT_INSTALLED, RIP_VERBOSE, "");
return FALSE;
}
/***************************************************************************\
* _UnhookWindowsHookEx (API)
*
* Applications call this API to 'unhook' a hook. First we check if someone
* is currently calling this hook. If no one is we go ahead and free the
* HOOK structure now. If someone is then we simply clear the filter-proc
* in the HOOK structure. In xxxCallHook2() we check for this and if by
* that time no one is calling the hook in question we free it there.
*
* History:
* 01-28-91 DavidPe Created.
\***************************************************************************/
BOOL _UnhookWindowsHookEx(
PHOOK phkFree)
{
PTHREADINFO pti;
pti = GETPTI(phkFree);
/*
* Clear the journaling flags in all the queues.
*/
if (abHookFlags[phkFree->iHook + 1] & HKF_JOURNAL) {
JournalAttach(pti, FALSE);
/*
* If someone got stuck because of the hook, let him go
*
* I want to get some performance numbers before checking this in.
* MSTest hooks and unhooks all the time when running a script.
* This code has never been in. 5/22/96. GerardoB
*/
// InterQueueMsgCleanup(3 * CMSWAITTOKILLTIMEOUT);
}
/*
* If no one is currently calling this hook,
* go ahead and free it now.
*/
FreeHook(phkFree);
/*
* If this thread has no more global hooks that are able to hook
* server-side window procs, we must clear it's TIF_GLOBALHOOKER bit.
*/
if (pti->TIF_flags & TIF_GLOBALHOOKER) {
int iHook;
PHOOK phk;
for (iHook = WH_MIN ; iHook <= WH_MAX ; ++iHook) {
/*
* Ignore those that can't hook server-side winprocs
*/
if (!(abHookFlags[iHook + 1] & HKF_INTRSND)) {
continue;
}
/*
* Scan the global hooks
*/
for (phk = pti->pDeskInfo->asphkStart[iHook + 1];
phk != NULL; phk = _PhkNext(phk)) {
if (GETPTI(phk) == pti) {
goto StillHasGlobalHooks;
}
}
}
pti->TIF_flags &= ~TIF_GLOBALHOOKER;
}
StillHasGlobalHooks:
/*
* Success, return TRUE.
*/
return TRUE;
}
/***************************************************************************\
* _CallMsgFilter (API)
*
* CallMsgFilter() allows applications to call the WH_*MSGFILTER hooks.
* If there's a sysmodal window we return FALSE right away. WH_MSGFILTER
* isn't called if WH_SYSMSGFILTER returned non-zero.
*
* History:
* 01-29-91 DavidPe Created.
\***************************************************************************/
BOOL _CallMsgFilter(
LPMSG pmsg,
int nCode)
{
PTHREADINFO pti;
pti = PtiCurrent();
/*
* First call WH_SYSMSGFILTER. If it returns non-zero, don't
* bother calling WH_MSGFILTER, just return TRUE. Otherwise
* return what WH_MSGFILTER gives us.
*/
if (IsHooked(pti, WHF_SYSMSGFILTER) && xxxCallHook(nCode, 0, (DWORD)pmsg,
WH_SYSMSGFILTER)) {
return TRUE;
}
if (IsHooked(pti, WHF_MSGFILTER)) {
return (BOOL)xxxCallHook(nCode, 0, (DWORD)pmsg, WH_MSGFILTER);
}
return FALSE;
}
/***************************************************************************\
* xxxCallHook
*
* User code calls this function to call the first hook of a specific
* type.
*
* History:
* 01-29-91 DavidPe Created.
\***************************************************************************/
int xxxCallHook(
int nCode,
DWORD wParam,
DWORD lParam,
int iHook)
{
BOOL bAnsiHook;
return xxxCallHook2(PhkFirst(PtiCurrent(), iHook), nCode, wParam, lParam, &bAnsiHook);
}
/***************************************************************************\
* xxxCallHook2
*
* When you have an actual HOOK structure to call, you'd use this function.
* It will check to see if the hook hasn't already been unhooked, and if
* is it will free it and keep looking until it finds a hook it can call
* or hits the end of the list. We also make sure any needed DLLs are loaded
* here. We also check to see if the HOOK was unhooked inside the call
* after we return.
*
* Note: Hooking server-side window procedures (such as the desktop and console
* windows) can only be done by sending the hook message to the hooking app.
* (This is because we must not load the hookproc DLL into the server process).
* The hook types this can be done with are currently WH_JOURNALRECORD,
* WH_JOURNALPLAYBACK, WH_KEYBOARD and WH_MOUSE : these are all marked as
* HKF_INTRSND. In order to prevent a global hooker from locking up the whole
* system, the hook message is sent with a timeout. To ensure minimal
* performance degradation, the hooker process is set to foreground priority,
* and prevented from being set back to background priority with the
* TIF_GLOBALHOOKER bit in hooking thread's pti->flags.
* Hooking emulated DOS apps is prevented with the TIF_DOSEMULATOR bit in the
* console thread: this is because these apps typically hog the CPU so much that
* the hooking app does not respond rapidly enough to the hook messsages sent
* to it. IanJa Nov 1994.
*
* History:
* 02-07-91 DavidPe Created.
* 1994 Nov 02 IanJa Hooking desktop and console windows.
\***************************************************************************/
int xxxCallHook2(
PHOOK phkCall,
int nCode,
DWORD wParam,
DWORD lParam,
LPBOOL lpbAnsiHook)
{
UINT iHook;
PHOOK phkSave;
int nRet;
PTHREADINFO ptiCurrent;
BOOL fLoadSuccess;
TL tlphkCall;
TL tlphkSave;
BYTE bHookFlags;
if (phkCall == NULL) {
return 0;
}
iHook = phkCall->iHook;
ptiCurrent = PtiCurrent();
/*
* If this queue is in cleanup, exit: it has no business calling back
* a hook proc. Also check if hooks are disabled for the thread.
*/
if ( ptiCurrent->TIF_flags & (TIF_INCLEANUP | TIF_DISABLEHOOKS) ||
ptiCurrent->rpdesk == NULL) {
return ampiHookError[iHook + 1];
}
/*
* Now check to see if we really want to call this hook.
* If not, keep going through the list until we either
* find an 'good' hook or hit the end of the lists.
*/
tryagain:
while (phkCall != NULL) {
*lpbAnsiHook = phkCall->flags & HF_ANSI;
bHookFlags = abHookFlags[phkCall->iHook + 1];
/*
* Some WH_SHELL hook types can be called from console
*/
if ((phkCall->iHook == WH_SHELL) && (ptiCurrent->TIF_flags & TIF_CSRSSTHREAD)) {
if ((nCode == HSHELL_LANGUAGE) || (nCode == HSHELL_WINDOWACTIVATED)) {
bHookFlags |= HKF_INTRSND;
}
}
if (!(bHookFlags & HKF_INTRSND)) {
if (
/*
* If this hook was set by a WOW app, and we're not on a
* 16-bit WOW thread, and this isn't a intersend hook,
* don't call it.
*
* For seperate WOW VDMs we don't let 16-bit hooks go to another
* VDM or process. (we could do an intersend like journal hooks but
* that might break some hooking apps that expect to share data)
*/
(GETPTI(phkCall)->TIF_flags & TIF_16BIT &&
(!(ptiCurrent->TIF_flags & TIF_16BIT) ||
ptiCurrent->ppi != GETPTI(phkCall)->ppi))
/*
* If this is a global and non-journal hook, do a security
* check on the current desktop to see if we can call here.
*/
||
(phkCall->flags & HF_GLOBAL &&
!RtlEqualLuid(&GETPTI(phkCall)->ppi->luidSession, &ptiCurrent->ppi->luidSession) &&
!(ptiCurrent->TIF_flags & TIF_ALLOWOTHERACCOUNTHOOK))
/*
* Don't allow this hook to be called for console or system threads
* by another process if it can't be done via an inter-thread call.
*/
||
(ptiCurrent->TIF_flags & (TIF_CSRSSTHREAD | TIF_SYSTEMTHREAD) &&
GETPTI(phkCall)->ppi != ptiCurrent->ppi))
{
phkCall = _PhkNext(phkCall);
continue;
}
}
/*
* This hook is fine, go ahead and call it.
*/
break;
}
/*
* Make sure that we did find a hook to call.
*/
if (phkCall == NULL) {
return ampiHookError[iHook + 1];
}
/*
* We're calling back... make sure the hook doesn't go away while
* we're calling back. We've thread locked here: we must unlock before
* returning or enumerating the next hook in the chain.
*/
ThreadLockAlwaysWithPti(ptiCurrent, phkCall, &tlphkCall);
/*
* If the hooker is a 16bit app and we are not in a 16 bit apps context,
* then run the hook in the destination's context.
* ((GETPTI(phkCall)->TIF_flags & TIF_16BIT) &&
* ((ptiCurrent->TIF_flags & TIF_16BIT)==0)))) {
*/
/*
* If the hooker & hooked threads are not the same we nust make an
* inter-thread call if:
* (1) it is a journal hook
* OR
* (2) it is an INTRSND hook on a server thread.
* OR
* (3) it is the console and we want to update the keyboard layout icon on the tray.
*
* (otherwise just call the hookproc in a DLL linked into the hooked app.)
*/
if ((GETPTI(phkCall) != ptiCurrent) &&
((bHookFlags & HKF_JOURNAL) ||
((bHookFlags & HKF_INTRSND) &&
(ptiCurrent->TIF_flags & (TIF_CSRSSTHREAD | TIF_SYSTEMTHREAD))))) {
/*
* Receiving thread can access this structure since the
* sender thread's stack is locked down during xxxInterSendMsgEx
*/
HOOKMSGSTRUCT hkmp;
hkmp.lParam = lParam;
hkmp.phk = phkCall;
hkmp.nCode = nCode;
/*
* Thread lock right away in case the lock frees the previous contents
*/
phkSave = ptiCurrent->sphkCurrent;
ThreadLockWithPti(ptiCurrent, phkSave, &tlphkSave);
Lock(&ptiCurrent->sphkCurrent, phkCall);
if (ptiCurrent->pClientInfo)
ptiCurrent->pClientInfo->phkCurrent = phkCall;
/*
* Make sure we don't get hung!
*/
if ((bHookFlags & HKF_JOURNAL) ||
!(ptiCurrent->TIF_flags & (TIF_CSRSSTHREAD | TIF_SYSTEMTHREAD))) {
nRet = xxxInterSendMsgEx(NULL, WM_HOOKMSG, wParam,
(DWORD)&hkmp, ptiCurrent, GETPTI(phkCall), NULL);
} else {
/*
* We are a server thread (console/desktop) and we aren't
* journalling, so we can't allow the hookproc to hang us -
* we must use a timeout.
*/
INTRSENDMSGEX ism;
ism.fuCall = ISM_TIMEOUT;
ism.fuSend = SMTO_ABORTIFHUNG | SMTO_NORMAL;
ism.uTimeout = 200; // 1/5 second!!!
ism.lpdwResult = &nRet;
/*
* Don't hook DOS apps connected to the emulator - they often
* grab too much CPU for the callback to the hookproc to
* complete in a timely fashion, causing poor response.
*/
if ((ptiCurrent->TIF_flags & TIF_DOSEMULATOR) ||
!xxxInterSendMsgEx(NULL, WM_HOOKMSG, wParam,
(DWORD)&hkmp, ptiCurrent, GETPTI(phkCall), &ism)) {
nRet = ampiHookError[iHook + 1];
}
}
Lock(&ptiCurrent->sphkCurrent, phkSave);
if (ptiCurrent->pClientInfo)
ptiCurrent->pClientInfo->phkCurrent = phkSave;
ThreadUnlock(&tlphkSave);
ThreadUnlock(&tlphkCall);
return nRet;
}
#if 0 // now redundant
/*
* If we're trying to call the hook from a console thread (or server
* thread), then fail it. This is new functionality: means console apps
* don't call any hooks except for journal hooks. This is because we
* can't load the .dll in the server, and we can't talk synchronously
* with any process that may be hung or really really slow because console
* windows would hang.
*
* Allow the console to call the hook if it set the hook.
*/
if (ptiCurrent->hThreadClient == ptiCurrent->hThreadServer &&
phkCall->head.pti != ptiCurrent) {
ThreadUnlock(&tlphkCall);
return ampiHookError[iHook + 1];
}
#endif
/*
* Make sure the DLL for this hook, if any, has been loaded
* for the current process.
*/
if ((phkCall->ihmod != -1) &&
(TESTHMODLOADED(ptiCurrent, phkCall->ihmod) == 0)) {
BOOL bWx86KnownDll;
/*
* Try loading the library, since it isn't loaded in this processes
* context. First lock this hook so it doesn't go away while we're
* loading this library.
*/
bWx86KnownDll = (phkCall->flags & HF_WX86KNOWNDLL) != 0;
fLoadSuccess = (xxxLoadHmodIndex(phkCall->ihmod, bWx86KnownDll) != NULL);
/*
* If the LoadLibrary() failed, skip to the next hook and try
* again.
*/
if (!fLoadSuccess) {
phkCall = _PhkNext(phkCall);
ThreadUnlock(&tlphkCall);
goto tryagain;
}
}
/*
* Is WH_DEBUG installed? If we're not already calling it, do so.
*/
if (IsHooked(ptiCurrent, WHF_DEBUG) && (phkCall->iHook != WH_DEBUG)) {
DEBUGHOOKINFO debug;
debug.idThread = (DWORD)ptiCurrent->Thread->Cid.UniqueThread;
debug.idThreadInstaller = 0;
debug.code = nCode;
debug.wParam = wParam;
debug.lParam = lParam;
if (xxxCallHook(HC_ACTION, phkCall->iHook, (DWORD)&debug, WH_DEBUG)) {
/*
* If WH_DEBUG returned non-zero, skip this hook and
* try the next one.
*/
phkCall = _PhkNext(phkCall);
ThreadUnlock(&tlphkCall);
goto tryagain;
}
}
/*
* Make sure the hook is still around before we
* try and call it.
*/
if (HMIsMarkDestroy(phkCall)) {
phkCall = _PhkNext(phkCall);
ThreadUnlock(&tlphkCall);
goto tryagain;
}
/*
* Time to call the hook! Lock it first so that it doesn't go away
* while we're using it. Thread lock right away in case the lock frees
* the previous contents.
*/
phkSave = ptiCurrent->sphkCurrent;
ThreadLockWithPti(ptiCurrent, phkSave, &tlphkSave);
Lock(&ptiCurrent->sphkCurrent, phkCall);
if (ptiCurrent->pClientInfo)
ptiCurrent->pClientInfo->phkCurrent = phkCall;
nRet = xxxHkCallHook(phkCall, nCode, wParam, lParam);
Lock(&ptiCurrent->sphkCurrent, phkSave);
if (ptiCurrent->pClientInfo)
ptiCurrent->pClientInfo->phkCurrent = phkSave;
ThreadUnlock(&tlphkSave);
/*
* This hook proc faulted, unhook it and find the next hook.
*/
if (phkCall->flags & HF_HOOKFAULTED) {
PHOOK phkFault = phkCall;
phkCall = _PhkNext(phkCall);
phkFault = ThreadUnlock(&tlphkCall);
if (phkFault != NULL) {
FreeHook(phkFault);
}
goto tryagain;
}
/*
* Lastly, we're done with this hook so it is ok to unlock it (it may
* get freed here!
*/
ThreadUnlock(&tlphkCall);
return nRet;
}
/***************************************************************************\
* xxxCallMouseHook
*
* This is a helper routine that packages up a MOUSEHOOKSTRUCT and calls
* the WH_MOUSE hook.
*
* History:
* 02-09-91 DavidPe Created.
\***************************************************************************/
BOOL xxxCallMouseHook(
UINT message,
PMOUSEHOOKSTRUCT pmhs,
BOOL fRemove)
{
BOOL bAnsiHook;
/*
* Call the mouse hook.
*/
if (xxxCallHook2(PhkFirst(PtiCurrent(), WH_MOUSE), fRemove ?
HC_ACTION : HC_NOREMOVE, (DWORD)message, (DWORD)pmhs, &bAnsiHook)) {
return TRUE;
}
return FALSE;
}
/***************************************************************************\
* xxxCallJournalRecordHook
*
* This is a helper routine that packages up an EVENTMSG and calls
* the WH_JOURNALRECORD hook.
*
* History:
* 02-28-91 DavidPe Created.
\***************************************************************************/
void xxxCallJournalRecordHook(
PQMSG pqmsg)
{
EVENTMSG emsg;
BOOL bAnsiHook;
/*
* Setup the EVENTMSG structure.
*/
emsg.message = pqmsg->msg.message;
emsg.time = pqmsg->msg.time;
if (RevalidateHwnd(pqmsg->msg.hwnd)) {
emsg.hwnd = pqmsg->msg.hwnd;
} else {
emsg.hwnd = NULL;
}
if ((emsg.message >= WM_MOUSEFIRST) && (emsg.message <= WM_MOUSELAST)) {
emsg.paramL = (UINT)pqmsg->msg.pt.x;
emsg.paramH = (UINT)pqmsg->msg.pt.y;
} else if ((emsg.message >= WM_KEYFIRST) && (emsg.message <= WM_KEYLAST)) {
/*
* Build up a Win 3.1 compatible journal record key
* Win 3.1 ParamL 00 00 SC VK (SC=scan code VK=virtual key)
* Also set ParamH 00 00 00 SC to be compatible with our Playback
*
* If WM_*CHAR messages ever come this way we would have a problem
* because we would lose the top byte of the Unicode character. We'd
* We'd get ParamL 00 00 SC CH (SC=scan code, CH = low byte of WCHAR)
*
*/
emsg.paramL =
MAKELONG(MAKEWORD(pqmsg->msg.wParam, HIWORD(pqmsg->msg.lParam)),0);
emsg.paramH = LOBYTE(HIWORD(pqmsg->msg.lParam));
UserAssert((emsg.message != WM_CHAR) &&
(emsg.message != WM_DEADCHAR) &&
(emsg.message != WM_SYSCHAR) &&
(emsg.message != WM_SYSDEADCHAR));
/*
* Set extended-key bit.
*/
if (pqmsg->msg.lParam & 0x01000000) {
emsg.paramH |= 0x8000;
}
} else {
RIPMSG2(RIP_WARNING,
"Bad journal record message!\n"
" message = 0x%08lx\n"
" dwQEvent = 0x%08lx",
pqmsg->msg.message,
pqmsg->dwQEvent);
}
/*
* Call the journal recording hook.
*/
xxxCallHook2(PhkFirst(PtiCurrent(), WH_JOURNALRECORD), HC_ACTION, 0,
(DWORD)&emsg, &bAnsiHook);
/*
* Write the MSG parameters back because the app may have modified it.
* AfterDark's screen saver password actually zero's out the keydown
* chars.
*
* If it was a mouse message patch up the mouse point. If it was a
* WM_KEYxxx message convert the Win 3.1 compatible journal record key
* back into a half backed WM_KEYxxx format. Only the VK and SC fields
* where initialized at this point.
*
* wParam 00 00 00 VK lParam 00 SC 00 00
*/
if ((pqmsg->msg.message >= WM_MOUSEFIRST) && (pqmsg->msg.message <= WM_MOUSELAST)) {
pqmsg->msg.pt.x = emsg.paramL;
pqmsg->msg.pt.y = emsg.paramH;
} else if ((pqmsg->msg.message >= WM_KEYFIRST) && (pqmsg->msg.message <= WM_KEYLAST)) {
(BYTE)pqmsg->msg.wParam = (BYTE)emsg.paramL;
((PBYTE)&pqmsg->msg.lParam)[2] = HIBYTE(LOWORD(emsg.paramL));
}
}
/***************************************************************************\
* xxxCallJournalPlaybackHook
*
*
* History:
* 03-01-91 DavidPe Created.
\***************************************************************************/
DWORD xxxCallJournalPlaybackHook(
PQMSG pqmsg)
{
EVENTMSG emsg;
LONG dt;
PWND pwnd;
DWORD wParam;
LONG lParam;
POINT pt;
PTHREADINFO pti;
BOOL bAnsiHook;
PHOOK phkCall;
TL tlphkCall;
TryNextEvent:
/*
* Initialized to the current time for compatibility with
* <= 3.0.
*/
emsg.time = NtGetTickCount();
pti = PtiCurrent();
pwnd = NULL;
phkCall = PhkFirst(pti, WH_JOURNALPLAYBACK);
ThreadLockWithPti(pti, phkCall, &tlphkCall);
dt = (DWORD)xxxCallHook2(phkCall, HC_GETNEXT, 0, (DWORD)&emsg, &bAnsiHook);
/*
* -1 means some error occured. Return -1 for error.
*/
if (dt == 0xFFFFFFFF) {
ThreadUnlock(&tlphkCall);
return dt;
}
/*
* Update the message id. Need this if we decide to sleep.
*/
pqmsg->msg.message = emsg.message;
if (dt > 0) {
if (pti->TIF_flags & TIF_IGNOREPLAYBACKDELAY) {
/*
* This flag tells us to ignore the requested delay (set in mnloop)
* We clear it to indicate that we did so.
*/
RIPMSG1(RIP_WARNING, "Journal Playback delay ignored (%lx)", emsg.message);
pti->TIF_flags &= ~TIF_IGNOREPLAYBACKDELAY;
dt = 0;
} else {
ThreadUnlock(&tlphkCall);
return dt;
}
}
/*
* The app is ready to be asked for the next event
*/
if ((emsg.message >= WM_MOUSEFIRST) && (emsg.message <= WM_MOUSELAST)) {
pt.x = (int)emsg.paramL;
pt.y = (int)emsg.paramH;
lParam = MAKELONG(LOWORD(pt.x), LOWORD(pt.y));
wParam = 0;
/*
* If the message has changed the mouse position,
* update the cursor.
*/
if ((pt.x != ptCursor.x) || (pt.y != ptCursor.y)) {
InternalSetCursorPos(pt.x, pt.y, grpdeskRitInput);
}
} else if ((emsg.message >= WM_KEYFIRST) && (emsg.message <= WM_KEYLAST)) {
UINT wExtraStuff = 0;
if ((emsg.message == WM_KEYUP) || (emsg.message == WM_SYSKEYUP)) {
wExtraStuff |= 0x8000;
}
if ((emsg.message == WM_SYSKEYUP) || (emsg.message == WM_SYSKEYDOWN)) {
wExtraStuff |= 0x2000;
}
if (emsg.paramH & 0x8000) {
wExtraStuff |= 0x0100;
}
if (TestKeyStateDown(pti->pq, (BYTE)emsg.paramL)) {
wExtraStuff |= 0x4000;
}
/*
* There are old ANSI apps that only fill in the byte for when
* they generate journal playback so we used to strip everything
* else off. That however breaks unicode journalling; 22645
* (Yes, some apps apparently do Playback WM_*CHAR msgs!)
*
#ifdef FE_SB // xxxCallJournalPlaybackHook()
*
* LATER : DBCS handling is needed for WM_CHAR...
*
#endif // FE_SB
*
*/
if (bAnsiHook) {
wParam = emsg.paramL & 0xff;
} else {
wParam = emsg.paramL & 0xffff;
}
lParam = MAKELONG(1, (UINT)((emsg.paramH & 0xFF) | wExtraStuff));
} else if (emsg.message == WM_QUEUESYNC) {
if (emsg.paramL == 0) {
pwnd = pti->pq->spwndActive;
} else {
if ((pwnd = RevalidateHwnd((HWND)emsg.paramL)) == NULL)
pwnd = pti->pq->spwndActive;
}
} else {
/*
* This event doesn't match up with what we're looking
* for. If the hook is still valid, then skip this message
* and try the next.
*/
if (phkCall == NULL || phkCall->offPfn == 0L) {
/* Hook is nolonger valid, return -1 */
ThreadUnlock(&tlphkCall);
return 0xFFFFFFFF;
}
RIPMSG1(RIP_WARNING,
"Bad journal playback message=0x%08lx",
emsg.message);
xxxCallHook(HC_SKIP, 0, 0, WH_JOURNALPLAYBACK);
ThreadUnlock(&tlphkCall);
goto TryNextEvent;
}
StoreQMessage(pqmsg, pwnd, emsg.message, wParam, lParam, 0, 0);
ThreadUnlock(&tlphkCall);
return 0;
}
/***************************************************************************\
* FreeHook
*
* Free hook unlinks the HOOK structure from its hook-list and removes
* any hmod dependencies on this hook. It also frees the HOOK structure.
*
* History:
* 01-31-91 DavidPe Created.
\***************************************************************************/
VOID FreeHook(
PHOOK phkFree)
{
/*
* Unlink it first.
*/
UnlinkHook(phkFree);
/*
* Mark it for destruction. If it the object is locked it can't
* be freed right now.
*/
if (!HMMarkObjectDestroy((PVOID)phkFree))
return;
/*
* Now remove the hmod dependency and free the
* HOOK structure.
*/
if (phkFree->ihmod >= 0) {
RemoveHmodDependency(phkFree->ihmod);
}
#ifdef HOOKBATCH
/*
* Free the cached Events
*/
if (phkFree->aEventCache) {
UserFreePool(phkFree->aEventCache);
phkFree->aEventCache = NULL;
}
#endif //HOOKBATCH
/*
* We're really going to free the hook. NULL out the next hook
* pointer now. We keep it filled even if the hook has been unlinked
* so that if a hook is being called back and the app frees the same
* hook with UnhookWindowsHook(), a call to CallNextHookProc() will
* call the next hook.
*/
Unlock(&phkFree->sphkNext);
HMFreeObject((PVOID)phkFree);
return;
}
void UnlinkHook(
PHOOK phkFree)
{
PHOOK *pphkStart, phk, phkPrev;
PTHREADINFO ptiT;
/*
* Since we have the HOOK structure, we can tell if this a global
* or local hook and start on the right list.
*/
if (phkFree->flags & HF_GLOBAL) {
pphkStart = &GETPTI(phkFree)->pDeskInfo->asphkStart[phkFree->iHook + 1];
} else {
ptiT = phkFree->ptiHooked;
if (ptiT == NULL) {
return;
} else if (ptiT == PtiCurrent()) {
/*
* If we get here during ptiHooked thread cleanup, decouple
* ptiHooked field now so that if phkFree is locked by another
* thread, we won't AV on bogus ptiHooked when phkFree is finally
* unlocked, freed and unlinked.
*/
phkFree->ptiHooked = NULL;
}
pphkStart = &(ptiT->asphkStart[phkFree->iHook + 1]);
}
for (phk = *pphkStart; phk != NULL; phk = phk->sphkNext) {
/*
* We've found it. Free the hook. Don't change phkNext during
* unhooking. This is so that if an app calls UnhookWindowsHook()
* during a hook callback, and then calls CallNextHookProc(), it
* will still really call the next hook proc.
*/
if (phk == phkFree) {
TL tlphk;
ThreadLock(phk, &tlphk);
/*
* First unlink it from its hook-list.
*/
if (phk == *pphkStart) {
Lock(pphkStart, phk->sphkNext);
} else {
Lock(&phkPrev->sphkNext, phk->sphkNext);
}
CheckWHFBits(GETPTI(phk), phk->iHook);
ThreadUnlock(&tlphk);
return;
}
phkPrev = phk;
}
}
/***************************************************************************\
* PhkFirst
*
* Given a filter-type PhkFirst() returns the first hook, if any, of the
* specified type.
*
* History:
* 02-10-91 DavidPe Created.
\***************************************************************************/
PHOOK PhkFirst(
PTHREADINFO pti,
int nFilterType)
{
PHOOK phk;
/*
* If we're on the RIT don't call any hooks!
*/
if (pti == gptiRit) {
return NULL;
}
/*
* Grab the first hooks off the local hook-list
* for the current queue.
*/
phk = pti->asphkStart[nFilterType + 1];
/*
* If there aren't any local hooks, try the global hooks.
*/
if (phk == NULL) {
return pti->pDeskInfo->asphkStart[nFilterType + 1];
}
UserAssert(phk != NULL);
return phk;
}
/***************************************************************************\
* PhkNext
*
* This helper routine simply does phk = phk->sphkNext with a simple check
* to jump from local hooks to the global hooks if it hits the end of the
* local hook chain.
*
* History:
* 01-30-91 DavidPe Created.
\***************************************************************************/
PHOOK _PhkNext(
PHOOK phk)
{
/*
* Return the next HOOK structure. If we reach the end of this list,
* check to see if we're still on the 'local' hook list. If so skip
* over to the global hooks.
*/
if (phk->sphkNext != NULL) {
return phk->sphkNext;
} else if ((phk->flags & HF_GLOBAL) == 0) {
return PtiCurrent()->pDeskInfo->asphkStart[phk->iHook + 1];
} else
return NULL;
}
/***************************************************************************\
* FreeThreadsWindowHooks
*
* During 'exit-list' processing this function is called to free any hooks
* created on, or set for the current queue.
*
* History:
* 02-10-91 DavidPe Created.
\***************************************************************************/
VOID FreeThreadsWindowHooks(VOID)
{
int iHook;
PHOOK phk, phkNext;
PTHREADINFO ptiCurrent = PtiCurrent();
/*
* If there is not thread info, there are not hooks to worry about
*/
if (ptiCurrent == NULL || ptiCurrent->rpdesk == NULL)
return;
/*
* Loop through all the hook types.
*/
for (iHook = WH_MIN ; iHook <= WH_MAX ; ++iHook) {
/*
* Loop through all the hooks of this type.
*/
phk = PhkFirst(ptiCurrent, iHook);
while (phk != NULL) {
/*
* Pick up the next hook pointer before we do any
* freeing so things don't get confused.
*/
phkNext = _PhkNext(phk);
/*
* If this hook wasn't created on this thread,
* then we don't want to free it.
*/
if (GETPTI(phk) == ptiCurrent) {
FreeHook(phk);
} else {
/*
* At least unlink it so we don't end up destroying a pti
* that references it, making us unable to destroy the hook
* later. This loop loops through global hooks too, and
* we don't want to unlink global hooks since they don't
* belong to this thread.
*/
if (!(phk->flags & HF_GLOBAL)) {
UnlinkHook(phk);
}
}
phk = phkNext;
}
}
/*
* And in case we have a hook locked in as the current hook unlock it
* so it can be freed
*/
Unlock(&ptiCurrent->sphkCurrent);
if (ptiCurrent->pClientInfo)
ptiCurrent->pClientInfo->phkCurrent = NULL;
}
/***************************************************************************\
* RegisterSystemThread: Private API
*
* Used to set various attributes pertaining to a thread.
*
* History:
* 21-Jun-1994 from Chicago Created.
\***************************************************************************/
VOID _RegisterSystemThread (DWORD dwFlags, DWORD dwReserved)
{
PTHREADINFO ptiCurrent;
UserAssert(dwReserved == 0);
if (dwReserved != 0)
return;
ptiCurrent = PtiCurrent();
if (dwFlags & RST_DONTATTACHQUEUE)
ptiCurrent->TIF_flags |= TIF_DONTATTACHQUEUE;
if (dwFlags & RST_DONTJOURNALATTACH) {
ptiCurrent->TIF_flags |= TIF_DONTJOURNALATTACH;
/*
* If we are already journaling, then this queue was already
* journal attached. We need to unattach and reattach journaling
* so that we are removed from the journal attached queues.
*/
if (FJOURNALPLAYBACK() || FJOURNALRECORD()) {
JournalAttach(ptiCurrent, FALSE);
JournalAttach(ptiCurrent, TRUE);
}
}
}