Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1017 lines
32 KiB

/**************************** Module Header ********************************\
* Module Name: winable.c
*
* This has the stuff for WinEvents:
* NotifyWinEvent
* _SetWinEventHook
* UnhookWinEventHook
*
* All other additions to USER for Active Accessibility are in winable2.c.
*
* Copyright (c) 1985 - 1999, Microsoft Corporation
*
* History:
* Based on snapshot taken from:
* \\trango\slmro\proj\win\src\CORE\access\user_40\user32 on 8/29/96
* 08-30-96 IanJa Ported from Windows '95
\***************************************************************************/
#include "precomp.h"
#pragma hdrstop
#if DBG
int gnNotifies;
__inline VOID DBGVERIFYEVENTHOOK(
PEVENTHOOK peh)
{
HMValidateCatHandleNoSecure(PtoH(peh), TYPE_WINEVENTHOOK);
UserAssert(peh->eventMin <= peh->eventMax);
}
__inline VOID DBGVERIFYNOTIFY(
PNOTIFY pNotify)
{
UserAssert(pNotify->spEventHook != NULL);
UserAssert(pNotify->spEventHook->fSync || (pNotify->dwWEFlags & WEF_ASYNC));
}
#else
#define DBGVERIFYEVENTHOOK(peh)
#define DBGVERIFYNOTIFY(pNotify)
#endif
/*
* Pending Event Notifications (sync and async).
*/
static NOTIFY notifyCache;
static BOOL fNotifyCacheInUse;
/*
* Local to this module.
*/
WINEVENTPROC xxxGetEventProc(
PEVENTHOOK pEventOrg);
PNOTIFY CreateNotify(
PEVENTHOOK peh,
DWORD event,
PWND pwnd,
LONG idObject,
LONG idChild,
PTHREADINFO ptiEvent,
DWORD dwTime);
/*****************************************************************************\
* xxxProcessNotifyWinEvent
*
* Posts or Sends a WinEvent notification.
*
* Post: uses PostEventMesage - does not leave the critical section.
* Send: makes a callback to user-mode - does leave the critical section.
*
* If this is a system thread (RIT, Desktop or Console) then synchronously
* hooked (WINEVENT_INCONTEXT) events are forced to be asynchronous.
*
* We return the next win event hook in the list.
\*****************************************************************************/
PEVENTHOOK xxxProcessNotifyWinEvent(
PNOTIFY pNotify)
{
WINEVENTPROC pfn;
PEVENTHOOK pEventHook;
TL tlpEventHook;
PTHREADINFO ptiCurrent = PtiCurrent();
pEventHook = pNotify->spEventHook;
DBGVERIFYEVENTHOOK(pEventHook);
UserAssert(pEventHook->head.cLockObj);
if (((pNotify->dwWEFlags & (WEF_ASYNC | WEF_POSTED)) == WEF_ASYNC)
||
(ptiCurrent->TIF_flags & (TIF_SYSTEMTHREAD | TIF_CSRSSTHREAD | TIF_INCLEANUP))
||
(!RtlEqualLuid(&GETPTI(pEventHook)->ppi->luidSession, &ptiCurrent->ppi->luidSession) &&
!(ptiCurrent->TIF_flags & TIF_ALLOWOTHERACCOUNTHOOK))
||
(GETPTI(pEventHook)->ppi != ptiCurrent->ppi &&
IsRestricted(GETPTI(pEventHook)->pEThread))
#if defined(_WIN64)
||
((GETPTI(pEventHook)->TIF_flags & TIF_WOW64) != (ptiCurrent->TIF_flags & TIF_WOW64))
#endif
) {
/*
* POST
*
* WinEvent Hook set without WINEVENT_INCONTEXT flag are posted;
* Events from system threads are posted because there is no user-mode
* part to callback to;
* Console is not permitted to load DLLs, so we must post back to the
* hooking application;
* DLLs can not be loaded cross bit type(32bit to 64bit) on 64bit NT
* so we must post(It may be usefull to let the app be aware and
* even supply both a 32bit and a 64bit DLL that are aware of each other);
* Threads in cleanup can't get called back, so turn their
* notifications into async ones. (Better late than never).
*
* If forcing these events ASYNC is unacceptable, we might consider
* doing system/console SYNC events like low-level hooks (sync with
* timeout: but may have to post it if the timeout expires) - IanJa
*/
PQ pqReceiver = GETPTI(pEventHook)->pq;
PEVENTHOOK pEventHookNext = pEventHook->pehNext;
BEGINATOMICCHECK();
DBGVERIFYNOTIFY(pNotify);
pNotify->dwWEFlags |= WEF_POSTED | WEF_ASYNC;
if (!pqReceiver || (GETPTI(pEventHook) == gptiRit) ||
pEventHook->fDestroyed ||
!PostEventMessage(GETPTI(pEventHook), pqReceiver,
QEVENT_NOTIFYWINEVENT,
NULL, 0, 0, (LPARAM)pNotify)) {
/*
* If the receiver doesn't have a queue or the
* post failed (low memory), cleanup what we just
* created.
* Note: destroying the notification may destroy pEventHook too.
*/
RIPMSG2(RIP_WARNING,
"Failed to post NOTIFY at 0x%p, time %lx",
pNotify,
pNotify->dwEventTime);
DestroyNotify(pNotify);
}
ENDATOMICCHECK();
if (pEventHookNext) {
DBGVERIFYEVENTHOOK(pEventHookNext);
}
return pEventHookNext;
}
/*
* Don't call back if the hook has been destroyed (unhooked).
*/
if (pEventHook->fDestroyed) {
/*
* Save the next hook since DestroyNotify may cause pEventHook to
* be freed by unlocking it.
*/
pEventHook = pEventHook->pehNext;
DestroyNotify(pNotify);
return pEventHook;
}
/*
* CALLBACK
*
* This leaves the critical section.
* We return the next Event Hook in the list so that the caller doesn't
* have to lock pEventHook.
*/
UserAssert((pNotify->dwWEFlags & WEF_DEFERNOTIFY) == 0);
ThreadLockAlways(pEventHook, &tlpEventHook);
UserAssertMsg1(pNotify->ptiReceiver == NULL,
"pNotify %#p is already in callback! Reentrant?", pNotify);
pNotify->ptiReceiver = ptiCurrent;
if (!pEventHook->fSync) {
UserAssert(pEventHook->ihmod == -1);
pfn = (WINEVENTPROC)pEventHook->offPfn;
} else {
pfn = xxxGetEventProc(pEventHook);
}
if (pfn) {
xxxClientCallWinEventProc(pfn, pEventHook, pNotify);
DBGVERIFYNOTIFY(pNotify);
DBGVERIFYEVENTHOOK(pEventHook);
UserAssert(pEventHook->head.cLockObj);
}
pNotify->ptiReceiver = NULL;
/*
* Save the next item in the list, ThreadUnlock() may destroy pEventHook.
* DestroyNotify() may also kill the event if it is a zombie (destroyed
* but being used, waiting for use count to go to 0 before being freed).
*/
pEventHook = pEventHook->pehNext;
ThreadUnlock(&tlpEventHook);
/*
* We are done with the notification. Kill it.
*
* NOTE that DestroyNotify does not yield, which is why we can hang on
* to the pehNext field above around this call.
*
* NOTE ALSO that DestroyNotify will kill the event it references if the
* ref count goes down to zero and it was zombied earlier.
*/
DestroyNotify(pNotify);
return pEventHook;
}
/****************************************************************************\
* xxxFlushDeferredWindowEvents
*
* Process notifications that were queued up during DeferWinEventNotify.
\****************************************************************************/
VOID xxxFlushDeferredWindowEvents(
VOID)
{
PNOTIFY pNotify;
DWORD idCurrentThread = W32GetCurrentTID();
/*
* If idCurrentThread is 0 we're not in danger of faulting, but we'll
* needlessly walk the list of pending notifications (since all will be
* ignored).
*/
UserAssert(idCurrentThread != 0);
UserAssert(IsWinEventNotifyDeferredOK());
pNotify = gpPendingNotifies;
while (pNotify) {
if (((pNotify->dwWEFlags & WEF_DEFERNOTIFY) == 0) ||
(pNotify->idSenderThread != idCurrentThread)) {
pNotify = pNotify->pNotifyNext;
} else {
/*
* Clear WEF_DEFERNOTIFY so that if we recurse in the callback
* we won't try to send this notification again.
*/
pNotify->dwWEFlags &= ~WEF_DEFERNOTIFY;
#if DBG
gnDeferredWinEvents--;
#endif
/*
* We shouldn't have deferred ASYNC notifications: we should have
* posted them immediately.
*/
UserAssert((pNotify->dwWEFlags & WEF_ASYNC) == 0);
xxxProcessNotifyWinEvent(pNotify);
/*
* Start again at the head of the list, in case it munged during
* the callback.
*/
pNotify = gpPendingNotifies;
}
}
}
/*****************************************************************************\
*
* xxxWindowEvent
*
* Send, Post or Defer a Win Event notification, depending on what Win Event
* hooks are installed and what the context of the caller is.
*
* The caller should test FWINABLE() and only call xxxWindowEvent if it is TRUE,
* that way only costs a few clocks if no Win Event hooks are set.
*
* Caller shouldn't lock pwnd, because xxxWindowEvent() will do it.
*
\*****************************************************************************/
VOID
xxxWindowEvent(
DWORD event,
PWND pwnd,
LONG idObject,
LONG idChild,
DWORD dwFlags)
{
PEVENTHOOK peh;
PEVENTHOOK pehNext;
PTHREADINFO ptiCurrent, ptiEvent;
DWORD dwTime;
PPROCESSINFO ppiEvent;
DWORD idEventThread;
HANDLE hEventProcess;
PNOTIFY pNotify;
TL tlpwnd;
TL tlpti;
/*
* Do not bother with CheckLock(pwnd) - we ThreadLock it below.
*/
if (!FEVENTHOOKED(event)) {
return;
}
/*
* This thread is in startup, and has not yet had it's pti set up
* This is pretty rare, but sometimes encountered in stress.
* Test gptiCurrent to avoid the UserAssert(gptiCurrent) in PtiCurrent()
*/
if (gptiCurrent == NULL) {
RIPMSG3(RIP_WARNING, "Ignore WinEvent %lx %#p %lx... no PtiCurrent yet",
event, pwnd, idObject);
return;
}
ptiCurrent = PtiCurrent();
/*
* Don't bother with destroyed windows
*/
if (pwnd && TestWF(pwnd, WFDESTROYED)) {
RIPMSG3(RIP_WARNING,
"Ignore WinEvent %lx %#p %lx... pwnd already destroyed",
event, pwnd, idObject);
return;
}
/*
* Under some special circumstances we have to defer
*/
if (ptiCurrent->TIF_flags & (TIF_DISABLEHOOKS | TIF_INCLEANUP)) {
dwFlags |= WEF_DEFERNOTIFY;
}
/*
* Determine process and thread issuing the event notification
*/
if ((dwFlags & WEF_USEPWNDTHREAD) && pwnd) {
ptiEvent = GETPTI(pwnd);
} else {
ptiEvent = ptiCurrent;
}
idEventThread = TIDq(ptiEvent);
ppiEvent = ptiEvent->ppi;
hEventProcess = PsGetThreadProcessId(ptiEvent->pEThread);
dwTime = NtGetTickCount();
ThreadLockWithPti(ptiCurrent, pwnd, &tlpwnd);
ThreadLockPti(ptiCurrent, ptiEvent, &tlpti);
/*
* If we're not deferring the current notification process any pending
* deferred notifications before proceeding with the current notification
*/
if (!(dwFlags & WEF_DEFERNOTIFY)) {
xxxFlushDeferredWindowEvents();
}
for (peh = gpWinEventHooks; peh; peh = pehNext) {
DBGVERIFYEVENTHOOK(peh);
pehNext = peh->pehNext;
//
// Is event in the right range? And is it for this process/thread?
// Note that we skip destroyed events. They will be freed any
// second now, it's just that yielding may have caused reentrancy.
//
// If the caller said to ignore events on his own thread, make sure
// we skip them.
//
if (!peh->fDestroyed &&
(peh->eventMin <= event) &&
(event <= peh->eventMax) &&
(!peh->hEventProcess || (peh->hEventProcess == hEventProcess)) &&
(!peh->fIgnoreOwnProcess || (ppiEvent != GETPTI(peh)->ppi)) &&
(!peh->idEventThread || (peh->idEventThread == idEventThread)) &&
(!peh->fIgnoreOwnThread || (ptiEvent != GETPTI(peh))) &&
// temp fix from SP3 - best to architect events on a per-desktop
// basis, with a separate pWinEventHook list per desktop. (IanJa)
(peh->head.pti->rpdesk == ptiCurrent->rpdesk))
{
/*
* Don't create new notifications for zombie event hooks.
* When an event is destroyed, it stays as a zombie until the in-use
* count goes to zero (all it's async and deferred notifies gone)
*/
if (HMIsMarkDestroy(peh)) {
break;
}
UserAssert(peh->fDestroyed == 0);
if ((pNotify = CreateNotify(peh, event, pwnd, idObject,
idChild, ptiEvent, dwTime)) == NULL) {
break;
}
pNotify->dwWEFlags |= dwFlags;
/*
* If it's async, don't defer it: post it straight away.
*/
if (pNotify->dwWEFlags & WEF_ASYNC) {
pNotify->dwWEFlags &= ~WEF_DEFERNOTIFY;
}
if (pNotify->dwWEFlags & WEF_DEFERNOTIFY) {
#if DBG
gnDeferredWinEvents++;
#endif
DBGVERIFYNOTIFY(pNotify);
} else {
pehNext = xxxProcessNotifyWinEvent(pNotify);
}
}
}
ThreadUnlockPti(ptiCurrent, &tlpti);
ThreadUnlock(&tlpwnd);
}
/****************************************************************************\
*
* CreateNotify()
*
* Gets a pointer to a NOTIFY struct that we can then propagate to our
* event window via Send/PostMessage. We have to do this since we want to
* (pass on a lot more data then can be packed in the parameters.
*
* We have one cached struct so we avoid lots of allocs and frees in the
* most common case of just one outstanding notification.
\****************************************************************************/
PNOTIFY
CreateNotify(PEVENTHOOK pEvent, DWORD event, PWND pwnd, LONG idObject,
LONG idChild, PTHREADINFO ptiSender, DWORD dwTime)
{
PNOTIFY pNotify;
UserAssert(pEvent != NULL);
//
// Get a pointer. From cache if available.
// IanJa - change this to allocate from zone a la AllocQEntry??
//
if (!fNotifyCacheInUse) {
fNotifyCacheInUse = TRUE;
pNotify = &notifyCache;
#if DBG
//
// Make sure we aren't forgetting to set any fields.
//
// DebugFillBuffer(pNotify, sizeof(NOTIFY));
#endif
} else {
pNotify = (PNOTIFY)UserAllocPool(sizeof(NOTIFY), TAG_NOTIFY);
if (!pNotify)
return NULL;
}
/*
* Fill in the notify block.
*/
pNotify->spEventHook = NULL;
Lock(&pNotify->spEventHook, pEvent);
pNotify->hwnd = HW(pwnd);
pNotify->event = event;
pNotify->idObject = idObject;
pNotify->idChild = idChild;
pNotify->idSenderThread = TIDq(ptiSender);
UserAssert(pNotify->idSenderThread != 0);
pNotify->dwEventTime = dwTime;
pNotify->dwWEFlags = pEvent->fSync ? 0 : WEF_ASYNC;
pNotify->pNotifyNext = NULL;
pNotify->ptiReceiver = NULL;
#if DBG
gnNotifies++;
#endif
/*
* The order of non-deferred notifications doesn't matter; they are here
* simply for cleanup/in-use tracking. However, deferred notifications must
* be ordered with most recent at the end, so just order them all that way.
*/
if (gpPendingNotifies) {
UserAssert(gpLastPendingNotify);
UserAssert(gpLastPendingNotify->pNotifyNext == NULL);
gpLastPendingNotify->pNotifyNext = pNotify;
} else {
gpPendingNotifies = pNotify;
}
gpLastPendingNotify = pNotify;
return pNotify;
}
/****************************************************************************\
* RemoveNotify
\****************************************************************************/
VOID RemoveNotify(
PNOTIFY *ppNotify)
{
PNOTIFY pNotifyRemove;
pNotifyRemove = *ppNotify;
/*
* First, get it out of the pending list.
*/
*ppNotify = pNotifyRemove->pNotifyNext;
#if DBG
if (pNotifyRemove->dwWEFlags & WEF_DEFERNOTIFY) {
UserAssert(gnDeferredWinEvents > 0);
gnDeferredWinEvents--;
}
#endif
if (*ppNotify == NULL) {
/*
* Removing last notify, so fix up gpLastPendingNotify:
* If list now empty, there is no last item.
*/
if (gpPendingNotifies == NULL) {
gpLastPendingNotify = NULL;
} else {
gpLastPendingNotify = CONTAINING_RECORD(ppNotify, NOTIFY, pNotifyNext);
}
}
UserAssert(gpPendingNotifies == 0 || gpPendingNotifies > (PNOTIFY)100);
DBGVERIFYEVENTHOOK(pNotifyRemove->spEventHook);
/*
* This may cause the win event hook to be freed.
*/
Unlock(&pNotifyRemove->spEventHook);
/*
* Now free it. Either put it back in the cache (if it is the cache)
* or really free it.
*/
if (pNotifyRemove == &notifyCache) {
UserAssert(fNotifyCacheInUse);
fNotifyCacheInUse = FALSE;
} else {
UserFreePool(pNotifyRemove);
}
#if DBG
UserAssert(gnNotifies > 0);
gnNotifies--;
#endif
}
/*****************************************************************************\
* DestroyNotify
*
* This gets the notification out of our pending list and frees the local
* memory it uses.
*
* This function is called
* (1) NORMALLY: After returning from calling the notify proc
* (2) CLEANUP: When a thread goes away, we cleanup async notifies it
* hasn't received, and sync notifies it was in the middle of trying
* to call (i.e. the event proc faulted).
\*****************************************************************************/
VOID DestroyNotify(
PNOTIFY pNotifyDestroy)
{
PNOTIFY *ppNotify;
PNOTIFY pNotifyT;
DBGVERIFYNOTIFY(pNotifyDestroy);
/*
* Either this notify isn't currently in the process of calling back
* (which means ptiReceiver is NULL) or the thread destroying it
* must be the one that was calling back (which means this thread
* was destroyed during the callback and is cleaning up).
*/
UserAssert(pNotifyDestroy->ptiReceiver == NULL ||
pNotifyDestroy->ptiReceiver == PtiCurrent());
ppNotify = &gpPendingNotifies;
while (pNotifyT = *ppNotify) {
if (pNotifyT == pNotifyDestroy) {
RemoveNotify(ppNotify);
return;
} else {
ppNotify = &pNotifyT->pNotifyNext;
}
}
RIPMSG1(RIP_ERROR, "DestroyNotify 0x%p - not found", pNotifyDestroy);
}
/***************************************************************************\
* FreeThreadsWinEvents
*
* During 'exit-list' processing this function is called to free any WinEvent
* notifications and WinEvent hooks created by the current thread.
*
* Notifications that remain may be:
* o Posted notifications (async)
* o Notifications in xxxClientCallWinEventProc (sync)
* o Deferred notifications (should be sync only)
* Destroy the sync notifications, because we cannot do callbacks
* while in thread cleanup.
* Leave the posted (async) notifications alone: they're on their way already.
*
* History:
* 11-11-96 IanJa Created.
\***************************************************************************/
VOID FreeThreadsWinEvents(
PTHREADINFO pti)
{
PEVENTHOOK peh, pehNext;
PNOTIFY pn, pnNext;
DWORD idCurrentThread = W32GetCurrentTID();
/*
* Loop through all the notifications.
*/
for (pn = gpPendingNotifies; pn; pn = pnNext) {
pnNext = pn->pNotifyNext;
/*
* Only destroy sync notifications that belong to this thread
* and are not currently calling back i.e. ptiReceiver must be NULL.
* Otherwise, when we come back from the callback in
* xxxProcessNotifyWinEvent we will operate on a freed notify.
* Also destroy the notification if the receiver is going away
* or else it gets leaked as long as the sender is alive.
*/
if ((pn->idSenderThread == idCurrentThread &&
pn->ptiReceiver == NULL) ||
pn->ptiReceiver == pti) {
if ((pn->dwWEFlags & WEF_ASYNC) == 0) {
UserAssert((pn->dwWEFlags & WEF_POSTED) == 0);
DestroyNotify(pn);
}
}
}
peh = gpWinEventHooks;
while (peh) {
pehNext = peh->pehNext;
if (GETPTI(peh) == pti) {
DestroyEventHook(peh);
}
peh = pehNext;
}
}
/***************************************************************************\
* _SetWinEventHook()
*
* This installs a win event hook.
*
* If hEventProcess set but idEventThread = 0, hook all threads in process.
* If idEventThread set but hEventProcess = NULL, hook single thread only.
* If neither are set, hook everything.
* If both are set ??
*
\***************************************************************************/
PEVENTHOOK _SetWinEventHook(
DWORD eventMin,
DWORD eventMax,
HMODULE hmodWinEventProc,
PUNICODE_STRING pstrLib,
WINEVENTPROC pfnWinEventProc,
HANDLE hEventProcess,
DWORD idEventThread,
DWORD dwFlags)
{
PEVENTHOOK pEventNew;
PTHREADINFO ptiCurrent;
int ihmod;
ptiCurrent = PtiCurrent();
//
// If exiting, fail the call.
//
if (ptiCurrent->TIF_flags & TIF_INCLEANUP) {
RIPMSG1(RIP_ERROR,
"SetWinEventHook: Fail call - thread 0x%p in cleanup",
ptiCurrent);
return NULL;
}
/*
* Check to see if filter proc is valid.
*/
if (pfnWinEventProc == NULL) {
RIPERR0(ERROR_INVALID_FILTER_PROC,
RIP_WARNING,
"pfnWinEventProc == NULL");
return NULL;
}
if (eventMin > eventMax) {
RIPERR0(ERROR_INVALID_HOOK_FILTER,
RIP_WARNING,
"eventMin > eventMax");
return NULL;
}
if (dwFlags & WINEVENT_INCONTEXT) {
/*
* WinEventProc to be called in context of hooked thread, so needs a DLL.
*/
if (hmodWinEventProc == NULL) {
RIPERR0(ERROR_HOOK_NEEDS_HMOD,
RIP_WARNING,
"In context hook w/o DLL!");
return NULL;
} else if (pstrLib == NULL) {
/*
* If we got an hmod, we should get a DLL name too!
*/
RIPERR1(ERROR_DLL_NOT_FOUND,
RIP_WARNING,
"hmod 0x%p, but no lib name",
hmodWinEventProc);
return NULL;
}
ihmod = GetHmodTableIndex(pstrLib);
if (ihmod == -1) {
RIPERR0(ERROR_MOD_NOT_FOUND,
RIP_WARNING,
"");
return NULL;
}
} else {
ihmod = -1; // means no DLL is required
hmodWinEventProc = 0;
}
/*
* Check the thread id, check it is a GUI thread.
*/
if (idEventThread != 0) {
PTHREADINFO ptiT;
ptiT = PtiFromThreadId(idEventThread);
if (ptiT == NULL || !(ptiT->TIF_flags & TIF_GUITHREADINITIALIZED)) {
RIPERR1(ERROR_INVALID_THREAD_ID, RIP_VERBOSE, "pti %#p", ptiT);
return NULL;
}
}
/*
* Create the window for async events first.
*
* NOTE that USER itself will not pass on window creation/destruction
* notifications for
* * IME windows
* * OLE windows
* * RPC windows
* * Event windows
*/
/*
* Get a new event.
*/
pEventNew = (PEVENTHOOK)HMAllocObject(ptiCurrent,
NULL,
TYPE_WINEVENTHOOK,
sizeof(EVENTHOOK));
if (!pEventNew) {
return NULL;
}
/*
* Fill in the new event.
*/
pEventNew->eventMin = (UINT)eventMin;
pEventNew->eventMax = (UINT)eventMax;
pEventNew->fIgnoreOwnThread = ((dwFlags & WINEVENT_SKIPOWNTHREAD) != 0);
pEventNew->fIgnoreOwnProcess = ((dwFlags & WINEVENT_SKIPOWNPROCESS) != 0);
pEventNew->fDestroyed = FALSE;
pEventNew->fSync = ((dwFlags & WINEVENT_INCONTEXT) != 0);
pEventNew->hEventProcess = hEventProcess;
pEventNew->idEventThread = idEventThread;
pEventNew->ihmod = ihmod;
/*
* Add a dependency on this module - meaning, increment a count
* that simply counts the number of hooks set into this module.
*/
if (pEventNew->ihmod >= 0) {
AddHmodDependency(pEventNew->ihmod);
}
/*
* If pfnWinEventProc is in caller's process and no DLL is involved,
* then pEventNew->offPfn is the actual address.
*/
pEventNew->offPfn = ((ULONG_PTR)pfnWinEventProc) - ((ULONG_PTR)hmodWinEventProc);
/*
* Link our event into the master list.
*
* Note that we count on USER to not generate any events when installing
* our hook. The caller can't handle it yet since he hasn't got back
* his event handle from this call.
*/
pEventNew->pehNext = gpWinEventHooks;
gpWinEventHooks = pEventNew;
/*
* Update the flags that indicate what event hooks are installed. These
* flags are accessable from user mode without a kernel transition since
* they are in shared memory.
*/
SET_FLAG(gpsi->dwInstalledEventHooks, CategoryMaskFromEventRange(eventMin, eventMax));
return pEventNew;
}
/****************************************************************************\
* UnhookWinEvent
*
* Unhooks a win event hook. We of course sanity check that this thread is
* the one which installed the hook. We have to: We are going to destroy
* the IPC window and that must be in context.
\****************************************************************************/
BOOL _UnhookWinEvent(
PEVENTHOOK pEventUnhook)
{
DBGVERIFYEVENTHOOK(pEventUnhook);
if (HMIsMarkDestroy(pEventUnhook) || (GETPTI(pEventUnhook) != PtiCurrent())) {
/*
* We do this to avoid someone calling UnhookWinEvent() the first
* time, then somehow getting control again and calling it a second
* time before we've managed to free up the event since someone was
* in the middle of using it at the first UWE call.
*/
RIPERR1(ERROR_INVALID_HANDLE,
RIP_WARNING,
"_UnhookWinEvent: Invalid event hook 0x%p",
PtoHq(pEventUnhook));
return FALSE;
}
/*
* Purge this baby if all notifications are done.
*
* If there are SYNC ones pending, the caller will clean this up upon
* the return from calling the event.
*
* If there are ASYNC ones pending, the receiver will not call the event
* and clean it up when he gets it.
*/
DestroyEventHook(pEventUnhook);
return TRUE;
}
/*****************************************************************************\
* DestroyEventHook
*
* Destroys an event when the ref count has gone down to zero. It may
* happen:
* * In the event generator's context, after returning from a callback
* and the ref count dropped to zero, if sync.
* * In the event installer's context, after returning from a callback
* and the ref count dropped to zero if async.
* * In the event installer's context, if on _UnhookWinEvent() the event
* was not in use at all.
\*****************************************************************************/
VOID DestroyEventHook(
PEVENTHOOK pEventDestroy)
{
PEVENTHOOK *ppEvent;
PEVENTHOOK pEventT;
DWORD dwCategoryMask = 0;
DBGVERIFYEVENTHOOK(pEventDestroy);
UserAssert(gpWinEventHooks);
/*
* Mark this event as destroyed, but don't remove it from the event list
* until its lock count goes to 0 - we may be traversing the list within
* xxxWindowEvent, so we mustn't break the link to the next hook.
*/
pEventDestroy->fDestroyed = TRUE;
/*
* If the object is locked, mark it for destroy but don't free it yet.
*/
if (!HMMarkObjectDestroy(pEventDestroy)) {
return;
}
/*
* Remove this from our event list.
*/
for (ppEvent = &gpWinEventHooks; pEventT = *ppEvent; ppEvent = &pEventT->pehNext) {
if (pEventT == pEventDestroy) {
*ppEvent = pEventDestroy->pehNext;
break;
}
}
UserAssert(pEventT);
/*
* Update the flags that indicate what event hooks are installed. These
* flags are accessable from user mode without a kernel transition since
* they are in shared memory. Note that a user could check the shared
* memory at any time, so they may get a false-positive during this
* processing. A false-positive would mean that we claim there is a
* listener, when there really isn't. We never want the user to get
* a false negative - meaning that we claim there aren't any listeners
* but there really is. That could mean bad things for accessability.
*/
for (pEventT = gpWinEventHooks; pEventT != NULL; pEventT = pEventT->pehNext) {
SET_FLAG(dwCategoryMask, CategoryMaskFromEventRange(pEventT->eventMin, pEventT->eventMax));
}
gpsi->dwInstalledEventHooks = dwCategoryMask;
/*
* Make sure each hooked thread will unload the hook proc DLL.
*/
if (pEventDestroy->ihmod >= 0) {
RemoveHmodDependency(pEventDestroy->ihmod);
}
/*
* Free this pointer.
*/
HMFreeObject(pEventDestroy);
}
/***************************************************************************\
* xxxGetEventProc
*
* For sync events, this gets the address to call. If 16-bits, then just
* return the installed address. If 32-bits, we need to load the library
* if not in the same process as the installer.
\***************************************************************************/
WINEVENTPROC xxxGetEventProc(
PEVENTHOOK pEventOrg)
{
PTHREADINFO ptiCurrent;
UserAssert(pEventOrg);
UserAssert(pEventOrg->fSync);
UserAssert(pEventOrg->ihmod >= 0);
UserAssert(pEventOrg->offPfn != 0);
CheckLock(pEventOrg);
/*
* Make sure the hook is still around before we try and call it.
*/
if (HMIsMarkDestroy(pEventOrg)) {
return NULL;
}
ptiCurrent = PtiCurrent();
/*
* Make sure the DLL for this hook, if any, has been loaded for the
* current process.
*/
if (pEventOrg->ihmod != -1 &&
TESTHMODLOADED(ptiCurrent, pEventOrg->ihmod) == 0) {
/*
* Try loading the library, since it isn't loaded in this processes
* context. The hook is alrerady locked, so it won't go away while
* we're loading this library.
*/
if (xxxLoadHmodIndex(pEventOrg->ihmod) == NULL) {
return NULL;
}
}
/*
* While we're still inside the critical section make sure the hook
* hasn't been 'freed'. If so just return NULL. Since WinEvent has
* already been called, you might think that we should pass the event
* on, but the hooker may not be expecting this after having
* cancelled the hook! In any case, we have two ways of detecting that
* this hook has been removed (see the following code).
*/
/*
* Make sure the hook is still around before we try and call it.
*/
if (HMIsMarkDestroy(pEventOrg)) {
return NULL;
}
return (WINEVENTPROC)PFNHOOK(pEventOrg);
}