Main Page | Class Hierarchy | Class List | File List | Class Members | File Members

winable.c

Go to the documentation of this file.
00001 /**************************** Module Header ********************************\ 00002 * Module Name: winable.c 00003 * 00004 * This has the stuff for WinEvents: 00005 * NotifyWinEvent 00006 * _SetWinEventHook 00007 * UnhookWinEventHook 00008 * 00009 * All other additions to USER for Active Accessibility are in WINABLE2.C 00010 * and its helper ASM file, ABLEASM.ASM. 00011 * 00012 * Copyright (c) 1985 - 1999, Microsoft Corporation 00013 * 00014 * History: 00015 * Based on snapshot taken from: 00016 * \\trango\slmro\proj\win\src\CORE\access\user_40\user32 on 8/29/96 00017 * 08-30-96 IanJa Ported from Windows '95 00018 \***************************************************************************/ 00019 00020 #include "precomp.h" 00021 #pragma hdrstop 00022 00023 #if DBG 00024 int gnNotifies = 0; 00025 #define DBGVERIFYEVENTHOOK(peh) \ 00026 HMValidateCatHandleNoSecure(PtoH(peh), TYPE_WINEVENTHOOK); \ 00027 UserAssertMsg1((IsValidTag(peh, TAG_WINEVENT)), "event hook %#p: bad tag", peh); \ 00028 UserAssertMsg1((peh->eventMin <= peh->eventMax), "event hook %#p: bad range", peh) 00029 #define DBGVERIFYNOTIFY(pNotify) \ 00030 UserAssert(pNotify->spEventHook != NULL); \ 00031 UserAssert(pNotify->spEventHook->fSync || (pNotify->dwWEFlags & WEF_ASYNC)) 00032 #else 00033 #define DBGVERIFYEVENTHOOK(peh) 00034 #define DBGVERIFYNOTIFY(pNotify) 00035 #endif 00036 00037 /* 00038 * Pending Event Notifications (sync and async) 00039 */ 00040 00041 static NOTIFY notifyCache; 00042 static BOOL fNotifyCacheInUse = FALSE; 00043 00044 00045 /* 00046 * Local to this module 00047 */ 00048 WINEVENTPROC xxxGetEventProc(PEVENTHOOK pEventOrg); 00049 PNOTIFY CreateNotify(PEVENTHOOK peh, DWORD event, PWND pwnd, LONG idObject, 00050 LONG idChild, PTHREADINFO ptiEvent, DWORD dwTime); 00051 00052 00053 /*****************************************************************************\ 00054 * 00055 * xxxProcessNotifyWinEvent() 00056 * 00057 * Posts or Sends a WinEvent notification. 00058 * Post: uses PostEventMesage - does not leave the critical section. 00059 * Send: makes a callback to user-mode - does leave the critical section. 00060 * 00061 * If this is a system thread (RIT, Desktop or Console) then synchronously 00062 * hooked (WINEVENT_INCONTEXT) events are forced to be asynchronous. 00063 * 00064 * We return the next win event hook in the list. 00065 * 00066 \*****************************************************************************/ 00067 PEVENTHOOK 00068 xxxProcessNotifyWinEvent(PNOTIFY pNotify) 00069 { 00070 WINEVENTPROC pfn; 00071 PEVENTHOOK pEventHook; 00072 TL tlpEventHook; 00073 PTHREADINFO ptiCurrent = PtiCurrent(); 00074 00075 pEventHook = pNotify->spEventHook; 00076 DBGVERIFYEVENTHOOK(pEventHook); 00077 UserAssert(pEventHook->head.cLockObj); 00078 00079 if (((pNotify->dwWEFlags & (WEF_ASYNC | WEF_POSTED)) == WEF_ASYNC) 00080 || 00081 (ptiCurrent->TIF_flags & (TIF_SYSTEMTHREAD | TIF_CSRSSTHREAD | TIF_INCLEANUP)) 00082 00083 || 00084 (!RtlEqualLuid(&GETPTI(pEventHook)->ppi->luidSession, &ptiCurrent->ppi->luidSession) && 00085 !(ptiCurrent->TIF_flags & TIF_ALLOWOTHERACCOUNTHOOK)) 00086 00087 || 00088 (GETPTI(pEventHook)->ppi != ptiCurrent->ppi && 00089 IsRestricted(GETPTI(pEventHook)->pEThread)) 00090 00091 #if defined(_WIN64) 00092 || 00093 ((GETPTI(pEventHook)->TIF_flags & TIF_WOW64) != (ptiCurrent->TIF_flags & TIF_WOW64)) 00094 #endif 00095 ) { 00096 /* 00097 * POST 00098 * 00099 * WinEvent Hook set without WINEVENT_INCONTEXT flag are posted; 00100 * Events from system threads are posted because there is no user-mode 00101 * part to callback to; 00102 * Console is not permitted to load DLLs, so we must post back to the 00103 * hooking application; 00104 * DLLs can not be loaded cross bit type(32bit to 64bit) on 64bit NT 00105 * so we must post(It may be usefull to let the app be aware and 00106 * even supply both a 32bit and a 64bit DLL that are aware of each other); 00107 * Threads in cleanup can't get called back, so turn their 00108 * notifications into async ones. (Better late than never). 00109 * 00110 * If forcing these events ASYNC is unacceptable, we might consider 00111 * doing system/console SYNC events like low-level hooks (sync with 00112 * timeout: but may have to post it if the timeout expires) - IanJa 00113 */ 00114 PQ pqReceiver = GETPTI(pEventHook)->pq; 00115 PEVENTHOOK pEventHookNext = pEventHook->pehNext; 00116 00117 BEGINATOMICCHECK(); 00118 00119 DBGVERIFYNOTIFY(pNotify); 00120 pNotify->dwWEFlags |= WEF_POSTED | WEF_ASYNC; 00121 if (!pqReceiver || (GETPTI(pEventHook) == gptiRit) || 00122 pEventHook->fDestroyed || 00123 !PostEventMessage(GETPTI(pEventHook), pqReceiver, 00124 QEVENT_NOTIFYWINEVENT, 00125 NULL, 0, 0, (LPARAM)pNotify)) { 00126 /* 00127 * If the receiver doesn't have a queue or the 00128 * post failed (low memory), cleanup what we just 00129 * created. 00130 * Note: destroying the notification may destroy pEventHook too. 00131 */ 00132 RIPMSG2(RIP_WARNING, "failed to post NOTIFY at %#p, time %lx\n", 00133 pNotify, pNotify->dwEventTime); 00134 DestroyNotify(pNotify); 00135 } 00136 00137 ENDATOMICCHECK(); 00138 00139 if (pEventHookNext) { 00140 DBGVERIFYEVENTHOOK(pEventHookNext); 00141 } 00142 return pEventHookNext; 00143 } 00144 00145 /* 00146 * Don't call back if the hook has been destroyed (unhooked). 00147 */ 00148 if (pEventHook->fDestroyed) { 00149 /* 00150 * Save the next hook since DestroyNotify may cause pEventHook to 00151 * be freed by unlocking it. 00152 */ 00153 pEventHook = pEventHook->pehNext; 00154 DestroyNotify(pNotify); 00155 return pEventHook; 00156 } 00157 00158 /* 00159 * CALLBACK 00160 * 00161 * This leaves the critical section. 00162 * We return the next Event Hook in the list so that the caller doesn't 00163 * have to lock pEventHook. 00164 */ 00165 UserAssert((pNotify->dwWEFlags & WEF_DEFERNOTIFY) == 0); 00166 00167 ThreadLockAlways(pEventHook, &tlpEventHook); 00168 00169 UserAssertMsg1(pNotify->ptiReceiver == NULL, 00170 "pNotify %#p is already in callback! Reentrant?", pNotify); 00171 pNotify->ptiReceiver = ptiCurrent; 00172 00173 if (!pEventHook->fSync) { 00174 UserAssert(pEventHook->ihmod == -1); 00175 pfn = (WINEVENTPROC)pEventHook->offPfn; 00176 } else { 00177 pfn = xxxGetEventProc(pEventHook); 00178 } 00179 if (pfn) { 00180 xxxClientCallWinEventProc(pfn, pEventHook, pNotify); 00181 DBGVERIFYNOTIFY(pNotify); 00182 DBGVERIFYEVENTHOOK(pEventHook); 00183 UserAssert(pEventHook->head.cLockObj); 00184 } 00185 00186 pNotify->ptiReceiver = NULL; 00187 00188 /* 00189 * Save the next item in the list, ThreadUnlock() may destroy pEventHook. 00190 * DestroyNotify() may also kill the event if it is a zombie (destroyed 00191 * but being used, waiting for use count to go to 0 before being freed). 00192 */ 00193 pEventHook = pEventHook->pehNext; 00194 ThreadUnlock(&tlpEventHook); 00195 00196 /* 00197 * We are done with the notification. Kill it. 00198 * 00199 * NOTE that DestroyNotify does not yield, which is why we can hang on 00200 * to the pehNext field above around this call. 00201 * 00202 * NOTE ALSO that DestroyNotify will kill the event it references if the 00203 * ref count goes down to zero and it was zombied earlier. 00204 */ 00205 DestroyNotify(pNotify); 00206 00207 return pEventHook; 00208 } 00209 00210 00211 /****************************************************************************\ 00212 * xxxFlushDeferredWindowEvents() 00213 * 00214 * Process notifications that were queued up during DeferWinEventNotify() 00215 \****************************************************************************/ 00216 VOID 00217 xxxFlushDeferredWindowEvents() 00218 { 00219 PNOTIFY pNotify; 00220 DWORD idCurrentThread = W32GetCurrentTID(); 00221 00222 if (idCurrentThread == 0) { 00223 RIPMSG0(RIP_ERROR, "processing deferred notifications before we have a pti!"); 00224 // return; 00225 } 00226 00227 UserAssert(IsWinEventNotifyDeferredOK()); 00228 00229 pNotify = gpPendingNotifies; 00230 while (pNotify) { 00231 if (((pNotify->dwWEFlags & WEF_DEFERNOTIFY) == 0) || 00232 (pNotify->idSenderThread != idCurrentThread)) { 00233 // UserAssert(pNotify->idSenderThread == idCurrentThread); // just testing! 00234 pNotify = pNotify->pNotifyNext; 00235 } else { 00236 /* 00237 * Clear WEF_DEFERNOTIFY so that if we recurse in the callback 00238 * we won't try to send this notification again. 00239 */ 00240 pNotify->dwWEFlags &= ~WEF_DEFERNOTIFY; 00241 #if DBG 00242 gnDeferredWinEvents--; 00243 #endif 00244 /* 00245 * We shouldn't have deferred ASYNC notifications: we should have 00246 * posted them immediately. 00247 */ 00248 UserAssert((pNotify->dwWEFlags & WEF_ASYNC) == 0); 00249 xxxProcessNotifyWinEvent(pNotify); 00250 /* 00251 * Start again at the head of the list, in case it munged during 00252 * the callback. 00253 */ 00254 pNotify = gpPendingNotifies; 00255 } 00256 } 00257 } 00258 00259 00260 /*****************************************************************************\ 00261 * 00262 * xxxWindowEvent 00263 * 00264 * Send, Post or Defer a Win Event notification, depending on what Win Event 00265 * hooks are installed and what the context of the caller is. 00266 * 00267 * The caller should test FWINABLE() and only call xxxWindowEvent if it is TRUE, 00268 * that way only costs a few clocks if no Win Event hooks are set. 00269 * 00270 * Caller shouldn't lock pwnd, because xxxWindowEvent() will do it. 00271 * 00272 \*****************************************************************************/ 00273 VOID 00274 xxxWindowEvent( 00275 DWORD event, 00276 PWND pwnd, 00277 LONG idObject, 00278 LONG idChild, 00279 DWORD dwFlags) 00280 { 00281 PEVENTHOOK peh; 00282 PEVENTHOOK pehNext; 00283 PTHREADINFO ptiCurrent, ptiEvent; 00284 DWORD dwTime; 00285 PPROCESSINFO ppiEvent; 00286 DWORD idEventThread; 00287 HANDLE hEventProcess; 00288 PNOTIFY pNotify; 00289 TL tlpwnd; 00290 TL tlpti; 00291 00292 /* 00293 * Do not bother with CheckLock(pwnd) - we ThreadLock it below. 00294 */ 00295 UserAssert(FWINABLE()); 00296 00297 /* 00298 * This thread is in startup, and has not yet had it's pti set up 00299 * This is pretty rare, but sometimes encountered in stress. 00300 * Test gptiCurrent to avoid the UserAssert(gptiCurrent) in PtiCurrent() 00301 */ 00302 if (gptiCurrent == NULL) { 00303 RIPMSG3(RIP_WARNING, "Ignore WinEvent %lx %#p %lx... no PtiCurrent yet", 00304 event, pwnd, idObject); 00305 return; 00306 } 00307 ptiCurrent = PtiCurrent(); 00308 00309 /* 00310 * Don't bother with destroyed windows 00311 */ 00312 if (pwnd && TestWF(pwnd, WFDESTROYED)) { 00313 RIPMSG3(RIP_WARNING, 00314 "Ignore WinEvent %lx %#p %lx... pwnd already destroyed", 00315 event, pwnd, idObject); 00316 return; 00317 } 00318 00319 /* 00320 * Under some special circumstances we have to defer 00321 */ 00322 if (ptiCurrent->TIF_flags & (TIF_DISABLEHOOKS | TIF_INCLEANUP)) { 00323 dwFlags |= WEF_DEFERNOTIFY; 00324 } 00325 00326 /* 00327 * Determine process and thread issuing the event notification 00328 */ 00329 if ((dwFlags & WEF_USEPWNDTHREAD) && pwnd) { 00330 ptiEvent = GETPTI(pwnd); 00331 } else { 00332 ptiEvent = ptiCurrent; 00333 } 00334 idEventThread = TIDq(ptiEvent); 00335 ppiEvent = ptiEvent->ppi; 00336 hEventProcess = ptiEvent->pEThread->Cid.UniqueProcess; 00337 00338 dwTime = NtGetTickCount(); 00339 00340 ThreadLockWithPti(ptiCurrent, pwnd, &tlpwnd); 00341 ThreadLockPti(ptiCurrent, ptiEvent, &tlpti); 00342 00343 /* 00344 * If we're not deferring the current notification process any pending 00345 * deferred notifications before proceeding with the current notification 00346 */ 00347 if (!(dwFlags & WEF_DEFERNOTIFY)) { 00348 xxxFlushDeferredWindowEvents(); 00349 } 00350 00351 for (peh = gpWinEventHooks; peh; peh = pehNext) { 00352 DBGVERIFYEVENTHOOK(peh); 00353 pehNext = peh->pehNext; 00354 00355 // 00356 // Is event in the right range? And is it for this process/thread? 00357 // Note that we skip destroyed events. They will be freed any 00358 // second now, it's just that yielding may have caused reentrancy. 00359 // 00360 // If the caller said to ignore events on his own thread, make sure 00361 // we skip them. 00362 // 00363 if (!peh->fDestroyed && 00364 (peh->eventMin <= event) && 00365 (event <= peh->eventMax) && 00366 (!peh->hEventProcess || (peh->hEventProcess == hEventProcess)) && 00367 (!peh->fIgnoreOwnProcess || (ppiEvent != GETPTI(peh)->ppi)) && 00368 (!peh->idEventThread || (peh->idEventThread == idEventThread)) && 00369 (!peh->fIgnoreOwnThread || (ptiEvent != GETPTI(peh))) && 00370 // temp fix from SP3 - best to architect events on a per-desktop 00371 // basis, with a separate pWinEventHook list per desktop. (IanJa) 00372 (peh->head.pti->rpdesk == ptiCurrent->rpdesk)) 00373 { 00374 /* 00375 * Don't create new notifications for zombie event hooks. 00376 * When an event is destroyed, it stays as a zombie until the in-use 00377 * count goes to zero (all it's async and deferred notifies gone) 00378 */ 00379 if (HMIsMarkDestroy(peh)) { 00380 break; 00381 } 00382 00383 UserAssert(peh->fDestroyed == 0); 00384 00385 if ((pNotify = CreateNotify(peh, event, pwnd, idObject, 00386 idChild, ptiEvent, dwTime)) == NULL) { 00387 break; 00388 } 00389 pNotify->dwWEFlags |= dwFlags; 00390 00391 /* 00392 * If it's async, don't defer it: post it straight away. 00393 */ 00394 if (pNotify->dwWEFlags & WEF_ASYNC) { 00395 pNotify->dwWEFlags &= ~WEF_DEFERNOTIFY; 00396 } 00397 00398 if (pNotify->dwWEFlags & WEF_DEFERNOTIFY) { 00399 #if DBG 00400 gnDeferredWinEvents++; 00401 #endif 00402 DBGVERIFYNOTIFY(pNotify); 00403 } else { 00404 pehNext = xxxProcessNotifyWinEvent(pNotify); 00405 } 00406 } 00407 } 00408 00409 ThreadUnlockPti(ptiCurrent, &tlpti); 00410 ThreadUnlock(&tlpwnd); 00411 } 00412 00413 /****************************************************************************\ 00414 * 00415 * CreateNotify() 00416 * 00417 * Gets a pointer to a NOTIFY struct that we can then propagate to our 00418 * event window via Send/PostMessage. We have to do this since we want to 00419 * (pass on a lot more data then can be packed in the parameters. 00420 * 00421 * We have one cached struct so we avoid lots of allocs and frees in the 00422 * most common case of just one outstanding notification. 00423 \****************************************************************************/ 00424 PNOTIFY 00425 CreateNotify(PEVENTHOOK pEvent, DWORD event, PWND pwnd, LONG idObject, 00426 LONG idChild, PTHREADINFO ptiSender, DWORD dwTime) 00427 { 00428 PNOTIFY pNotify; 00429 UserAssert(pEvent != NULL); 00430 00431 // 00432 // Get a pointer. From cache if available. 00433 // IanJa - change this to allocate from zone a la AllocQEntry?? 00434 // 00435 if (!fNotifyCacheInUse) { 00436 fNotifyCacheInUse = TRUE; 00437 pNotify = &notifyCache; 00438 #if DBG 00439 // 00440 // Make sure we aren't forgetting to set any fields. 00441 // 00442 // DebugFillBuffer(pNotify, sizeof(NOTIFY)); 00443 #endif 00444 } else { 00445 pNotify = (PNOTIFY)UserAllocPool(sizeof(NOTIFY), TAG_NOTIFY); 00446 if (!pNotify) 00447 return NULL; 00448 } 00449 00450 00451 /* 00452 * Fill in the notify block. 00453 */ 00454 pNotify->spEventHook = NULL; 00455 Lock(&pNotify->spEventHook, pEvent); 00456 pNotify->hwnd = HW(pwnd); 00457 pNotify->event = event; 00458 pNotify->idObject = idObject; 00459 pNotify->idChild = idChild; 00460 pNotify->idSenderThread = TIDq(ptiSender); 00461 UserAssert(pNotify->idSenderThread != 0); 00462 pNotify->dwEventTime = dwTime; 00463 pNotify->dwWEFlags = pEvent->fSync ? 0 : WEF_ASYNC; 00464 pNotify->pNotifyNext = NULL; 00465 pNotify->ptiReceiver = NULL; 00466 #if DBG 00467 gnNotifies++; 00468 #endif 00469 00470 /* 00471 * The order of non-deferred notifications doesn't matter; they are here 00472 * simply for cleanup/in-use tracking. However, deferred notifications must 00473 * be ordered with most recent at the end, so just order them all that way. 00474 */ 00475 if (gpPendingNotifies) { 00476 UserAssert(gpLastPendingNotify); 00477 UserAssert(gpLastPendingNotify->pNotifyNext == NULL); 00478 gpLastPendingNotify->pNotifyNext = pNotify; 00479 } else { 00480 gpPendingNotifies = pNotify; 00481 } 00482 gpLastPendingNotify = pNotify; 00483 00484 return pNotify; 00485 } 00486 00487 00488 /****************************************************************************\ 00489 * 00490 * RemoveNotify() 00491 * 00492 * NOTE: This does NOT yield. 00493 \****************************************************************************/ 00494 VOID 00495 RemoveNotify(PNOTIFY *ppNotify) 00496 { 00497 PNOTIFY pNotifyRemove; 00498 00499 pNotifyRemove = *ppNotify; 00500 00501 /* 00502 * First, get it out of the pending list. 00503 */ 00504 *ppNotify = pNotifyRemove->pNotifyNext; 00505 00506 #if DBG 00507 if (pNotifyRemove->dwWEFlags & WEF_DEFERNOTIFY) { 00508 UserAssert(gnDeferredWinEvents > 0); 00509 gnDeferredWinEvents--; 00510 } 00511 #endif 00512 if (*ppNotify == NULL) { 00513 /* 00514 * Removing last notify, so fix up gpLastPendingNotify: 00515 * If list now empty, there is no last item. 00516 */ 00517 if (gpPendingNotifies == NULL) { 00518 gpLastPendingNotify = NULL; 00519 } else { 00520 gpLastPendingNotify = CONTAINING_RECORD(ppNotify, NOTIFY, pNotifyNext); 00521 } 00522 } 00523 UserAssert((gpPendingNotifies == 0) || (gpPendingNotifies > (PNOTIFY)100)); 00524 00525 DBGVERIFYEVENTHOOK(pNotifyRemove->spEventHook); 00526 00527 /* 00528 * This may cause the win event hook to be freed. 00529 */ 00530 Unlock(&pNotifyRemove->spEventHook); 00531 00532 // 00533 // Now free it. Either put it back in the cache if it is the cache, 00534 // or really free it otherwise. 00535 // 00536 if (pNotifyRemove == &notifyCache) { 00537 UserAssert(fNotifyCacheInUse); 00538 fNotifyCacheInUse = FALSE; 00539 } else { 00540 UserFreePool(pNotifyRemove); 00541 } 00542 #if DBG 00543 UserAssert(gnNotifies > 0); 00544 gnNotifies--; 00545 #endif 00546 } 00547 00548 00549 /*****************************************************************************\ 00550 * 00551 * DestroyNotify() 00552 * 00553 * NOTE: This does NOT yield. 00554 * 00555 * This gets the notification out of our pending list and frees the local 00556 * memory it uses. 00557 * 00558 * This function is called 00559 * (1) NORMALLY: After returning from calling the notify proc 00560 * (2) CLEANUP: When a thread goes away, we cleanup async notifies it 00561 * hasn't received, and sync notifies it was in the middle of trying 00562 * to call (i.e. the event proc faulted). 00563 * 00564 \*****************************************************************************/ 00565 VOID 00566 DestroyNotify(PNOTIFY pNotifyDestroy) 00567 { 00568 PNOTIFY *ppNotify; 00569 PNOTIFY pNotifyT; 00570 00571 DBGVERIFYNOTIFY(pNotifyDestroy); 00572 00573 /* 00574 * Either this notify isn't currently in the process of calling back 00575 * (which means ptiReceiver is NULL) or the thread destroying it 00576 * must be the one that was calling back (which means this thread 00577 * was destroyed during the callback and is cleaning up). 00578 */ 00579 UserAssert((pNotifyDestroy->ptiReceiver == NULL) || 00580 (pNotifyDestroy->ptiReceiver == PtiCurrent())); 00581 00582 ppNotify = &gpPendingNotifies; 00583 while (pNotifyT = *ppNotify) { 00584 if (pNotifyT == pNotifyDestroy) { 00585 RemoveNotify(ppNotify); 00586 return; 00587 } else { 00588 ppNotify = &pNotifyT->pNotifyNext; 00589 } 00590 } 00591 RIPMSG1(RIP_ERROR, "DestroyNotify %#p - not found", pNotifyDestroy); 00592 } 00593 00594 00595 00596 /***************************************************************************\ 00597 * FreeThreadsWinEvents 00598 * 00599 * During 'exit-list' processing this function is called to free any WinEvent 00600 * notifications and WinEvent hooks created by the current thread. 00601 * 00602 * Notifications that remain may be: 00603 * o Posted notifications (async) 00604 * o Notifications in xxxClientCallWinEventProc (sync) 00605 * o Deferred notifications (should be sync only) 00606 * Destroy the sync notifications, because we cannot do callbacks 00607 * while in thread cleanup. 00608 * Leave the posted (async) notifications alone: they're on their way already. 00609 * 00610 * History: 00611 * 11-11-96 IanJa Created. 00612 \***************************************************************************/ 00613 00614 VOID 00615 FreeThreadsWinEvents(PTHREADINFO pti) 00616 { 00617 PEVENTHOOK peh, pehNext; 00618 PNOTIFY pn, pnNext; 00619 DWORD idCurrentThread = W32GetCurrentTID(); 00620 00621 /* 00622 * Loop through all the notifications 00623 */ 00624 for (pn = gpPendingNotifies; pn; pn = pnNext) { 00625 pnNext = pn->pNotifyNext; 00626 00627 /* 00628 * Only destroy sync notifications that belong to this thread 00629 * and are not currently calling back i.e. ptiReceiver must be NULL. 00630 * Otherwise, when we come back from the callback in 00631 * xxxProcessNotifyWinEvent we will operate on a freed notify. 00632 * Also destroy the notification if the receiver is going away 00633 * or else it gets leaked as long as the sender is alive. 00634 */ 00635 if ((pn->idSenderThread == idCurrentThread && 00636 pn->ptiReceiver == NULL) || (pn->ptiReceiver == pti)) { 00637 if ((pn->dwWEFlags & WEF_ASYNC) == 0) { 00638 UserAssert((pn->dwWEFlags & WEF_POSTED) == 0); 00639 DestroyNotify(pn); 00640 } 00641 } 00642 } 00643 00644 peh = gpWinEventHooks; 00645 while (peh) { 00646 pehNext = peh->pehNext; 00647 if (GETPTI(peh) == pti) { 00648 DestroyEventHook(peh); 00649 } 00650 peh = pehNext; 00651 } 00652 // Async notification not yet processed may still be posted in a queue, 00653 // pending being read and processed (gnNotifies > 0), although the 00654 // originating hook has now been unhooked (maybe gpWinEventHooks == NULL) 00655 // so the following assert is no good: 00656 // UserAssert(gpWinEventHooks || (!gpWinEventHooks && !gnNotifies)); 00657 } 00658 00659 00660 // -------------------------------------------------------------------------- 00661 // 00662 // _SetWinEventHook() 00663 // 00664 // This installs a win event hook. 00665 // 00666 // 00667 // If hEventProcess set but idEventThread = 0, hook all threads in process. 00668 // If idEventThread set but hEventProcess = NULL, hook single thread only. 00669 // If neither are set, hook everything. 00670 // If both are set ?? 00671 // 00672 // -------------------------------------------------------------------------- 00673 PEVENTHOOK 00674 _SetWinEventHook( 00675 DWORD eventMin, 00676 DWORD eventMax, 00677 HMODULE hmodWinEventProc, 00678 PUNICODE_STRING pstrLib, 00679 WINEVENTPROC pfnWinEventProc, 00680 HANDLE hEventProcess, 00681 DWORD idEventThread, 00682 DWORD dwFlags) 00683 { 00684 PEVENTHOOK pEventNew; 00685 PTHREADINFO ptiCurrent; 00686 00687 int ihmod; 00688 00689 ptiCurrent = PtiCurrent(); 00690 00691 // 00692 // If exiting, fail the call. 00693 // 00694 if (ptiCurrent->TIF_flags & TIF_INCLEANUP) { 00695 RIPMSG1(RIP_ERROR, "SetWinEventHook: Fail call - thread %#p in cleanup", ptiCurrent); 00696 return NULL; 00697 } 00698 00699 /* 00700 * Check to see if filter proc is valid. 00701 */ 00702 if (pfnWinEventProc == NULL) { 00703 RIPERR0(ERROR_INVALID_FILTER_PROC, RIP_VERBOSE, "pfnWinEventProc == NULL"); 00704 return NULL; 00705 } 00706 00707 if (eventMin > eventMax) { 00708 RIPERR0(ERROR_INVALID_HOOK_FILTER, RIP_VERBOSE, "eventMin > eventMax"); 00709 return NULL; 00710 } 00711 00712 if (dwFlags & WINEVENT_INCONTEXT) { 00713 /* 00714 * WinEventProc to be called in context of hooked thread, so needs a DLL 00715 */ 00716 if (hmodWinEventProc == NULL) { 00717 RIPERR0(ERROR_HOOK_NEEDS_HMOD, RIP_VERBOSE, ""); 00718 return NULL; 00719 } else if (pstrLib == NULL) { 00720 /* 00721 * If we got an hmod, we should get a DLL name too! 00722 */ 00723 RIPERR1(ERROR_DLL_NOT_FOUND, RIP_ERROR, 00724 "hmod %#p, but no lib name", hmodWinEventProc); 00725 return NULL; 00726 } 00727 ihmod = GetHmodTableIndex(pstrLib); 00728 if (ihmod == -1) { 00729 RIPERR0(ERROR_MOD_NOT_FOUND, RIP_VERBOSE, ""); 00730 return NULL; 00731 } 00732 } else { 00733 ihmod = -1; // means no DLL is required 00734 hmodWinEventProc = 0; 00735 } 00736 00737 /* 00738 * Check the thread id, check it is a GUI thread. 00739 */ 00740 if (idEventThread != 0) { 00741 PTHREADINFO ptiT; 00742 00743 ptiT = PtiFromThreadId(idEventThread); 00744 if ((ptiT == NULL) || 00745 !(ptiT->TIF_flags & TIF_GUITHREADINITIALIZED)) { 00746 RIPERR1(ERROR_INVALID_THREAD_ID, RIP_VERBOSE, "pti %#p", ptiT); 00747 return NULL; 00748 } 00749 } 00750 00751 // 00752 // Create the window for async events first. Creating it might yield, 00753 // so we want to do this before we've touched our event array. 00754 // 00755 // NOTE that USER itself will not pass on window creation/destruction 00756 // notifications for 00757 // * IME windows 00758 // * OLE windows 00759 // * RPC windows 00760 // * Event windows 00761 // 00762 00763 // 00764 // Get a new event. 00765 // 00766 pEventNew = (PEVENTHOOK)HMAllocObject(ptiCurrent, NULL, 00767 TYPE_WINEVENTHOOK, sizeof(EVENTHOOK)); 00768 if (!pEventNew) 00769 return NULL; 00770 00771 // 00772 // Fill in the new event. 00773 // 00774 pEventNew->eventMin = (UINT)eventMin; 00775 pEventNew->eventMax = (UINT)eventMax; 00776 00777 // pEventNew->f32Bit = ((dwFlags & WINEVENT_32BITCALLER) != 0); 00778 pEventNew->fIgnoreOwnThread = ((dwFlags & WINEVENT_SKIPOWNTHREAD) != 0); 00779 pEventNew->fIgnoreOwnProcess = ((dwFlags & WINEVENT_SKIPOWNPROCESS) != 0); 00780 pEventNew->fDestroyed = FALSE; 00781 pEventNew->fSync = ((dwFlags & WINEVENT_INCONTEXT) != 0); 00782 00783 pEventNew->hEventProcess = hEventProcess; 00784 pEventNew->idEventThread = idEventThread; 00785 // pEventNew->cInUse = 0; 00786 00787 pEventNew->ihmod = ihmod; 00788 00789 /* 00790 * Add a dependency on this module - meaning, increment a count 00791 * that simply counts the number of hooks set into this module. 00792 */ 00793 if (pEventNew->ihmod >= 0) { 00794 AddHmodDependency(pEventNew->ihmod); 00795 } 00796 00797 /* 00798 * If pfnWinEventProc is in caller's process and no DLL is involved, 00799 * then pEventNew->offPfn is the actual address. 00800 */ 00801 pEventNew->offPfn = ((ULONG_PTR)pfnWinEventProc) - ((ULONG_PTR)hmodWinEventProc); 00802 00803 // 00804 // 00805 // Link our event into the master list. 00806 // 00807 // Note that we count on USER to not generate any events when installing 00808 // our hook. The caller can't handle it yet since he hasn't got back 00809 // his event handle from this call. 00810 // 00811 pEventNew->pehNext = gpWinEventHooks; 00812 gpWinEventHooks = pEventNew; 00813 SET_SRVIF(SRVIF_WINEVENTHOOKS); 00814 00815 return pEventNew; 00816 } 00817 00818 /****************************************************************************\ 00819 * UnhookWinEvent() 00820 * 00821 * Unhooks a win event hook. We of course sanity check that this thread is 00822 * the one which installed the hook. We have to: We are going to destroy 00823 * the IPC window and that must be in context. 00824 * 00825 \****************************************************************************/ 00826 BOOL 00827 _UnhookWinEvent(PEVENTHOOK pEventUnhook) 00828 { 00829 DBGVERIFYEVENTHOOK(pEventUnhook); 00830 00831 if (HMIsMarkDestroy(pEventUnhook) || (GETPTI(pEventUnhook) != PtiCurrent())) { 00832 // 00833 // We do this to avoid someone calling UnhookWinEvent() the first 00834 // time, then somehow getting control again and calling it a second 00835 // time before we've managed to free up the event since someone was 00836 // in the middle of using it at the first UWE call. 00837 // 00838 00839 RIPERR0(ERROR_INVALID_HANDLE, RIP_WARNING, "_UnhookWinEvent: Invalid event hook"); 00840 return FALSE; 00841 } 00842 00843 // 00844 // Purge this baby if all notifications are done. 00845 // * if there are SYNC ones pending, the caller will clean this up 00846 // upon the return from calling the event 00847 // * if there are ASYNC ones pending, the receiver will not call 00848 // the event and clean it up when he gets it. 00849 // 00850 00851 // 00852 // NOTE that DestroyEventHook() does not yield! 00853 // 00854 DestroyEventHook(pEventUnhook); 00855 00856 return TRUE; 00857 } 00858 00859 00860 00861 00862 /*****************************************************************************\ 00863 * 00864 * DestroyEventHook() 00865 * 00866 * NOTE that this does NOT yield 00867 * 00868 * Destroys an event when the ref count has gone down to zero. It may 00869 * happen 00870 * * in the event generator's context, after returning from a callback 00871 * and the ref count dropped to zero, if sync 00872 * * in the event installer's context, after returning from a callback 00873 * and the ref count dropped to zero if async 00874 * * in the event installer's context, if on _UnhookWinEvent() the event 00875 * was not in use at all 00876 * 00877 \*****************************************************************************/ 00878 VOID 00879 DestroyEventHook(PEVENTHOOK pEventDestroy) 00880 { 00881 PEVENTHOOK *ppEvent; 00882 PEVENTHOOK pEventT; 00883 00884 DBGVERIFYEVENTHOOK(pEventDestroy); 00885 UserAssert(gpWinEventHooks); 00886 00887 /* 00888 * Mark this event as destroyed, but don't remove it from the event list 00889 * until its lock count goes to 0 - we may be traversing the list 00890 * within xxxWindowEvent, so we mustn't break the link to the next hook. 00891 */ 00892 pEventDestroy->fDestroyed = TRUE; 00893 00894 /* 00895 * If the object is locked, mark it for destroy but don't free it yet. 00896 */ 00897 if (!HMMarkObjectDestroy(pEventDestroy)) 00898 return; 00899 00900 /* 00901 * Remove this from our event list. 00902 */ 00903 for (ppEvent = &gpWinEventHooks; pEventT = *ppEvent; ppEvent = &pEventT->pehNext) { 00904 if (pEventT == pEventDestroy) { 00905 *ppEvent = pEventDestroy->pehNext; 00906 break; 00907 } 00908 } 00909 UserAssert(pEventT); 00910 SET_OR_CLEAR_SRVIF(SRVIF_WINEVENTHOOKS, gpWinEventHooks); 00911 00912 /* 00913 * Make sure each hooked thread will unload the hook proc DLL 00914 */ 00915 if (pEventDestroy->ihmod >= 0) { 00916 RemoveHmodDependency(pEventDestroy->ihmod); 00917 } 00918 00919 /* 00920 * Free this pointer. 00921 */ 00922 HMFreeObject(pEventDestroy); 00923 00924 return; 00925 } 00926 00927 /***************************************************************************\ 00928 * 00929 * xxxGetEventProc() 00930 * 00931 * For sync events, this gets the address to call. If 16-bits, then just 00932 * return the installed address. If 32-bits, we need to load the library 00933 * if not in the same process as the installer. 00934 \***************************************************************************/ 00935 WINEVENTPROC 00936 xxxGetEventProc(PEVENTHOOK pEventOrg) { 00937 PTHREADINFO ptiCurrent; 00938 00939 UserAssert(pEventOrg); 00940 UserAssert(pEventOrg->fSync); 00941 UserAssert(pEventOrg->ihmod >= 0); 00942 UserAssert(pEventOrg->offPfn != 0); 00943 00944 CheckLock(pEventOrg); 00945 00946 /* 00947 * Make sure the hook is still around before we 00948 * try and call it. 00949 */ 00950 if (HMIsMarkDestroy(pEventOrg)) { 00951 return NULL; 00952 } 00953 00954 ptiCurrent = PtiCurrent(); 00955 00956 /* 00957 * Make sure the DLL for this hook, if any, has been loaded 00958 * for the current process. 00959 */ 00960 if ((pEventOrg->ihmod != -1) && 00961 (TESTHMODLOADED(ptiCurrent, pEventOrg->ihmod) == 0)) { 00962 00963 /* 00964 * Try loading the library, since it isn't loaded in this processes 00965 * context. The hook is alrerady locked, so it won't go away while 00966 * we're loading this library. 00967 */ 00968 if (xxxLoadHmodIndex(pEventOrg->ihmod, FALSE) == NULL) { 00969 return NULL; 00970 } 00971 } 00972 00973 /* 00974 * While we're still inside the critical section make sure the 00975 * hook hasn't been 'freed'. If so just return NULL. 00976 * IanJa - since WinEvent has already been called, you might think that we 00977 * should pass the event on, but the hooker may not be expecting this after 00978 * having cancelled the hook! In any case, seems we may have two ways 00979 * of detecting that this hook has been removed: 00980 */ 00981 00982 /* 00983 * Make sure the hook is still around before we 00984 * try and call it. 00985 */ 00986 if (HMIsMarkDestroy(pEventOrg)) { 00987 return NULL; 00988 } 00989 00990 return (WINEVENTPROC)PFNHOOK(pEventOrg); 00991 }

Generated on Sat May 15 19:42:25 2004 for test by doxygen 1.3.7