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

  1. /**************************** Module Header ********************************\
  2. * Module Name: winable.c
  3. *
  4. * This has the stuff for WinEvents:
  5. * NotifyWinEvent
  6. * _SetWinEventHook
  7. * UnhookWinEventHook
  8. *
  9. * All other additions to USER for Active Accessibility are in winable2.c.
  10. *
  11. * Copyright (c) 1985 - 1999, Microsoft Corporation
  12. *
  13. * History:
  14. * Based on snapshot taken from:
  15. * \\trango\slmro\proj\win\src\CORE\access\user_40\user32 on 8/29/96
  16. * 08-30-96 IanJa Ported from Windows '95
  17. \***************************************************************************/
  18. #include "precomp.h"
  19. #pragma hdrstop
  20. #if DBG
  21. int gnNotifies;
  22. __inline VOID DBGVERIFYEVENTHOOK(
  23. PEVENTHOOK peh)
  24. {
  25. HMValidateCatHandleNoSecure(PtoH(peh), TYPE_WINEVENTHOOK);
  26. UserAssert(peh->eventMin <= peh->eventMax);
  27. }
  28. __inline VOID DBGVERIFYNOTIFY(
  29. PNOTIFY pNotify)
  30. {
  31. UserAssert(pNotify->spEventHook != NULL);
  32. UserAssert(pNotify->spEventHook->fSync || (pNotify->dwWEFlags & WEF_ASYNC));
  33. }
  34. #else
  35. #define DBGVERIFYEVENTHOOK(peh)
  36. #define DBGVERIFYNOTIFY(pNotify)
  37. #endif
  38. /*
  39. * Pending Event Notifications (sync and async).
  40. */
  41. static NOTIFY notifyCache;
  42. static BOOL fNotifyCacheInUse;
  43. /*
  44. * Local to this module.
  45. */
  46. WINEVENTPROC xxxGetEventProc(
  47. PEVENTHOOK pEventOrg);
  48. PNOTIFY CreateNotify(
  49. PEVENTHOOK peh,
  50. DWORD event,
  51. PWND pwnd,
  52. LONG idObject,
  53. LONG idChild,
  54. PTHREADINFO ptiEvent,
  55. DWORD dwTime);
  56. /*****************************************************************************\
  57. * xxxProcessNotifyWinEvent
  58. *
  59. * Posts or Sends a WinEvent notification.
  60. *
  61. * Post: uses PostEventMesage - does not leave the critical section.
  62. * Send: makes a callback to user-mode - does leave the critical section.
  63. *
  64. * If this is a system thread (RIT, Desktop or Console) then synchronously
  65. * hooked (WINEVENT_INCONTEXT) events are forced to be asynchronous.
  66. *
  67. * We return the next win event hook in the list.
  68. \*****************************************************************************/
  69. PEVENTHOOK xxxProcessNotifyWinEvent(
  70. PNOTIFY pNotify)
  71. {
  72. WINEVENTPROC pfn;
  73. PEVENTHOOK pEventHook;
  74. TL tlpEventHook;
  75. PTHREADINFO ptiCurrent = PtiCurrent();
  76. pEventHook = pNotify->spEventHook;
  77. DBGVERIFYEVENTHOOK(pEventHook);
  78. UserAssert(pEventHook->head.cLockObj);
  79. if (((pNotify->dwWEFlags & (WEF_ASYNC | WEF_POSTED)) == WEF_ASYNC)
  80. ||
  81. (ptiCurrent->TIF_flags & (TIF_SYSTEMTHREAD | TIF_CSRSSTHREAD | TIF_INCLEANUP))
  82. ||
  83. (!RtlEqualLuid(&GETPTI(pEventHook)->ppi->luidSession, &ptiCurrent->ppi->luidSession) &&
  84. !(ptiCurrent->TIF_flags & TIF_ALLOWOTHERACCOUNTHOOK))
  85. ||
  86. (GETPTI(pEventHook)->ppi != ptiCurrent->ppi &&
  87. IsRestricted(GETPTI(pEventHook)->pEThread))
  88. #if defined(_WIN64)
  89. ||
  90. ((GETPTI(pEventHook)->TIF_flags & TIF_WOW64) != (ptiCurrent->TIF_flags & TIF_WOW64))
  91. #endif
  92. ) {
  93. /*
  94. * POST
  95. *
  96. * WinEvent Hook set without WINEVENT_INCONTEXT flag are posted;
  97. * Events from system threads are posted because there is no user-mode
  98. * part to callback to;
  99. * Console is not permitted to load DLLs, so we must post back to the
  100. * hooking application;
  101. * DLLs can not be loaded cross bit type(32bit to 64bit) on 64bit NT
  102. * so we must post(It may be usefull to let the app be aware and
  103. * even supply both a 32bit and a 64bit DLL that are aware of each other);
  104. * Threads in cleanup can't get called back, so turn their
  105. * notifications into async ones. (Better late than never).
  106. *
  107. * If forcing these events ASYNC is unacceptable, we might consider
  108. * doing system/console SYNC events like low-level hooks (sync with
  109. * timeout: but may have to post it if the timeout expires) - IanJa
  110. */
  111. PQ pqReceiver = GETPTI(pEventHook)->pq;
  112. PEVENTHOOK pEventHookNext = pEventHook->pehNext;
  113. BEGINATOMICCHECK();
  114. DBGVERIFYNOTIFY(pNotify);
  115. pNotify->dwWEFlags |= WEF_POSTED | WEF_ASYNC;
  116. if (!pqReceiver || (GETPTI(pEventHook) == gptiRit) ||
  117. pEventHook->fDestroyed ||
  118. !PostEventMessage(GETPTI(pEventHook), pqReceiver,
  119. QEVENT_NOTIFYWINEVENT,
  120. NULL, 0, 0, (LPARAM)pNotify)) {
  121. /*
  122. * If the receiver doesn't have a queue or the
  123. * post failed (low memory), cleanup what we just
  124. * created.
  125. * Note: destroying the notification may destroy pEventHook too.
  126. */
  127. RIPMSG2(RIP_WARNING,
  128. "Failed to post NOTIFY at 0x%p, time %lx",
  129. pNotify,
  130. pNotify->dwEventTime);
  131. DestroyNotify(pNotify);
  132. }
  133. ENDATOMICCHECK();
  134. if (pEventHookNext) {
  135. DBGVERIFYEVENTHOOK(pEventHookNext);
  136. }
  137. return pEventHookNext;
  138. }
  139. /*
  140. * Don't call back if the hook has been destroyed (unhooked).
  141. */
  142. if (pEventHook->fDestroyed) {
  143. /*
  144. * Save the next hook since DestroyNotify may cause pEventHook to
  145. * be freed by unlocking it.
  146. */
  147. pEventHook = pEventHook->pehNext;
  148. DestroyNotify(pNotify);
  149. return pEventHook;
  150. }
  151. /*
  152. * CALLBACK
  153. *
  154. * This leaves the critical section.
  155. * We return the next Event Hook in the list so that the caller doesn't
  156. * have to lock pEventHook.
  157. */
  158. UserAssert((pNotify->dwWEFlags & WEF_DEFERNOTIFY) == 0);
  159. ThreadLockAlways(pEventHook, &tlpEventHook);
  160. UserAssertMsg1(pNotify->ptiReceiver == NULL,
  161. "pNotify %#p is already in callback! Reentrant?", pNotify);
  162. pNotify->ptiReceiver = ptiCurrent;
  163. if (!pEventHook->fSync) {
  164. UserAssert(pEventHook->ihmod == -1);
  165. pfn = (WINEVENTPROC)pEventHook->offPfn;
  166. } else {
  167. pfn = xxxGetEventProc(pEventHook);
  168. }
  169. if (pfn) {
  170. xxxClientCallWinEventProc(pfn, pEventHook, pNotify);
  171. DBGVERIFYNOTIFY(pNotify);
  172. DBGVERIFYEVENTHOOK(pEventHook);
  173. UserAssert(pEventHook->head.cLockObj);
  174. }
  175. pNotify->ptiReceiver = NULL;
  176. /*
  177. * Save the next item in the list, ThreadUnlock() may destroy pEventHook.
  178. * DestroyNotify() may also kill the event if it is a zombie (destroyed
  179. * but being used, waiting for use count to go to 0 before being freed).
  180. */
  181. pEventHook = pEventHook->pehNext;
  182. ThreadUnlock(&tlpEventHook);
  183. /*
  184. * We are done with the notification. Kill it.
  185. *
  186. * NOTE that DestroyNotify does not yield, which is why we can hang on
  187. * to the pehNext field above around this call.
  188. *
  189. * NOTE ALSO that DestroyNotify will kill the event it references if the
  190. * ref count goes down to zero and it was zombied earlier.
  191. */
  192. DestroyNotify(pNotify);
  193. return pEventHook;
  194. }
  195. /****************************************************************************\
  196. * xxxFlushDeferredWindowEvents
  197. *
  198. * Process notifications that were queued up during DeferWinEventNotify.
  199. \****************************************************************************/
  200. VOID xxxFlushDeferredWindowEvents(
  201. VOID)
  202. {
  203. PNOTIFY pNotify;
  204. DWORD idCurrentThread = W32GetCurrentTID();
  205. /*
  206. * If idCurrentThread is 0 we're not in danger of faulting, but we'll
  207. * needlessly walk the list of pending notifications (since all will be
  208. * ignored).
  209. */
  210. UserAssert(idCurrentThread != 0);
  211. UserAssert(IsWinEventNotifyDeferredOK());
  212. pNotify = gpPendingNotifies;
  213. while (pNotify) {
  214. if (((pNotify->dwWEFlags & WEF_DEFERNOTIFY) == 0) ||
  215. (pNotify->idSenderThread != idCurrentThread)) {
  216. pNotify = pNotify->pNotifyNext;
  217. } else {
  218. /*
  219. * Clear WEF_DEFERNOTIFY so that if we recurse in the callback
  220. * we won't try to send this notification again.
  221. */
  222. pNotify->dwWEFlags &= ~WEF_DEFERNOTIFY;
  223. #if DBG
  224. gnDeferredWinEvents--;
  225. #endif
  226. /*
  227. * We shouldn't have deferred ASYNC notifications: we should have
  228. * posted them immediately.
  229. */
  230. UserAssert((pNotify->dwWEFlags & WEF_ASYNC) == 0);
  231. xxxProcessNotifyWinEvent(pNotify);
  232. /*
  233. * Start again at the head of the list, in case it munged during
  234. * the callback.
  235. */
  236. pNotify = gpPendingNotifies;
  237. }
  238. }
  239. }
  240. /*****************************************************************************\
  241. *
  242. * xxxWindowEvent
  243. *
  244. * Send, Post or Defer a Win Event notification, depending on what Win Event
  245. * hooks are installed and what the context of the caller is.
  246. *
  247. * The caller should test FWINABLE() and only call xxxWindowEvent if it is TRUE,
  248. * that way only costs a few clocks if no Win Event hooks are set.
  249. *
  250. * Caller shouldn't lock pwnd, because xxxWindowEvent() will do it.
  251. *
  252. \*****************************************************************************/
  253. VOID
  254. xxxWindowEvent(
  255. DWORD event,
  256. PWND pwnd,
  257. LONG idObject,
  258. LONG idChild,
  259. DWORD dwFlags)
  260. {
  261. PEVENTHOOK peh;
  262. PEVENTHOOK pehNext;
  263. PTHREADINFO ptiCurrent, ptiEvent;
  264. DWORD dwTime;
  265. PPROCESSINFO ppiEvent;
  266. DWORD idEventThread;
  267. HANDLE hEventProcess;
  268. PNOTIFY pNotify;
  269. TL tlpwnd;
  270. TL tlpti;
  271. /*
  272. * Do not bother with CheckLock(pwnd) - we ThreadLock it below.
  273. */
  274. if (!FEVENTHOOKED(event)) {
  275. return;
  276. }
  277. /*
  278. * This thread is in startup, and has not yet had it's pti set up
  279. * This is pretty rare, but sometimes encountered in stress.
  280. * Test gptiCurrent to avoid the UserAssert(gptiCurrent) in PtiCurrent()
  281. */
  282. if (gptiCurrent == NULL) {
  283. RIPMSG3(RIP_WARNING, "Ignore WinEvent %lx %#p %lx... no PtiCurrent yet",
  284. event, pwnd, idObject);
  285. return;
  286. }
  287. ptiCurrent = PtiCurrent();
  288. /*
  289. * Don't bother with destroyed windows
  290. */
  291. if (pwnd && TestWF(pwnd, WFDESTROYED)) {
  292. RIPMSG3(RIP_WARNING,
  293. "Ignore WinEvent %lx %#p %lx... pwnd already destroyed",
  294. event, pwnd, idObject);
  295. return;
  296. }
  297. /*
  298. * Under some special circumstances we have to defer
  299. */
  300. if (ptiCurrent->TIF_flags & (TIF_DISABLEHOOKS | TIF_INCLEANUP)) {
  301. dwFlags |= WEF_DEFERNOTIFY;
  302. }
  303. /*
  304. * Determine process and thread issuing the event notification
  305. */
  306. if ((dwFlags & WEF_USEPWNDTHREAD) && pwnd) {
  307. ptiEvent = GETPTI(pwnd);
  308. } else {
  309. ptiEvent = ptiCurrent;
  310. }
  311. idEventThread = TIDq(ptiEvent);
  312. ppiEvent = ptiEvent->ppi;
  313. hEventProcess = PsGetThreadProcessId(ptiEvent->pEThread);
  314. dwTime = NtGetTickCount();
  315. ThreadLockWithPti(ptiCurrent, pwnd, &tlpwnd);
  316. ThreadLockPti(ptiCurrent, ptiEvent, &tlpti);
  317. /*
  318. * If we're not deferring the current notification process any pending
  319. * deferred notifications before proceeding with the current notification
  320. */
  321. if (!(dwFlags & WEF_DEFERNOTIFY)) {
  322. xxxFlushDeferredWindowEvents();
  323. }
  324. for (peh = gpWinEventHooks; peh; peh = pehNext) {
  325. DBGVERIFYEVENTHOOK(peh);
  326. pehNext = peh->pehNext;
  327. //
  328. // Is event in the right range? And is it for this process/thread?
  329. // Note that we skip destroyed events. They will be freed any
  330. // second now, it's just that yielding may have caused reentrancy.
  331. //
  332. // If the caller said to ignore events on his own thread, make sure
  333. // we skip them.
  334. //
  335. if (!peh->fDestroyed &&
  336. (peh->eventMin <= event) &&
  337. (event <= peh->eventMax) &&
  338. (!peh->hEventProcess || (peh->hEventProcess == hEventProcess)) &&
  339. (!peh->fIgnoreOwnProcess || (ppiEvent != GETPTI(peh)->ppi)) &&
  340. (!peh->idEventThread || (peh->idEventThread == idEventThread)) &&
  341. (!peh->fIgnoreOwnThread || (ptiEvent != GETPTI(peh))) &&
  342. // temp fix from SP3 - best to architect events on a per-desktop
  343. // basis, with a separate pWinEventHook list per desktop. (IanJa)
  344. (peh->head.pti->rpdesk == ptiCurrent->rpdesk))
  345. {
  346. /*
  347. * Don't create new notifications for zombie event hooks.
  348. * When an event is destroyed, it stays as a zombie until the in-use
  349. * count goes to zero (all it's async and deferred notifies gone)
  350. */
  351. if (HMIsMarkDestroy(peh)) {
  352. break;
  353. }
  354. UserAssert(peh->fDestroyed == 0);
  355. if ((pNotify = CreateNotify(peh, event, pwnd, idObject,
  356. idChild, ptiEvent, dwTime)) == NULL) {
  357. break;
  358. }
  359. pNotify->dwWEFlags |= dwFlags;
  360. /*
  361. * If it's async, don't defer it: post it straight away.
  362. */
  363. if (pNotify->dwWEFlags & WEF_ASYNC) {
  364. pNotify->dwWEFlags &= ~WEF_DEFERNOTIFY;
  365. }
  366. if (pNotify->dwWEFlags & WEF_DEFERNOTIFY) {
  367. #if DBG
  368. gnDeferredWinEvents++;
  369. #endif
  370. DBGVERIFYNOTIFY(pNotify);
  371. } else {
  372. pehNext = xxxProcessNotifyWinEvent(pNotify);
  373. }
  374. }
  375. }
  376. ThreadUnlockPti(ptiCurrent, &tlpti);
  377. ThreadUnlock(&tlpwnd);
  378. }
  379. /****************************************************************************\
  380. *
  381. * CreateNotify()
  382. *
  383. * Gets a pointer to a NOTIFY struct that we can then propagate to our
  384. * event window via Send/PostMessage. We have to do this since we want to
  385. * (pass on a lot more data then can be packed in the parameters.
  386. *
  387. * We have one cached struct so we avoid lots of allocs and frees in the
  388. * most common case of just one outstanding notification.
  389. \****************************************************************************/
  390. PNOTIFY
  391. CreateNotify(PEVENTHOOK pEvent, DWORD event, PWND pwnd, LONG idObject,
  392. LONG idChild, PTHREADINFO ptiSender, DWORD dwTime)
  393. {
  394. PNOTIFY pNotify;
  395. UserAssert(pEvent != NULL);
  396. //
  397. // Get a pointer. From cache if available.
  398. // IanJa - change this to allocate from zone a la AllocQEntry??
  399. //
  400. if (!fNotifyCacheInUse) {
  401. fNotifyCacheInUse = TRUE;
  402. pNotify = &notifyCache;
  403. #if DBG
  404. //
  405. // Make sure we aren't forgetting to set any fields.
  406. //
  407. // DebugFillBuffer(pNotify, sizeof(NOTIFY));
  408. #endif
  409. } else {
  410. pNotify = (PNOTIFY)UserAllocPool(sizeof(NOTIFY), TAG_NOTIFY);
  411. if (!pNotify)
  412. return NULL;
  413. }
  414. /*
  415. * Fill in the notify block.
  416. */
  417. pNotify->spEventHook = NULL;
  418. Lock(&pNotify->spEventHook, pEvent);
  419. pNotify->hwnd = HW(pwnd);
  420. pNotify->event = event;
  421. pNotify->idObject = idObject;
  422. pNotify->idChild = idChild;
  423. pNotify->idSenderThread = TIDq(ptiSender);
  424. UserAssert(pNotify->idSenderThread != 0);
  425. pNotify->dwEventTime = dwTime;
  426. pNotify->dwWEFlags = pEvent->fSync ? 0 : WEF_ASYNC;
  427. pNotify->pNotifyNext = NULL;
  428. pNotify->ptiReceiver = NULL;
  429. #if DBG
  430. gnNotifies++;
  431. #endif
  432. /*
  433. * The order of non-deferred notifications doesn't matter; they are here
  434. * simply for cleanup/in-use tracking. However, deferred notifications must
  435. * be ordered with most recent at the end, so just order them all that way.
  436. */
  437. if (gpPendingNotifies) {
  438. UserAssert(gpLastPendingNotify);
  439. UserAssert(gpLastPendingNotify->pNotifyNext == NULL);
  440. gpLastPendingNotify->pNotifyNext = pNotify;
  441. } else {
  442. gpPendingNotifies = pNotify;
  443. }
  444. gpLastPendingNotify = pNotify;
  445. return pNotify;
  446. }
  447. /****************************************************************************\
  448. * RemoveNotify
  449. \****************************************************************************/
  450. VOID RemoveNotify(
  451. PNOTIFY *ppNotify)
  452. {
  453. PNOTIFY pNotifyRemove;
  454. pNotifyRemove = *ppNotify;
  455. /*
  456. * First, get it out of the pending list.
  457. */
  458. *ppNotify = pNotifyRemove->pNotifyNext;
  459. #if DBG
  460. if (pNotifyRemove->dwWEFlags & WEF_DEFERNOTIFY) {
  461. UserAssert(gnDeferredWinEvents > 0);
  462. gnDeferredWinEvents--;
  463. }
  464. #endif
  465. if (*ppNotify == NULL) {
  466. /*
  467. * Removing last notify, so fix up gpLastPendingNotify:
  468. * If list now empty, there is no last item.
  469. */
  470. if (gpPendingNotifies == NULL) {
  471. gpLastPendingNotify = NULL;
  472. } else {
  473. gpLastPendingNotify = CONTAINING_RECORD(ppNotify, NOTIFY, pNotifyNext);
  474. }
  475. }
  476. UserAssert(gpPendingNotifies == 0 || gpPendingNotifies > (PNOTIFY)100);
  477. DBGVERIFYEVENTHOOK(pNotifyRemove->spEventHook);
  478. /*
  479. * This may cause the win event hook to be freed.
  480. */
  481. Unlock(&pNotifyRemove->spEventHook);
  482. /*
  483. * Now free it. Either put it back in the cache (if it is the cache)
  484. * or really free it.
  485. */
  486. if (pNotifyRemove == &notifyCache) {
  487. UserAssert(fNotifyCacheInUse);
  488. fNotifyCacheInUse = FALSE;
  489. } else {
  490. UserFreePool(pNotifyRemove);
  491. }
  492. #if DBG
  493. UserAssert(gnNotifies > 0);
  494. gnNotifies--;
  495. #endif
  496. }
  497. /*****************************************************************************\
  498. * DestroyNotify
  499. *
  500. * This gets the notification out of our pending list and frees the local
  501. * memory it uses.
  502. *
  503. * This function is called
  504. * (1) NORMALLY: After returning from calling the notify proc
  505. * (2) CLEANUP: When a thread goes away, we cleanup async notifies it
  506. * hasn't received, and sync notifies it was in the middle of trying
  507. * to call (i.e. the event proc faulted).
  508. \*****************************************************************************/
  509. VOID DestroyNotify(
  510. PNOTIFY pNotifyDestroy)
  511. {
  512. PNOTIFY *ppNotify;
  513. PNOTIFY pNotifyT;
  514. DBGVERIFYNOTIFY(pNotifyDestroy);
  515. /*
  516. * Either this notify isn't currently in the process of calling back
  517. * (which means ptiReceiver is NULL) or the thread destroying it
  518. * must be the one that was calling back (which means this thread
  519. * was destroyed during the callback and is cleaning up).
  520. */
  521. UserAssert(pNotifyDestroy->ptiReceiver == NULL ||
  522. pNotifyDestroy->ptiReceiver == PtiCurrent());
  523. ppNotify = &gpPendingNotifies;
  524. while (pNotifyT = *ppNotify) {
  525. if (pNotifyT == pNotifyDestroy) {
  526. RemoveNotify(ppNotify);
  527. return;
  528. } else {
  529. ppNotify = &pNotifyT->pNotifyNext;
  530. }
  531. }
  532. RIPMSG1(RIP_ERROR, "DestroyNotify 0x%p - not found", pNotifyDestroy);
  533. }
  534. /***************************************************************************\
  535. * FreeThreadsWinEvents
  536. *
  537. * During 'exit-list' processing this function is called to free any WinEvent
  538. * notifications and WinEvent hooks created by the current thread.
  539. *
  540. * Notifications that remain may be:
  541. * o Posted notifications (async)
  542. * o Notifications in xxxClientCallWinEventProc (sync)
  543. * o Deferred notifications (should be sync only)
  544. * Destroy the sync notifications, because we cannot do callbacks
  545. * while in thread cleanup.
  546. * Leave the posted (async) notifications alone: they're on their way already.
  547. *
  548. * History:
  549. * 11-11-96 IanJa Created.
  550. \***************************************************************************/
  551. VOID FreeThreadsWinEvents(
  552. PTHREADINFO pti)
  553. {
  554. PEVENTHOOK peh, pehNext;
  555. PNOTIFY pn, pnNext;
  556. DWORD idCurrentThread = W32GetCurrentTID();
  557. /*
  558. * Loop through all the notifications.
  559. */
  560. for (pn = gpPendingNotifies; pn; pn = pnNext) {
  561. pnNext = pn->pNotifyNext;
  562. /*
  563. * Only destroy sync notifications that belong to this thread
  564. * and are not currently calling back i.e. ptiReceiver must be NULL.
  565. * Otherwise, when we come back from the callback in
  566. * xxxProcessNotifyWinEvent we will operate on a freed notify.
  567. * Also destroy the notification if the receiver is going away
  568. * or else it gets leaked as long as the sender is alive.
  569. */
  570. if ((pn->idSenderThread == idCurrentThread &&
  571. pn->ptiReceiver == NULL) ||
  572. pn->ptiReceiver == pti) {
  573. if ((pn->dwWEFlags & WEF_ASYNC) == 0) {
  574. UserAssert((pn->dwWEFlags & WEF_POSTED) == 0);
  575. DestroyNotify(pn);
  576. }
  577. }
  578. }
  579. peh = gpWinEventHooks;
  580. while (peh) {
  581. pehNext = peh->pehNext;
  582. if (GETPTI(peh) == pti) {
  583. DestroyEventHook(peh);
  584. }
  585. peh = pehNext;
  586. }
  587. }
  588. /***************************************************************************\
  589. * _SetWinEventHook()
  590. *
  591. * This installs a win event hook.
  592. *
  593. * If hEventProcess set but idEventThread = 0, hook all threads in process.
  594. * If idEventThread set but hEventProcess = NULL, hook single thread only.
  595. * If neither are set, hook everything.
  596. * If both are set ??
  597. *
  598. \***************************************************************************/
  599. PEVENTHOOK _SetWinEventHook(
  600. DWORD eventMin,
  601. DWORD eventMax,
  602. HMODULE hmodWinEventProc,
  603. PUNICODE_STRING pstrLib,
  604. WINEVENTPROC pfnWinEventProc,
  605. HANDLE hEventProcess,
  606. DWORD idEventThread,
  607. DWORD dwFlags)
  608. {
  609. PEVENTHOOK pEventNew;
  610. PTHREADINFO ptiCurrent;
  611. int ihmod;
  612. ptiCurrent = PtiCurrent();
  613. //
  614. // If exiting, fail the call.
  615. //
  616. if (ptiCurrent->TIF_flags & TIF_INCLEANUP) {
  617. RIPMSG1(RIP_ERROR,
  618. "SetWinEventHook: Fail call - thread 0x%p in cleanup",
  619. ptiCurrent);
  620. return NULL;
  621. }
  622. /*
  623. * Check to see if filter proc is valid.
  624. */
  625. if (pfnWinEventProc == NULL) {
  626. RIPERR0(ERROR_INVALID_FILTER_PROC,
  627. RIP_WARNING,
  628. "pfnWinEventProc == NULL");
  629. return NULL;
  630. }
  631. if (eventMin > eventMax) {
  632. RIPERR0(ERROR_INVALID_HOOK_FILTER,
  633. RIP_WARNING,
  634. "eventMin > eventMax");
  635. return NULL;
  636. }
  637. if (dwFlags & WINEVENT_INCONTEXT) {
  638. /*
  639. * WinEventProc to be called in context of hooked thread, so needs a DLL.
  640. */
  641. if (hmodWinEventProc == NULL) {
  642. RIPERR0(ERROR_HOOK_NEEDS_HMOD,
  643. RIP_WARNING,
  644. "In context hook w/o DLL!");
  645. return NULL;
  646. } else if (pstrLib == NULL) {
  647. /*
  648. * If we got an hmod, we should get a DLL name too!
  649. */
  650. RIPERR1(ERROR_DLL_NOT_FOUND,
  651. RIP_WARNING,
  652. "hmod 0x%p, but no lib name",
  653. hmodWinEventProc);
  654. return NULL;
  655. }
  656. ihmod = GetHmodTableIndex(pstrLib);
  657. if (ihmod == -1) {
  658. RIPERR0(ERROR_MOD_NOT_FOUND,
  659. RIP_WARNING,
  660. "");
  661. return NULL;
  662. }
  663. } else {
  664. ihmod = -1; // means no DLL is required
  665. hmodWinEventProc = 0;
  666. }
  667. /*
  668. * Check the thread id, check it is a GUI thread.
  669. */
  670. if (idEventThread != 0) {
  671. PTHREADINFO ptiT;
  672. ptiT = PtiFromThreadId(idEventThread);
  673. if (ptiT == NULL || !(ptiT->TIF_flags & TIF_GUITHREADINITIALIZED)) {
  674. RIPERR1(ERROR_INVALID_THREAD_ID, RIP_VERBOSE, "pti %#p", ptiT);
  675. return NULL;
  676. }
  677. }
  678. /*
  679. * Create the window for async events first.
  680. *
  681. * NOTE that USER itself will not pass on window creation/destruction
  682. * notifications for
  683. * * IME windows
  684. * * OLE windows
  685. * * RPC windows
  686. * * Event windows
  687. */
  688. /*
  689. * Get a new event.
  690. */
  691. pEventNew = (PEVENTHOOK)HMAllocObject(ptiCurrent,
  692. NULL,
  693. TYPE_WINEVENTHOOK,
  694. sizeof(EVENTHOOK));
  695. if (!pEventNew) {
  696. return NULL;
  697. }
  698. /*
  699. * Fill in the new event.
  700. */
  701. pEventNew->eventMin = (UINT)eventMin;
  702. pEventNew->eventMax = (UINT)eventMax;
  703. pEventNew->fIgnoreOwnThread = ((dwFlags & WINEVENT_SKIPOWNTHREAD) != 0);
  704. pEventNew->fIgnoreOwnProcess = ((dwFlags & WINEVENT_SKIPOWNPROCESS) != 0);
  705. pEventNew->fDestroyed = FALSE;
  706. pEventNew->fSync = ((dwFlags & WINEVENT_INCONTEXT) != 0);
  707. pEventNew->hEventProcess = hEventProcess;
  708. pEventNew->idEventThread = idEventThread;
  709. pEventNew->ihmod = ihmod;
  710. /*
  711. * Add a dependency on this module - meaning, increment a count
  712. * that simply counts the number of hooks set into this module.
  713. */
  714. if (pEventNew->ihmod >= 0) {
  715. AddHmodDependency(pEventNew->ihmod);
  716. }
  717. /*
  718. * If pfnWinEventProc is in caller's process and no DLL is involved,
  719. * then pEventNew->offPfn is the actual address.
  720. */
  721. pEventNew->offPfn = ((ULONG_PTR)pfnWinEventProc) - ((ULONG_PTR)hmodWinEventProc);
  722. /*
  723. * Link our event into the master list.
  724. *
  725. * Note that we count on USER to not generate any events when installing
  726. * our hook. The caller can't handle it yet since he hasn't got back
  727. * his event handle from this call.
  728. */
  729. pEventNew->pehNext = gpWinEventHooks;
  730. gpWinEventHooks = pEventNew;
  731. /*
  732. * Update the flags that indicate what event hooks are installed. These
  733. * flags are accessable from user mode without a kernel transition since
  734. * they are in shared memory.
  735. */
  736. SET_FLAG(gpsi->dwInstalledEventHooks, CategoryMaskFromEventRange(eventMin, eventMax));
  737. return pEventNew;
  738. }
  739. /****************************************************************************\
  740. * UnhookWinEvent
  741. *
  742. * Unhooks a win event hook. We of course sanity check that this thread is
  743. * the one which installed the hook. We have to: We are going to destroy
  744. * the IPC window and that must be in context.
  745. \****************************************************************************/
  746. BOOL _UnhookWinEvent(
  747. PEVENTHOOK pEventUnhook)
  748. {
  749. DBGVERIFYEVENTHOOK(pEventUnhook);
  750. if (HMIsMarkDestroy(pEventUnhook) || (GETPTI(pEventUnhook) != PtiCurrent())) {
  751. /*
  752. * We do this to avoid someone calling UnhookWinEvent() the first
  753. * time, then somehow getting control again and calling it a second
  754. * time before we've managed to free up the event since someone was
  755. * in the middle of using it at the first UWE call.
  756. */
  757. RIPERR1(ERROR_INVALID_HANDLE,
  758. RIP_WARNING,
  759. "_UnhookWinEvent: Invalid event hook 0x%p",
  760. PtoHq(pEventUnhook));
  761. return FALSE;
  762. }
  763. /*
  764. * Purge this baby if all notifications are done.
  765. *
  766. * If there are SYNC ones pending, the caller will clean this up upon
  767. * the return from calling the event.
  768. *
  769. * If there are ASYNC ones pending, the receiver will not call the event
  770. * and clean it up when he gets it.
  771. */
  772. DestroyEventHook(pEventUnhook);
  773. return TRUE;
  774. }
  775. /*****************************************************************************\
  776. * DestroyEventHook
  777. *
  778. * Destroys an event when the ref count has gone down to zero. It may
  779. * happen:
  780. * * In the event generator's context, after returning from a callback
  781. * and the ref count dropped to zero, if sync.
  782. * * In the event installer's context, after returning from a callback
  783. * and the ref count dropped to zero if async.
  784. * * In the event installer's context, if on _UnhookWinEvent() the event
  785. * was not in use at all.
  786. \*****************************************************************************/
  787. VOID DestroyEventHook(
  788. PEVENTHOOK pEventDestroy)
  789. {
  790. PEVENTHOOK *ppEvent;
  791. PEVENTHOOK pEventT;
  792. DWORD dwCategoryMask = 0;
  793. DBGVERIFYEVENTHOOK(pEventDestroy);
  794. UserAssert(gpWinEventHooks);
  795. /*
  796. * Mark this event as destroyed, but don't remove it from the event list
  797. * until its lock count goes to 0 - we may be traversing the list within
  798. * xxxWindowEvent, so we mustn't break the link to the next hook.
  799. */
  800. pEventDestroy->fDestroyed = TRUE;
  801. /*
  802. * If the object is locked, mark it for destroy but don't free it yet.
  803. */
  804. if (!HMMarkObjectDestroy(pEventDestroy)) {
  805. return;
  806. }
  807. /*
  808. * Remove this from our event list.
  809. */
  810. for (ppEvent = &gpWinEventHooks; pEventT = *ppEvent; ppEvent = &pEventT->pehNext) {
  811. if (pEventT == pEventDestroy) {
  812. *ppEvent = pEventDestroy->pehNext;
  813. break;
  814. }
  815. }
  816. UserAssert(pEventT);
  817. /*
  818. * Update the flags that indicate what event hooks are installed. These
  819. * flags are accessable from user mode without a kernel transition since
  820. * they are in shared memory. Note that a user could check the shared
  821. * memory at any time, so they may get a false-positive during this
  822. * processing. A false-positive would mean that we claim there is a
  823. * listener, when there really isn't. We never want the user to get
  824. * a false negative - meaning that we claim there aren't any listeners
  825. * but there really is. That could mean bad things for accessability.
  826. */
  827. for (pEventT = gpWinEventHooks; pEventT != NULL; pEventT = pEventT->pehNext) {
  828. SET_FLAG(dwCategoryMask, CategoryMaskFromEventRange(pEventT->eventMin, pEventT->eventMax));
  829. }
  830. gpsi->dwInstalledEventHooks = dwCategoryMask;
  831. /*
  832. * Make sure each hooked thread will unload the hook proc DLL.
  833. */
  834. if (pEventDestroy->ihmod >= 0) {
  835. RemoveHmodDependency(pEventDestroy->ihmod);
  836. }
  837. /*
  838. * Free this pointer.
  839. */
  840. HMFreeObject(pEventDestroy);
  841. }
  842. /***************************************************************************\
  843. * xxxGetEventProc
  844. *
  845. * For sync events, this gets the address to call. If 16-bits, then just
  846. * return the installed address. If 32-bits, we need to load the library
  847. * if not in the same process as the installer.
  848. \***************************************************************************/
  849. WINEVENTPROC xxxGetEventProc(
  850. PEVENTHOOK pEventOrg)
  851. {
  852. PTHREADINFO ptiCurrent;
  853. UserAssert(pEventOrg);
  854. UserAssert(pEventOrg->fSync);
  855. UserAssert(pEventOrg->ihmod >= 0);
  856. UserAssert(pEventOrg->offPfn != 0);
  857. CheckLock(pEventOrg);
  858. /*
  859. * Make sure the hook is still around before we try and call it.
  860. */
  861. if (HMIsMarkDestroy(pEventOrg)) {
  862. return NULL;
  863. }
  864. ptiCurrent = PtiCurrent();
  865. /*
  866. * Make sure the DLL for this hook, if any, has been loaded for the
  867. * current process.
  868. */
  869. if (pEventOrg->ihmod != -1 &&
  870. TESTHMODLOADED(ptiCurrent, pEventOrg->ihmod) == 0) {
  871. /*
  872. * Try loading the library, since it isn't loaded in this processes
  873. * context. The hook is alrerady locked, so it won't go away while
  874. * we're loading this library.
  875. */
  876. if (xxxLoadHmodIndex(pEventOrg->ihmod) == NULL) {
  877. return NULL;
  878. }
  879. }
  880. /*
  881. * While we're still inside the critical section make sure the hook
  882. * hasn't been 'freed'. If so just return NULL. Since WinEvent has
  883. * already been called, you might think that we should pass the event
  884. * on, but the hooker may not be expecting this after having
  885. * cancelled the hook! In any case, we have two ways of detecting that
  886. * this hook has been removed (see the following code).
  887. */
  888. /*
  889. * Make sure the hook is still around before we try and call it.
  890. */
  891. if (HMIsMarkDestroy(pEventOrg)) {
  892. return NULL;
  893. }
  894. return (WINEVENTPROC)PFNHOOK(pEventOrg);
  895. }