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.

2619 lines
85 KiB

  1. /****************************** Module Header ******************************\
  2. * Module Name: hooks.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * This module contains the user hook APIs and support routines.
  7. *
  8. * History:
  9. * 01-28-91 DavidPe Created.
  10. * 08 Feb 1992 IanJa Unicode/ANSI aware & neutral
  11. \***************************************************************************/
  12. #include "precomp.h"
  13. #pragma hdrstop
  14. /*
  15. * This table is used to determine whether a particular hook
  16. * can be set for the system or a task, and other hook-ID specific things.
  17. */
  18. #define HKF_SYSTEM 0x01
  19. #define HKF_TASK 0x02
  20. #define HKF_JOURNAL 0x04 // JOURNAL the mouse on set
  21. #define HKF_NZRET 0x08 // Always return NZ hook for <=3.0 compatibility
  22. #define HKF_INTERSENDABLE 0x10 // OK to call hookproc in context of hooking thread
  23. #define HKF_LOWLEVEL 0x20 // Low level hook
  24. CONST int ampiHookError[CWINHOOKS] = {
  25. 0, // WH_MSGFILTER (-1)
  26. 0, // WH_JOURNALRECORD 0
  27. -1, // WH_JOURNALPLAYBACK 1
  28. 0, // WH_KEYBOARD 2
  29. 0, // WH_GETMESSAGE 3
  30. 0, // WH_CALLWNDPROC 4
  31. 0, // WH_CBT 5
  32. 0, // WH_SYSMSGFILTER 6
  33. 0, // WH_MOUSE 7
  34. 0, // WH_HARDWARE 8
  35. 0, // WH_DEBUG 9
  36. 0, // WH_SHELL 10
  37. 0, // WH_FOREGROUNDIDLE 11
  38. 0, // WH_CALLWNDPROCRET 12
  39. 0, // WH_KEYBOARD_LL 13
  40. 0 // WH_MOUSE_LL 14
  41. #ifdef REDIRECTION
  42. ,0 // WH_HITTEST
  43. #endif // REDIRECTION
  44. };
  45. CONST BYTE abHookFlags[CWINHOOKS] = {
  46. HKF_SYSTEM | HKF_TASK | HKF_NZRET , // WH_MSGFILTER (-1)
  47. HKF_SYSTEM | HKF_JOURNAL | HKF_INTERSENDABLE , // WH_JOURNALRECORD 0
  48. HKF_SYSTEM | HKF_JOURNAL | HKF_INTERSENDABLE , // WH_JOURNALPLAYBACK 1
  49. HKF_SYSTEM | HKF_TASK | HKF_NZRET | HKF_INTERSENDABLE , // WH_KEYBOARD 2
  50. HKF_SYSTEM | HKF_TASK , // WH_GETMESSAGE 3
  51. HKF_SYSTEM | HKF_TASK , // WH_CALLWNDPROC 4
  52. HKF_SYSTEM | HKF_TASK , // WH_CBT 5
  53. HKF_SYSTEM , // WH_SYSMSGFILTER 6
  54. HKF_SYSTEM | HKF_TASK | HKF_INTERSENDABLE , // WH_MOUSE 7
  55. HKF_SYSTEM | HKF_TASK , // WH_HARDWARE 8
  56. HKF_SYSTEM | HKF_TASK , // WH_DEBUG 9
  57. HKF_SYSTEM | HKF_TASK , // WH_SHELL 10
  58. HKF_SYSTEM | HKF_TASK , // WH_FOREGROUNDIDLE 11
  59. HKF_SYSTEM | HKF_TASK , // WH_CALLWNDPROCRET 12
  60. HKF_SYSTEM | HKF_LOWLEVEL | HKF_INTERSENDABLE , // WH_KEYBOARD_LL 13
  61. HKF_SYSTEM | HKF_LOWLEVEL | HKF_INTERSENDABLE // WH_MOUSE_LL 14
  62. #ifdef REDIRECTION
  63. ,HKF_SYSTEM | HKF_LOWLEVEL | HKF_INTERSENDABLE // WH_HITTEST 15
  64. #endif // REDIRECTION
  65. };
  66. /*
  67. * HACK (hiroyama) see xxxCallJournalPlaybackHook()
  68. * Optimization: faster determination whether the message is one of
  69. * WM_[SYS][DEAD]CHAR.
  70. * Argument (msg) requires to be one of keyboard messages. Range check
  71. * should be done before calling IS_CHAR_MSG() macro.
  72. *
  73. * (i.e. WM_KEYFIRST <= msg < WM_KEYLAST)
  74. *
  75. * We expect bit 0x02 of all WM_*CHAR messages to be set.
  76. * and bit 0x02 of all WM_*KEY* messages to be clear
  77. *
  78. * WM_KEYDOWN 0x100 000
  79. * WM_KEYUP 0x101 001
  80. * WM_CHAR 0x102 010
  81. * WM_DEADCHAR 0x103 011
  82. *
  83. * WM_SYSKEYDOWN 0x104 100
  84. * WM_SYSKEYUP 0x105 101
  85. * WM_SYSCHAR 0x106 110
  86. * WM_SYSDEADCHAR 0x107 111
  87. *
  88. */
  89. /*
  90. */
  91. #if (WM_KEYFIRST != 0x100) || \
  92. (WM_KEYLAST != 0x109) || \
  93. (WM_KEYLAST != WM_UNICHAR) || \
  94. (WM_KEYDOWN & 0x2) || \
  95. (WM_KEYUP & 0x2) || \
  96. (WM_SYSKEYDOWN & 0x2) || \
  97. (WM_SYSKEYUP & 0x2) || \
  98. !(WM_CHAR & 0x02) || \
  99. !(WM_DEADCHAR & 0x02) || \
  100. !(WM_SYSCHAR & 0x02) || \
  101. !(WM_SYSDEADCHAR & 0x02)
  102. #error "unexpected value in keyboard messages."
  103. #endif
  104. #if DBG
  105. BOOL IsCharMsg(UINT msg)
  106. {
  107. UserAssert(msg >= WM_KEYFIRST && msg < WM_KEYLAST);
  108. return msg & 0x02;
  109. }
  110. #define IS_CHAR_MSG(msg) IsCharMsg(msg)
  111. #else
  112. #define IS_CHAR_MSG(msg) ((msg) & 0x02)
  113. #endif
  114. void UnlinkHook(PHOOK phkFree);
  115. /***************************************************************************\
  116. * DbgValidateThisHook
  117. *
  118. * Validates a hook structure and returns the start of its chain.
  119. *
  120. * History:
  121. * 03-25-97 GerardoB Created
  122. \***************************************************************************/
  123. #if DBG
  124. PHOOK * DbgValidateThisHook (PHOOK phk, int iType, PTHREADINFO ptiHooked)
  125. {
  126. CheckCritIn();
  127. /*
  128. * No bogus flags
  129. */
  130. UserAssert(!(phk->flags & ~HF_DBGUSED));
  131. /*
  132. * Type
  133. */
  134. UserAssert(phk->iHook == iType);
  135. /*
  136. * HF_GLOBAL & ptiHooked. return the start of its hook chain.
  137. */
  138. if (phk->flags & HF_GLOBAL) {
  139. UserAssert(phk->ptiHooked == NULL);
  140. if (phk->rpdesk != NULL) {
  141. UserAssert(GETPTI(phk) == gptiRit);
  142. return &phk->rpdesk->pDeskInfo->aphkStart[iType + 1];
  143. } else {
  144. return &GETPTI(phk)->pDeskInfo->aphkStart[iType + 1];
  145. }
  146. } else {
  147. UserAssert((phk->ptiHooked == ptiHooked)
  148. || (abHookFlags[iType + 1] & HKF_INTERSENDABLE));
  149. return &(phk->ptiHooked->aphkStart[iType + 1]);
  150. }
  151. }
  152. /***************************************************************************\
  153. * DbgValidatefsHook
  154. *
  155. * Make sure that the fsHook bit masks are in sync. If the bits
  156. * are out of sync, some hook must have the HF_INCHECKWHF flag
  157. * (this means the bits are being adjusted right now)
  158. *
  159. * History:
  160. * 05-20-97 GerardoB Extracted from PhkFirst*Valid
  161. \***************************************************************************/
  162. void DbgValidatefsHook(PHOOK phk, int nFilterType, PTHREADINFO pti, BOOL fGlobal)
  163. {
  164. CheckCritIn();
  165. /*
  166. * If no pti is provided, figure out what it should be.
  167. * phk is expected to be NULL.
  168. */
  169. if (pti == NULL) {
  170. fGlobal = (phk->flags & HF_GLOBAL);
  171. if (fGlobal) {
  172. pti = GETPTI(phk);
  173. } else {
  174. pti = phk->ptiHooked;
  175. UserAssert(pti != NULL);
  176. }
  177. }
  178. if (fGlobal) {
  179. if ((phk != NULL) ^ IsGlobalHooked(pti, WHF_FROM_WH(nFilterType))) {
  180. PHOOK phkTemp = pti->pDeskInfo->aphkStart[nFilterType + 1];
  181. while ((phkTemp != NULL) && !(phkTemp->flags & HF_INCHECKWHF)) {
  182. phkTemp = phkTemp->phkNext;
  183. }
  184. UserAssert(phkTemp != NULL);
  185. }
  186. } else {
  187. if ((phk != NULL) ^ IsHooked(pti, WHF_FROM_WH(nFilterType))) {
  188. PHOOK phkTemp = pti->aphkStart[nFilterType + 1];
  189. while ((phkTemp != NULL) && !(phkTemp->flags & HF_INCHECKWHF)) {
  190. phkTemp = phkTemp->phkNext;
  191. }
  192. if (phkTemp == NULL) {
  193. phkTemp = pti->pDeskInfo->aphkStart[nFilterType + 1];
  194. while ((phkTemp != NULL) && !(phkTemp->flags & HF_INCHECKWHF)) {
  195. phkTemp = phkTemp->phkNext;
  196. }
  197. }
  198. UserAssert(phkTemp != NULL);
  199. }
  200. }
  201. }
  202. /***************************************************************************\
  203. * DbgValidateHooks
  204. *
  205. * This functions expects valid (not destroyed) and properly linked.
  206. * History:
  207. * 03-25-97 GerardoB Created
  208. \***************************************************************************/
  209. void DbgValidateHooks (PHOOK phk, int iType)
  210. {
  211. PHOOK *pphkStart, *pphkNext;
  212. if (phk == NULL) {
  213. return;
  214. }
  215. /*
  216. * It shouldn't be destroyed
  217. */
  218. UserAssert(!(phk->flags & (HF_DESTROYED | HF_FREED)));
  219. /*
  220. * Validate fsHooks
  221. */
  222. DbgValidatefsHook(phk, iType, NULL, FALSE);
  223. /*
  224. * Validate this hook and get the beginning of the hook chain
  225. */
  226. pphkStart = DbgValidateThisHook(phk, iType, phk->ptiHooked);
  227. /*
  228. * There must be at least one hook in the chain
  229. */
  230. UserAssert(*pphkStart != NULL);
  231. /*
  232. * Validate the link.
  233. * And while your're at it, validate all hooks!
  234. */
  235. pphkNext = pphkStart;
  236. while ((*pphkNext != phk) && (*pphkNext != NULL)) {
  237. UserAssert(pphkStart == DbgValidateThisHook(*pphkNext, iType, phk->ptiHooked));
  238. pphkNext = &(*pphkNext)->phkNext;
  239. }
  240. /*
  241. * Verify that we found it.
  242. */
  243. UserAssert(*pphkNext == phk);
  244. /*
  245. * Walk until the end of the chain
  246. */
  247. while (*pphkNext != NULL) {
  248. UserAssert(pphkStart == DbgValidateThisHook(*pphkNext, iType, phk->ptiHooked));
  249. pphkNext = &(*pphkNext)->phkNext;
  250. }
  251. }
  252. #else
  253. #define DbgValidatefsHook(phk, nFilterType, pti, fGlobal)
  254. #endif /* DBG */
  255. /***************************************************************************\
  256. * zzzJournalAttach
  257. *
  258. * This attaches/detaches threads to one input queue so input is synchronized.
  259. * Journalling requires this.
  260. *
  261. * 12-10-92 ScottLu Created.
  262. \***************************************************************************/
  263. BOOL zzzJournalAttach(
  264. PTHREADINFO pti,
  265. BOOL fAttach)
  266. {
  267. PTHREADINFO ptiT;
  268. PQ pq;
  269. PLIST_ENTRY pHead, pEntry;
  270. /*
  271. * If we're attaching, calculate the pqAttach for all threads journalling.
  272. * If we're unattaching, just call zzzReattachThreads() and it will calculate
  273. * the non-journalling queues to attach to.
  274. */
  275. if (fAttach) {
  276. if ((pq = AllocQueue(pti, NULL)) == NULL)
  277. return FALSE;
  278. pHead = &pti->rpdesk->PtiList;
  279. for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) {
  280. ptiT = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink);
  281. /*
  282. * This is the Q to attach to for all threads that will do
  283. * journalling.
  284. */
  285. if (!(ptiT->TIF_flags & (TIF_DONTJOURNALATTACH | TIF_INCLEANUP))) {
  286. ptiT->pqAttach = pq;
  287. ptiT->pqAttach->cThreads++;
  288. }
  289. }
  290. }
  291. return zzzReattachThreads(fAttach);
  292. }
  293. /***************************************************************************\
  294. * InterQueueMsgCleanup
  295. *
  296. * Walk gpsmsList looking for inter queue messages with a hung receiver;
  297. * if one is found and it's a message that would have been an async event or
  298. * intra queue if not journalling, then it cleans it up.
  299. *
  300. * While Journalling most threads are attached to the same queue. This causes
  301. * activation and input stuff to be synchronous; if a thread hangs or dies,
  302. * any other thread sending a message to the hung/dead thread will be
  303. * blocked for good.
  304. * This is critical when the blocked thread is cssr; this can happen with
  305. * console windows or when some one requests a hard error box, specially
  306. * during window activation.
  307. *
  308. * This function must be called when all queues have been detached (unless previously attached),
  309. * so we can take care of hung/dead receivers with pending SMSs.
  310. *
  311. * 03-28-96 GerardoB Created
  312. \***************************************************************************/
  313. void InterQueueMsgCleanup (DWORD dwTimeFromLastRead)
  314. {
  315. PSMS *ppsms;
  316. PSMS psmsNext;
  317. CheckCritIn();
  318. /*
  319. * Walk gpsmsList
  320. */
  321. for (ppsms = &gpsmsList; *ppsms; ) {
  322. psmsNext = (*ppsms)->psmsNext;
  323. /*
  324. * If this is an inter queue message
  325. */
  326. if (((*ppsms)->ptiSender != NULL)
  327. && ((*ppsms)->ptiReceiver != NULL)
  328. && ((*ppsms)->ptiSender->pq != (*ppsms)->ptiReceiver->pq)) {
  329. /*
  330. * If the receiver has been hung for a while
  331. */
  332. if (FHungApp ((*ppsms)->ptiReceiver, dwTimeFromLastRead)) {
  333. switch ((*ppsms)->message) {
  334. /*
  335. * Activation messages
  336. */
  337. case WM_NCACTIVATE:
  338. case WM_ACTIVATEAPP:
  339. case WM_ACTIVATE:
  340. case WM_SETFOCUS:
  341. case WM_KILLFOCUS:
  342. case WM_QUERYNEWPALETTE:
  343. /*
  344. * Sent to spwndFocus, which now can be in a different queue
  345. */
  346. case WM_INPUTLANGCHANGE:
  347. RIPMSG3 (RIP_WARNING, "InterQueueMsgCleanup: ptiSender:%#p ptiReceiver:%#p message:%#lx",
  348. (*ppsms)->ptiSender, (*ppsms)->ptiReceiver, (*ppsms)->message);
  349. ReceiverDied(*ppsms, ppsms);
  350. break;
  351. } /* switch */
  352. } /* If hung receiver */
  353. } /* If inter queue message */
  354. /*
  355. * If the message was not unlinked, go to the next one.
  356. */
  357. if (*ppsms != psmsNext)
  358. ppsms = &(*ppsms)->psmsNext;
  359. } /* for */
  360. }
  361. /***************************************************************************\
  362. * zzzCancelJournalling
  363. *
  364. * Journalling is cancelled with control-escape is pressed, or when the desktop
  365. * is switched.
  366. *
  367. * 01-27-93 ScottLu Created.
  368. \***************************************************************************/
  369. void zzzCancelJournalling(void)
  370. {
  371. PTHREADINFO ptiCancelJournal;
  372. PHOOK phook;
  373. PHOOK phookNext;
  374. /*
  375. * Mouse buttons sometimes get stuck down due to hardware glitches,
  376. * usually due to input concentrator switchboxes or faulty serial
  377. * mouse COM ports, so clear the global button state here just in case,
  378. * otherwise we may not be able to change focus with the mouse.
  379. * Also do this in Alt-Tab processing.
  380. */
  381. #if DBG
  382. if (gwMouseOwnerButton)
  383. RIPMSG1(RIP_WARNING,
  384. "gwMouseOwnerButton=%x, being cleared forcibly\n",
  385. gwMouseOwnerButton);
  386. #endif
  387. gwMouseOwnerButton = 0;
  388. /*
  389. * Remove journal hooks. This'll cause threads to associate with
  390. * different queues.
  391. * DeferWinEventNotify() so we can traverse the phook list safely
  392. */
  393. DeferWinEventNotify();
  394. UserAssert(gptiRit->pDeskInfo == grpdeskRitInput->pDeskInfo);
  395. phook = PhkFirstGlobalValid(gptiRit, WH_JOURNALPLAYBACK);
  396. while (phook != NULL) {
  397. ptiCancelJournal = phook->head.pti;
  398. if (ptiCancelJournal != NULL) {
  399. /*
  400. * Let the thread that set the journal hook know this is happening.
  401. */
  402. _PostThreadMessage(ptiCancelJournal, WM_CANCELJOURNAL, 0, 0);
  403. /*
  404. * If there was an app waiting for a response back from the journal
  405. * application, cancel that request so the app can continue running
  406. * (for example, we don't want winlogon or console to wait for an
  407. * app that may be hung!)
  408. */
  409. SendMsgCleanup(ptiCancelJournal);
  410. }
  411. phookNext = PhkNextValid(phook);
  412. zzzUnhookWindowsHookEx(phook); // May free phook memory
  413. phook = phookNext;
  414. }
  415. zzzEndDeferWinEventNotify();
  416. /*
  417. * DeferWinEventNotify() so we can traverse the phook list safely
  418. */
  419. DeferWinEventNotify();
  420. UserAssert(gptiRit->pDeskInfo == grpdeskRitInput->pDeskInfo);
  421. phook = PhkFirstGlobalValid(gptiRit, WH_JOURNALRECORD);
  422. while (phook != NULL) {
  423. ptiCancelJournal = phook->head.pti;
  424. if (ptiCancelJournal != NULL) {
  425. /*
  426. * Let the thread that set the journal hook know this is happening.
  427. */
  428. _PostThreadMessage(ptiCancelJournal, WM_CANCELJOURNAL, 0, 0);
  429. /*
  430. * If there was an app waiting for a response back from the journal
  431. * application, cancel that request so the app can continue running
  432. * (for example, we don't want winlogon or console to wait for an
  433. * app that may be hung!)
  434. */
  435. SendMsgCleanup(ptiCancelJournal);
  436. }
  437. phookNext = PhkNextValid(phook);
  438. zzzUnhookWindowsHookEx(phook); // May free phook memory
  439. phook = phookNext;
  440. }
  441. zzzEndDeferWinEventNotify();
  442. /*
  443. * Make sure journalling ssync mode didn't hose any one
  444. */
  445. InterQueueMsgCleanup(CMSWAITTOKILLTIMEOUT);
  446. /*
  447. * Unlock SetForegroundWindow (if locked)
  448. */
  449. gppiLockSFW = NULL;
  450. /*
  451. * NT5's last minute hack for evil applications, who disables the desktop window
  452. * (perhaps by accidents though) leaving the system pretty unusable.
  453. * See Raid #423704.
  454. */
  455. if (grpdeskRitInput && grpdeskRitInput->pDeskInfo) {
  456. PWND pwndDesktop = grpdeskRitInput->pDeskInfo->spwnd;
  457. if (pwndDesktop && TestWF(pwndDesktop, WFDISABLED)) {
  458. ClrWF(pwndDesktop, WFDISABLED);
  459. }
  460. }
  461. }
  462. /***************************************************************************\
  463. * zzzSetWindowsHookAW (API)
  464. *
  465. * This is the Win32 version of the SetWindowsHook() call. It has the
  466. * same characteristics as far as return values, but only sets 'local'
  467. * hooks. This is because we weren't provided a DLL we can load into
  468. * other processes. Because of this WH_SYSMSGFILTER is no longer a
  469. * valid hook. Apps will either need to call with WH_MSGFILTER or call
  470. * the new API SetWindowsHookEx(). Essentially this API is obsolete and
  471. * everyone should call SetWindowsHookEx().
  472. *
  473. * History:
  474. * 10-Feb-1991 DavidPe Created.
  475. * 30-Jan-1992 IanJa Added bAnsi parameter
  476. \***************************************************************************/
  477. PROC zzzSetWindowsHookAW(
  478. int nFilterType,
  479. PROC pfnFilterProc,
  480. DWORD dwFlags)
  481. {
  482. PHOOK phk;
  483. phk = zzzSetWindowsHookEx(NULL, NULL, PtiCurrent(),
  484. nFilterType, pfnFilterProc, dwFlags);
  485. /*
  486. * If we get an error from zzzSetWindowsHookEx() then we return
  487. * -1 to be compatible with older version of Windows.
  488. */
  489. if (phk == NULL) {
  490. return (PROC)-1;
  491. }
  492. /*
  493. * Handle the backwards compatibility return value cases for
  494. * SetWindowsHook. If this was the first hook in the chain,
  495. * then return NULL, else return something non-zero. HKF_NZRET
  496. * is a special case where SetWindowsHook would always return
  497. * something because there was a default hook installed. Some
  498. * apps relied on a non-zero return value in those cases.
  499. */
  500. if ((phk->phkNext != NULL) || (abHookFlags[nFilterType + 1] & HKF_NZRET)) {
  501. return (PROC)phk;
  502. }
  503. return NULL;
  504. }
  505. /***************************************************************************\
  506. * zzzSetWindowsHookEx
  507. *
  508. * SetWindowsHookEx() is the updated version of SetWindowsHook(). It allows
  509. * applications to set hooks on specific threads or throughout the entire
  510. * system. The function returns a hook handle to the application if
  511. * successful and NULL if a failure occured.
  512. *
  513. * History:
  514. * 28-Jan-1991 DavidPe Created.
  515. * 15-May-1991 ScottLu Changed to work client/server.
  516. * 30-Jan-1992 IanJa Added bAnsi parameter
  517. \***************************************************************************/
  518. PHOOK zzzSetWindowsHookEx(
  519. HANDLE hmod,
  520. PUNICODE_STRING pstrLib,
  521. PTHREADINFO ptiThread,
  522. int nFilterType,
  523. PROC pfnFilterProc,
  524. DWORD dwFlags)
  525. {
  526. ACCESS_MASK amDesired;
  527. PHOOK phkNew;
  528. TL tlphkNew;
  529. PHOOK *pphkStart;
  530. PTHREADINFO ptiCurrent;
  531. /*
  532. * Check to see if filter type is valid.
  533. */
  534. if (nFilterType < WH_MIN || nFilterType > WH_MAX) {
  535. RIPERR1(ERROR_INVALID_HOOK_FILTER,
  536. RIP_WARNING,
  537. "Invalid hook type 0x%x",
  538. nFilterType);
  539. return NULL;
  540. }
  541. /*
  542. * Check to see if filter proc is valid.
  543. */
  544. if (pfnFilterProc == NULL) {
  545. RIPERR0(ERROR_INVALID_FILTER_PROC,
  546. RIP_WARNING,
  547. "NULL hook callback");
  548. return NULL;
  549. }
  550. ptiCurrent = PtiCurrent();
  551. if (ptiThread == NULL) {
  552. /*
  553. * Is the app trying to set a global hook without a library?
  554. * If so return an error.
  555. */
  556. if (hmod == NULL) {
  557. RIPERR0(ERROR_HOOK_NEEDS_HMOD,
  558. RIP_WARNING,
  559. "Global hook requires a non-NULL hmod");
  560. return NULL;
  561. }
  562. } else {
  563. /*
  564. * Is the app trying to set a local hook that is global-only?
  565. * If so return an error.
  566. */
  567. if (!(abHookFlags[nFilterType + 1] & HKF_TASK)) {
  568. RIPERR1(ERROR_GLOBAL_ONLY_HOOK,
  569. RIP_WARNING,
  570. "Hook type 0x%x must be global",
  571. nFilterType);
  572. return NULL;
  573. }
  574. /*
  575. * Can't hook outside our own desktop.
  576. */
  577. if (ptiThread->rpdesk != ptiCurrent->rpdesk) {
  578. RIPERR0(ERROR_ACCESS_DENIED,
  579. RIP_WARNING,
  580. "Access denied to desktop in zzzSetWindowsHookEx - can't hook other desktops");
  581. return NULL;
  582. }
  583. if (ptiCurrent->ppi != ptiThread->ppi) {
  584. /*
  585. * Is the app trying to set hook in another process without a library?
  586. * If so return an error.
  587. */
  588. if (hmod == NULL) {
  589. RIPERR0(ERROR_HOOK_NEEDS_HMOD,
  590. RIP_WARNING,
  591. "Cross-thread hook needs a non-NULL hmod");
  592. return NULL;
  593. }
  594. /*
  595. * Is the app hooking another user without access?
  596. * If so return an error. Note that this check is done
  597. * for global hooks every time the hook is called.
  598. */
  599. if ((!RtlEqualLuid(&ptiThread->ppi->luidSession,
  600. &ptiCurrent->ppi->luidSession)) &&
  601. !(ptiThread->TIF_flags & TIF_ALLOWOTHERACCOUNTHOOK)) {
  602. RIPERR0(ERROR_ACCESS_DENIED,
  603. RIP_WARNING,
  604. "Access denied to other user in zzzSetWindowsHookEx");
  605. return NULL;
  606. }
  607. if ((ptiThread->TIF_flags & (TIF_CSRSSTHREAD | TIF_SYSTEMTHREAD)) &&
  608. !(abHookFlags[nFilterType + 1] & HKF_INTERSENDABLE)) {
  609. /*
  610. * Can't hook console or GUI system thread if inter-thread
  611. * calling isn't implemented for this hook type.
  612. */
  613. RIPERR1(ERROR_HOOK_TYPE_NOT_ALLOWED,
  614. RIP_WARNING,
  615. "nFilterType (%ld) not allowed in zzzSetWindowsHookEx",
  616. nFilterType);
  617. return NULL;
  618. }
  619. }
  620. }
  621. /*
  622. * Check if this thread has access to hook its desktop.
  623. */
  624. switch( nFilterType ) {
  625. case WH_JOURNALRECORD:
  626. amDesired = DESKTOP_JOURNALRECORD;
  627. break;
  628. case WH_JOURNALPLAYBACK:
  629. amDesired = DESKTOP_JOURNALPLAYBACK;
  630. break;
  631. default:
  632. amDesired = DESKTOP_HOOKCONTROL;
  633. break;
  634. }
  635. /*
  636. * The console input thread *must* be allowed to set this hook, even if
  637. * the account it's impersonating doesn't have hook access to the desktop
  638. * in question.
  639. */
  640. if (!RtlAreAllAccessesGranted(ptiCurrent->amdesk, amDesired) &&
  641. !(ISCSRSS() && nFilterType == WH_MSGFILTER)) {
  642. UserAssert(!ISCSRSS() ||
  643. (PsGetCurrentThreadId() ==
  644. UlongToHandle(ptiCurrent->rpdesk->dwConsoleThreadId)));
  645. RIPERR0(ERROR_ACCESS_DENIED,
  646. RIP_WARNING,
  647. "Access denied to desktop in zzzSetWindowsHookEx");
  648. return NULL;
  649. }
  650. if (amDesired != DESKTOP_HOOKCONTROL &&
  651. (ptiCurrent->rpdesk->rpwinstaParent->dwWSF_Flags & WSF_NOIO)) {
  652. RIPERR0(ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION,
  653. RIP_WARNING,
  654. "Journal hooks invalid on a desktop belonging to a non-interactive WindowStation.");
  655. return NULL;
  656. }
  657. /*
  658. * Allocate the new HOOK structure.
  659. */
  660. phkNew = HMAllocObject(ptiCurrent, ptiCurrent->rpdesk, TYPE_HOOK, sizeof(HOOK));
  661. if (phkNew == NULL) {
  662. return NULL;
  663. }
  664. /*
  665. * If a DLL is required for this hook, register the library with
  666. * the library management routines so we can assure it's loaded
  667. * into all the processes necessary.
  668. */
  669. phkNew->ihmod = -1;
  670. if (hmod != NULL) {
  671. phkNew->ihmod = GetHmodTableIndex(pstrLib);
  672. if (phkNew->ihmod == -1) {
  673. RIPERR2(ERROR_MOD_NOT_FOUND,
  674. RIP_WARNING,
  675. "Couldn't load DLL %.*ws",
  676. pstrLib->Length,
  677. pstrLib->Buffer);
  678. HMFreeObject((PVOID)phkNew);
  679. return NULL;
  680. }
  681. /*
  682. * Add a dependency on this module - meaning, increment a count
  683. * that simply counts the number of hooks set into this module.
  684. */
  685. if (phkNew->ihmod >= 0) {
  686. AddHmodDependency(phkNew->ihmod);
  687. }
  688. }
  689. /*
  690. * Depending on whether we're setting a global or local hook,
  691. * get the start of the appropriate linked-list of HOOKs. Also
  692. * set the HF_GLOBAL flag if it's a global hook.
  693. */
  694. if (ptiThread != NULL) {
  695. pphkStart = &ptiThread->aphkStart[nFilterType + 1];
  696. /*
  697. * Set the WHF_* in the THREADINFO so we know it's hooked.
  698. */
  699. ptiThread->fsHooks |= WHF_FROM_WH(nFilterType);
  700. /*
  701. * Set the flags in the thread's TEB
  702. */
  703. if (ptiThread->pClientInfo) {
  704. BOOL fAttached;
  705. /*
  706. * If the thread being hooked is in another process, attach
  707. * to that process so that we can access its ClientInfo.
  708. */
  709. if (ptiThread->ppi != ptiCurrent->ppi) {
  710. KeAttachProcess(PsGetProcessPcb(ptiThread->ppi->Process));
  711. fAttached = TRUE;
  712. } else
  713. fAttached = FALSE;
  714. try {
  715. ptiThread->pClientInfo->fsHooks = ptiThread->fsHooks;
  716. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  717. /*
  718. * Worst case scenario is the the cleint side will
  719. * be out of sync of which hooks are installed
  720. */
  721. }
  722. if (fAttached) {
  723. KeDetachProcess();
  724. }
  725. }
  726. /*
  727. * Remember which thread we're hooking.
  728. */
  729. phkNew->ptiHooked = ptiThread;
  730. } else {
  731. pphkStart = &ptiCurrent->pDeskInfo->aphkStart[nFilterType + 1];
  732. phkNew->flags |= HF_GLOBAL;
  733. /*
  734. * Set the WHF_* in the SERVERINFO so we know it's hooked.
  735. */
  736. ptiCurrent->pDeskInfo->fsHooks |= WHF_FROM_WH(nFilterType);
  737. phkNew->ptiHooked = NULL;
  738. }
  739. /*
  740. * Does the hook function expect ANSI or Unicode text?
  741. */
  742. phkNew->flags |= (dwFlags & HF_ANSI);
  743. /*
  744. * Initialize the HOOK structure. Unreferenced parameters are assumed
  745. * to be initialized to zero by LocalAlloc().
  746. */
  747. phkNew->iHook = nFilterType;
  748. /*
  749. * Libraries are loaded at different linear addresses in different
  750. * process contexts. For this reason, we need to convert the filter
  751. * proc address into an offset while setting the hook, and then convert
  752. * it back to a real per-process function pointer when calling a
  753. * hook. Do this by subtracting the 'hmod' (which is a pointer to the
  754. * linear and contiguous .exe header) from the function index.
  755. */
  756. phkNew->offPfn = ((ULONG_PTR)pfnFilterProc) - ((ULONG_PTR)hmod);
  757. #ifdef HOOKBATCH
  758. phkNew->cEventMessages = 0;
  759. phkNew->iCurrentEvent = 0;
  760. phkNew->CacheTimeOut = 0;
  761. phkNew->aEventCache = NULL;
  762. #endif //HOOKBATCH
  763. /*
  764. * Link this hook into the front of the hook-list.
  765. */
  766. phkNew->phkNext = *pphkStart;
  767. *pphkStart = phkNew;
  768. /*
  769. * If this is a journal hook, setup synchronized input processing
  770. * AFTER we set the hook - so this synchronization can be cancelled
  771. * with control-esc.
  772. */
  773. if (abHookFlags[nFilterType + 1] & HKF_JOURNAL) {
  774. /*
  775. * Attach everyone to us so journal-hook processing
  776. * will be synchronized.
  777. * No need to DeferWinEventNotify() here, since we lock phkNew.
  778. */
  779. ThreadLockAlwaysWithPti(ptiCurrent, phkNew, &tlphkNew);
  780. if (!zzzJournalAttach(ptiCurrent, TRUE)) {
  781. RIPMSG1(RIP_WARNING, "zzzJournalAttach failed, so abort hook %#p", phkNew);
  782. if (ThreadUnlock(&tlphkNew) != NULL) {
  783. zzzUnhookWindowsHookEx(phkNew);
  784. }
  785. return NULL;
  786. }
  787. if ((phkNew = ThreadUnlock(&tlphkNew)) == NULL) {
  788. return NULL;
  789. }
  790. }
  791. UserAssert(phkNew != NULL);
  792. /*
  793. * Later 5.0 GerardoB: The old code just to check this but
  794. * I think it's some left over stuff from server side days.
  795. .* Let's assert on it for a while
  796. * Also, I added the assertions in the else's below because I reorganized
  797. * the code and want to make sure we don't change behavior
  798. */
  799. UserAssert(ptiCurrent->pEThread && PsGetThreadProcess(ptiCurrent->pEThread));
  800. /*
  801. * Can't allow a process that has set a global hook that works
  802. * on server-side winprocs to run at background priority! Bump
  803. * up it's dynamic priority and mark it so it doesn't get reset.
  804. */
  805. if ((phkNew->flags & HF_GLOBAL) &&
  806. (abHookFlags[nFilterType + 1] & HKF_INTERSENDABLE)) {
  807. ptiCurrent->TIF_flags |= TIF_GLOBALHOOKER;
  808. KeSetPriorityThread(PsGetThreadTcb(ptiCurrent->pEThread), LOW_REALTIME_PRIORITY-2);
  809. if (abHookFlags[nFilterType + 1] & HKF_JOURNAL) {
  810. ThreadLockAlwaysWithPti(ptiCurrent, phkNew, &tlphkNew);
  811. /*
  812. * If we're changing the journal hooks, jiggle the mouse.
  813. * This way the first event will always be a mouse move, which
  814. * will ensure that the cursor is set properly.
  815. */
  816. zzzSetFMouseMoved();
  817. phkNew = ThreadUnlock(&tlphkNew);
  818. /*
  819. * If setting a journal playback hook, this process is the input
  820. * provider. This gives it the right to call SetForegroundWindow
  821. */
  822. if (nFilterType == WH_JOURNALPLAYBACK) {
  823. gppiInputProvider = ptiCurrent->ppi;
  824. }
  825. } else {
  826. UserAssert(nFilterType != WH_JOURNALPLAYBACK);
  827. }
  828. } else {
  829. UserAssert(!(abHookFlags[nFilterType + 1] & HKF_JOURNAL));
  830. UserAssert(nFilterType != WH_JOURNALPLAYBACK);
  831. }
  832. /*
  833. * Return pointer to our internal hook structure so we know
  834. * which hook to call next in CallNextHookEx().
  835. */
  836. DbgValidateHooks(phkNew, phkNew->iHook);
  837. return phkNew;
  838. }
  839. /***************************************************************************\
  840. * xxxCallNextHookEx
  841. *
  842. * In the new world DefHookProc() is a bit deceptive since SetWindowsHook()
  843. * isn't returning the actual address of the next hook to call, but instead
  844. * a hook handle. CallNextHookEx() is a slightly clearer picture of what's
  845. * going on so apps don't get tempted to try and call the value we return.
  846. *
  847. * As a side note we don't actually use the hook handle passed in. We keep
  848. * track of which hooks is currently being called on a thread in the Q
  849. * structure and use that. This is because SetWindowsHook() will sometimes
  850. * return NULL to be compatible with the way it used to work, but even though
  851. * we may be dealing with the last 'local' hook, there may be further 'global'
  852. * hooks we need to call. PhkNext() is smart enough to jump over to the
  853. * 'global' hook chain if it reaches the end of the 'local' hook chain.
  854. *
  855. * History:
  856. * 01-30-91 DavidPe Created.
  857. \***************************************************************************/
  858. LRESULT xxxCallNextHookEx(
  859. int nCode,
  860. WPARAM wParam,
  861. LPARAM lParam)
  862. {
  863. BOOL bAnsiHook;
  864. if (PtiCurrent()->sphkCurrent == NULL) {
  865. return 0;
  866. }
  867. return xxxCallHook2(PhkNextValid(PtiCurrent()->sphkCurrent), nCode, wParam, lParam, &bAnsiHook);
  868. }
  869. /***************************************************************************\
  870. * CheckWHFBits
  871. *
  872. * This routine checks to see if any hooks for nFilterType exist, and clear
  873. * the appropriate WHF_ in the THREADINFO and SERVERINFO.
  874. *
  875. * History:
  876. * 08-17-92 DavidPe Created.
  877. \***************************************************************************/
  878. VOID CheckWHFBits(
  879. PTHREADINFO pti,
  880. int nFilterType)
  881. {
  882. BOOL fClearThreadBits;
  883. BOOL fClearDesktopBits;
  884. PHOOK phook;
  885. /*
  886. * Assume we're are going to clear local(thread) and
  887. * global(desktop) bits.
  888. */
  889. fClearThreadBits = TRUE;
  890. fClearDesktopBits = TRUE;
  891. /*
  892. * Get the first valid hook for this thread
  893. */
  894. phook = PhkFirstValid(pti, nFilterType);
  895. if (phook != NULL) {
  896. /*
  897. * If it found a global hook, don't clear the desktop bits
  898. * (that would mean that there are no local(thread) hooks
  899. * so we fall through to clear the thread bits)
  900. */
  901. if (phook->flags & HF_GLOBAL) {
  902. fClearDesktopBits = FALSE;
  903. } else {
  904. /*
  905. * It found a thread hook so don't clear the thread bits
  906. */
  907. fClearThreadBits = FALSE;
  908. /*
  909. * Check for global hooks now. If there is one, don't
  910. * clear the desktop bits
  911. */
  912. phook = PhkFirstGlobalValid(pti, nFilterType);
  913. fClearDesktopBits = (phook == NULL);
  914. }
  915. } /* if (phook != NULL) */
  916. if (fClearThreadBits) {
  917. pti->fsHooks &= ~(WHF_FROM_WH(nFilterType));
  918. /*
  919. * Set the flags in the thread's TEB
  920. */
  921. if (pti->pClientInfo) {
  922. BOOL fAttached;
  923. /*
  924. * If the hooked thread is in another process, attach
  925. * to that process to access its address space.
  926. */
  927. if (pti->ppi != PpiCurrent()) {
  928. KeAttachProcess(PsGetProcessPcb(pti->ppi->Process));
  929. fAttached = TRUE;
  930. } else
  931. fAttached = FALSE;
  932. try {
  933. pti->pClientInfo->fsHooks = pti->fsHooks;
  934. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  935. /*
  936. * Worst case scenario is the the cleint side will
  937. * be out of sync of which hooks are installed
  938. */
  939. }
  940. if (fAttached)
  941. KeDetachProcess();
  942. }
  943. }
  944. if (fClearDesktopBits) {
  945. pti->pDeskInfo->fsHooks &= ~(WHF_FROM_WH(nFilterType));
  946. }
  947. }
  948. /***************************************************************************\
  949. * zzzUnhookWindowsHook (API)
  950. *
  951. * This is the old version of the Unhook API. It does the same thing as
  952. * zzzUnhookWindowsHookEx(), but takes a filter-type and filter-proc to
  953. * identify which hook to unhook.
  954. *
  955. * History:
  956. * 01-28-91 DavidPe Created.
  957. \***************************************************************************/
  958. BOOL zzzUnhookWindowsHook(
  959. int nFilterType,
  960. PROC pfnFilterProc)
  961. {
  962. PHOOK phk;
  963. PTHREADINFO ptiCurrent;
  964. if ((nFilterType < WH_MIN) || (nFilterType > WH_MAX)) {
  965. RIPERR0(ERROR_INVALID_HOOK_FILTER, RIP_VERBOSE, "");
  966. return FALSE;
  967. }
  968. ptiCurrent = PtiCurrent();
  969. for (phk = PhkFirstValid(ptiCurrent, nFilterType); phk != NULL; phk = PhkNextValid(phk)) {
  970. /*
  971. * Is this the hook we're looking for?
  972. */
  973. if (PFNHOOK(phk) == pfnFilterProc) {
  974. /*
  975. * Are we on the thread that set the hook?
  976. * If not return an error.
  977. */
  978. if (GETPTI(phk) != ptiCurrent) {
  979. RIPERR0(ERROR_ACCESS_DENIED,
  980. RIP_WARNING,
  981. "Access denied in zzzUnhookWindowsHook: "
  982. "this thread is not the same as that which set the hook");
  983. return FALSE;
  984. }
  985. return zzzUnhookWindowsHookEx( phk );
  986. }
  987. }
  988. /*
  989. * Didn't find the hook we were looking for so return FALSE.
  990. */
  991. RIPERR0(ERROR_HOOK_NOT_INSTALLED, RIP_VERBOSE, "");
  992. return FALSE;
  993. }
  994. /***************************************************************************\
  995. * zzzUnhookWindowsHookEx (API)
  996. *
  997. * Applications call this API to 'unhook' a hook. First we check if someone
  998. * is currently calling this hook. If no one is we go ahead and free the
  999. * HOOK structure now. If someone is then we simply clear the filter-proc
  1000. * in the HOOK structure. In xxxCallHook2() we check for this and if by
  1001. * that time no one is calling the hook in question we free it there.
  1002. *
  1003. * History:
  1004. * 01-28-91 DavidPe Created.
  1005. \***************************************************************************/
  1006. BOOL zzzUnhookWindowsHookEx(
  1007. PHOOK phkFree)
  1008. {
  1009. PTHREADINFO pti;
  1010. pti = GETPTI(phkFree);
  1011. /*
  1012. * If this hook is already destroyed, bail
  1013. */
  1014. if (phkFree->flags & HF_DESTROYED) {
  1015. RIPMSG1(RIP_WARNING, "_UnhookWindowsHookEx(%#p) already destroyed", phkFree);
  1016. return FALSE;
  1017. }
  1018. /*
  1019. * Clear the journaling flags in all the queues.
  1020. */
  1021. if (abHookFlags[phkFree->iHook + 1] & HKF_JOURNAL) {
  1022. zzzJournalAttach(pti, FALSE);
  1023. /*
  1024. * If someone got stuck because of the hook, let him go
  1025. *
  1026. * I want to get some performance numbers before checking this in.
  1027. * MSTest hooks and unhooks all the time when running a script.
  1028. * This code has never been in. 5/22/96. GerardoB
  1029. */
  1030. // InterQueueMsgCleanup(3 * CMSWAITTOKILLTIMEOUT);
  1031. }
  1032. /*
  1033. * If no one is currently calling this hook,
  1034. * go ahead and free it now.
  1035. */
  1036. FreeHook(phkFree);
  1037. /*
  1038. * If this thread has no more global hooks that are able to hook
  1039. * server-side window procs, we must clear it's TIF_GLOBALHOOKER bit.
  1040. */
  1041. if (pti->TIF_flags & TIF_GLOBALHOOKER) {
  1042. int iHook;
  1043. PHOOK phk;
  1044. for (iHook = WH_MIN ; iHook <= WH_MAX ; ++iHook) {
  1045. /*
  1046. * Ignore those that can't hook server-side winprocs
  1047. */
  1048. if (!(abHookFlags[iHook + 1] & HKF_INTERSENDABLE)) {
  1049. continue;
  1050. }
  1051. /*
  1052. * Scan the global hooks
  1053. */
  1054. for (phk = PhkFirstGlobalValid(pti, iHook);
  1055. phk != NULL; phk = PhkNextValid(phk)) {
  1056. if (GETPTI(phk) == pti) {
  1057. goto StillHasGlobalHooks;
  1058. }
  1059. }
  1060. }
  1061. pti->TIF_flags &= ~TIF_GLOBALHOOKER;
  1062. }
  1063. StillHasGlobalHooks:
  1064. /*
  1065. * Success, return TRUE.
  1066. */
  1067. return TRUE;
  1068. }
  1069. /***************************************************************************\
  1070. * _CallMsgFilter (API)
  1071. *
  1072. * CallMsgFilter() allows applications to call the WH_*MSGFILTER hooks.
  1073. * If there's a sysmodal window we return FALSE right away. WH_MSGFILTER
  1074. * isn't called if WH_SYSMSGFILTER returned non-zero.
  1075. *
  1076. * History:
  1077. * 01-29-91 DavidPe Created.
  1078. \***************************************************************************/
  1079. BOOL _CallMsgFilter(
  1080. LPMSG pmsg,
  1081. int nCode)
  1082. {
  1083. PTHREADINFO pti;
  1084. pti = PtiCurrent();
  1085. /*
  1086. * First call WH_SYSMSGFILTER. If it returns non-zero, don't
  1087. * bother calling WH_MSGFILTER, just return TRUE. Otherwise
  1088. * return what WH_MSGFILTER gives us.
  1089. */
  1090. if (IsHooked(pti, WHF_SYSMSGFILTER) && xxxCallHook(nCode, 0, (LPARAM)pmsg,
  1091. WH_SYSMSGFILTER)) {
  1092. return TRUE;
  1093. }
  1094. if (IsHooked(pti, WHF_MSGFILTER)) {
  1095. return (BOOL)xxxCallHook(nCode, 0, (LPARAM)pmsg, WH_MSGFILTER);
  1096. }
  1097. return FALSE;
  1098. }
  1099. /***************************************************************************\
  1100. * xxxCallHook
  1101. *
  1102. * User code calls this function to call the first hook of a specific
  1103. * type.
  1104. *
  1105. * History:
  1106. * 01-29-91 DavidPe Created.
  1107. \***************************************************************************/
  1108. int xxxCallHook(
  1109. int nCode,
  1110. WPARAM wParam,
  1111. LPARAM lParam,
  1112. int iHook)
  1113. {
  1114. BOOL bAnsiHook;
  1115. return (int)xxxCallHook2(PhkFirstValid(PtiCurrent(), iHook), nCode, wParam, lParam, &bAnsiHook);
  1116. }
  1117. /***************************************************************************\
  1118. * xxxCallHook2
  1119. *
  1120. * When you have an actual HOOK structure to call, you'd use this function.
  1121. * It will check to see if the hook hasn't already been unhooked, and if
  1122. * is it will free it and keep looking until it finds a hook it can call
  1123. * or hits the end of the list. We also make sure any needed DLLs are loaded
  1124. * here. We also check to see if the HOOK was unhooked inside the call
  1125. * after we return.
  1126. *
  1127. * Note: Hooking server-side window procedures (such as the desktop and console
  1128. * windows) can only be done by sending the hook message to the hooking app.
  1129. * (This is because we must not load the hookproc DLL into the server process).
  1130. * The hook types this can be done with are currently WH_JOURNALRECORD,
  1131. * WH_JOURNALPLAYBACK, WH_KEYBOARD and WH_MOUSE : these are all marked as
  1132. * HKF_INTERSENDABLE. In order to prevent a global hooker from locking up the whole
  1133. * system, the hook message is sent with a timeout. To ensure minimal
  1134. * performance degradation, the hooker process is set to foreground priority,
  1135. * and prevented from being set back to background priority with the
  1136. * TIF_GLOBALHOOKER bit in hooking thread's pti->flags.
  1137. * Hooking emulated DOS apps is prevented with the TIF_DOSEMULATOR bit in the
  1138. * console thread: this is because these apps typically hog the CPU so much that
  1139. * the hooking app does not respond rapidly enough to the hook messsages sent
  1140. * to it. IanJa Nov 1994.
  1141. *
  1142. * History:
  1143. * 02-07-91 DavidPe Created.
  1144. * 1994 Nov 02 IanJa Hooking desktop and console windows.
  1145. \***************************************************************************/
  1146. LRESULT xxxCallHook2(
  1147. PHOOK phkCall,
  1148. int nCode,
  1149. WPARAM wParam,
  1150. LPARAM lParam,
  1151. LPBOOL lpbAnsiHook)
  1152. {
  1153. UINT iHook;
  1154. PHOOK phkSave;
  1155. LONG_PTR nRet;
  1156. PTHREADINFO ptiCurrent;
  1157. BOOL fLoadSuccess;
  1158. TL tlphkCall;
  1159. TL tlphkSave;
  1160. BYTE bHookFlags;
  1161. BOOL fMustIntersend;
  1162. CheckCritIn();
  1163. if (phkCall == NULL) {
  1164. return 0;
  1165. }
  1166. iHook = phkCall->iHook;
  1167. ptiCurrent = PtiCurrent();
  1168. /*
  1169. * Only low level hooks are allowed in the RIT context
  1170. * (This check used to be done in PhkFirstValid).
  1171. */
  1172. if (ptiCurrent == gptiRit) {
  1173. switch (iHook) {
  1174. case WH_MOUSE_LL:
  1175. case WH_KEYBOARD_LL:
  1176. #ifdef REDIRECTION
  1177. case WH_HITTEST:
  1178. #endif // REDIRECTION
  1179. break;
  1180. default:
  1181. return 0;
  1182. }
  1183. }
  1184. /*
  1185. * If this queue is in cleanup, exit: it has no business calling back
  1186. * a hook proc. Also check if hooks are disabled for the thread.
  1187. */
  1188. if ( ptiCurrent->TIF_flags & (TIF_INCLEANUP | TIF_DISABLEHOOKS) ||
  1189. ((ptiCurrent->rpdesk == NULL) && (phkCall->iHook != WH_MOUSE_LL))) {
  1190. return ampiHookError[iHook + 1];
  1191. }
  1192. /*
  1193. * Try to call each hook in the list until one is successful or
  1194. * we reach the end of the list.
  1195. */
  1196. do {
  1197. *lpbAnsiHook = phkCall->flags & HF_ANSI;
  1198. bHookFlags = abHookFlags[phkCall->iHook + 1];
  1199. /*
  1200. * Some WH_SHELL hook types can be called from console
  1201. * HSHELL_APPCOMMAND added for bug 346575 DefWindowProc invokes a shell hook
  1202. * for console windows if they don't handle the wm_appcommand message - we need the hook
  1203. * to go through for csrss.
  1204. */
  1205. if ((phkCall->iHook == WH_SHELL) && (ptiCurrent->TIF_flags & TIF_CSRSSTHREAD)) {
  1206. if ((nCode == HSHELL_LANGUAGE) || (nCode == HSHELL_WINDOWACTIVATED) ||
  1207. (nCode == HSHELL_APPCOMMAND)) {
  1208. bHookFlags |= HKF_INTERSENDABLE;
  1209. }
  1210. }
  1211. if ((phkCall->iHook == WH_SHELL) && (ptiCurrent->TIF_flags & TIF_SYSTEMTHREAD)) {
  1212. if ((nCode == HSHELL_ACCESSIBILITYSTATE) ) {
  1213. bHookFlags |= HKF_INTERSENDABLE;
  1214. }
  1215. }
  1216. fMustIntersend =
  1217. (GETPTI(phkCall) != ptiCurrent) &&
  1218. (
  1219. /*
  1220. * We always want to intersend journal hooks.
  1221. * CONSIDER (adams): Why? There's a performance hit by
  1222. * doing so, so if we haven't a reason, we shouldn't
  1223. * do it.
  1224. *
  1225. * we also need to intersend low level hooks. They can be called
  1226. * from the desktop thread, the raw input thread AND also from
  1227. * any thread that calls CallNextHookEx.
  1228. */
  1229. (bHookFlags & (HKF_JOURNAL | HKF_LOWLEVEL))
  1230. /*
  1231. * We must intersend if a 16bit app hooks a 32bit app
  1232. * because we can't load a 16bit dll into a 32bit process.
  1233. * We must also intersend if a 16bit app hooks another 16bit app
  1234. * in a different VDM, because we can't load a 16bit dll from
  1235. * one VDM into a 16bit app in another VDM (because that
  1236. * VDM is actually a 32bit process).
  1237. */
  1238. ||
  1239. ( GETPTI(phkCall)->TIF_flags & TIF_16BIT &&
  1240. ( !(ptiCurrent->TIF_flags & TIF_16BIT) ||
  1241. ptiCurrent->ppi != GETPTI(phkCall)->ppi))
  1242. #if defined(_WIN64)
  1243. /*
  1244. * Intersend if a 64bit app hooks a 32bit app or
  1245. * a 32bit app hooks a 64bit app.
  1246. * This is necessary since a hook DLL can not be loaded
  1247. * cross bit type.
  1248. */
  1249. ||
  1250. ( (GETPTI(phkCall)->TIF_flags & TIF_WOW64) !=
  1251. (ptiCurrent->TIF_flags & TIF_WOW64)
  1252. )
  1253. #endif /* defined(_WIN64) */
  1254. /*
  1255. * We must intersend if a console or system thread is calling a hook
  1256. * that is not in the same console or the system process.
  1257. */
  1258. ||
  1259. ( ptiCurrent->TIF_flags & (TIF_CSRSSTHREAD | TIF_SYSTEMTHREAD) &&
  1260. GETPTI(phkCall)->ppi != ptiCurrent->ppi)
  1261. /*
  1262. * If this is a global and non-journal hook, do a security
  1263. * check on the current desktop to see if we can call here.
  1264. * Note that we allow processes with the SYSTEM_LUID to hook
  1265. * other processes even if the other process says that it
  1266. * doesn't allow other accounts to hook them. We did this
  1267. * because there was a bug in NT 3.x that allowed it and some
  1268. * services were written to use it.
  1269. */
  1270. ||
  1271. ( phkCall->flags & HF_GLOBAL &&
  1272. !RtlEqualLuid(&GETPTI(phkCall)->ppi->luidSession, &ptiCurrent->ppi->luidSession) &&
  1273. !(ptiCurrent->TIF_flags & TIF_ALLOWOTHERACCOUNTHOOK) &&
  1274. !RtlEqualLuid(&GETPTI(phkCall)->ppi->luidSession, &luidSystem))
  1275. /*
  1276. * We must intersend if the hooking thread is running in
  1277. * another process and is restricted.
  1278. */
  1279. ||
  1280. ( GETPTI(phkCall)->ppi != ptiCurrent->ppi &&
  1281. IsRestricted(GETPTI(phkCall)->pEThread))
  1282. );
  1283. /*
  1284. * We're calling back... make sure the hook doesn't go away while
  1285. * we're calling back. We've thread locked here: we must unlock before
  1286. * returning or enumerating the next hook in the chain.
  1287. */
  1288. ThreadLockAlwaysWithPti(ptiCurrent, phkCall, &tlphkCall);
  1289. if (!fMustIntersend) {
  1290. /*
  1291. * Make sure the DLL for this hook, if any, has been loaded
  1292. * for the current process.
  1293. */
  1294. if ((phkCall->ihmod != -1) &&
  1295. (TESTHMODLOADED(ptiCurrent, phkCall->ihmod) == 0)) {
  1296. /*
  1297. * Try loading the library, since it isn't loaded in this processes
  1298. * context. First lock this hook so it doesn't go away while we're
  1299. * loading this library.
  1300. */
  1301. fLoadSuccess = (xxxLoadHmodIndex(phkCall->ihmod) != NULL);
  1302. /*
  1303. * If the LoadLibrary() failed, skip to the next hook and try
  1304. * again.
  1305. */
  1306. if (!fLoadSuccess) {
  1307. goto LoopAgain;
  1308. }
  1309. }
  1310. /*
  1311. * Is WH_DEBUG installed? If we're not already calling it, do so.
  1312. */
  1313. if (IsHooked(ptiCurrent, WHF_DEBUG) && (phkCall->iHook != WH_DEBUG)) {
  1314. DEBUGHOOKINFO debug;
  1315. debug.idThread = TIDq(ptiCurrent);
  1316. debug.idThreadInstaller = 0;
  1317. debug.code = nCode;
  1318. debug.wParam = wParam;
  1319. debug.lParam = lParam;
  1320. if (xxxCallHook(HC_ACTION, phkCall->iHook, (LPARAM)&debug, WH_DEBUG)) {
  1321. /*
  1322. * If WH_DEBUG returned non-zero, skip this hook and
  1323. * try the next one.
  1324. */
  1325. goto LoopAgain;
  1326. }
  1327. }
  1328. /*
  1329. * Make sure the hook is still around before we
  1330. * try and call it.
  1331. */
  1332. if (HMIsMarkDestroy(phkCall)) {
  1333. goto LoopAgain;
  1334. }
  1335. /*
  1336. * Time to call the hook! Lock it first so that it doesn't go away
  1337. * while we're using it. Thread lock right away in case the lock frees
  1338. * the previous contents.
  1339. */
  1340. #if DBG
  1341. if (phkCall->flags & HF_GLOBAL) {
  1342. UserAssert(phkCall->ptiHooked == NULL);
  1343. } else {
  1344. UserAssert(phkCall->ptiHooked == ptiCurrent);
  1345. }
  1346. #endif
  1347. phkSave = ptiCurrent->sphkCurrent;
  1348. ThreadLockWithPti(ptiCurrent, phkSave, &tlphkSave);
  1349. if (ptiCurrent->pClientInfo) {
  1350. try {
  1351. ptiCurrent->pClientInfo->phkCurrent = phkCall;
  1352. } except (W32ExceptionHandler(TRUE, RIP_WARNING)) {
  1353. nRet = 0;
  1354. goto Error;
  1355. }
  1356. }
  1357. Lock(&ptiCurrent->sphkCurrent, phkCall);
  1358. nRet = xxxHkCallHook(phkCall, nCode, wParam, lParam);
  1359. Lock(&ptiCurrent->sphkCurrent, phkSave);
  1360. if (ptiCurrent->pClientInfo) {
  1361. try {
  1362. ptiCurrent->pClientInfo->phkCurrent = phkSave;
  1363. } except (W32ExceptionHandler(TRUE, RIP_WARNING)) {
  1364. nRet = 0;
  1365. goto Error;
  1366. }
  1367. }
  1368. ThreadUnlock(&tlphkSave);
  1369. /*
  1370. * This hook proc faulted, so unhook it and try the next one.
  1371. */
  1372. if (phkCall->flags & HF_HOOKFAULTED) {
  1373. PHOOK phkFault;
  1374. phkCall = PhkNextValid(phkCall);
  1375. phkFault = ThreadUnlock(&tlphkCall);
  1376. if (phkFault != NULL) {
  1377. FreeHook(phkFault);
  1378. }
  1379. continue;
  1380. }
  1381. /*
  1382. * Lastly, we're done with this hook so it is ok to unlock it (it may
  1383. * get freed here!
  1384. */
  1385. ThreadUnlock(&tlphkCall);
  1386. return nRet;
  1387. } else if (bHookFlags & HKF_INTERSENDABLE) {
  1388. /*
  1389. * Receiving thread can access this structure since the
  1390. * sender thread's stack is locked down during xxxInterSendMsgEx
  1391. */
  1392. HOOKMSGSTRUCT hkmp;
  1393. int timeout = 200; // 1/5 second !!!
  1394. hkmp.lParam = lParam;
  1395. hkmp.phk = phkCall;
  1396. hkmp.nCode = nCode;
  1397. /*
  1398. * Thread lock right away in case the lock frees the previous contents
  1399. */
  1400. phkSave = ptiCurrent->sphkCurrent;
  1401. ThreadLockWithPti(ptiCurrent, phkSave, &tlphkSave);
  1402. Lock(&ptiCurrent->sphkCurrent, phkCall);
  1403. if (ptiCurrent->pClientInfo) {
  1404. try {
  1405. ptiCurrent->pClientInfo->phkCurrent = phkCall;
  1406. } except (W32ExceptionHandler(TRUE, RIP_WARNING)) {
  1407. nRet = 0;
  1408. goto Error;
  1409. }
  1410. }
  1411. /*
  1412. * Make sure we don't get hung!
  1413. */
  1414. if (bHookFlags & HKF_LOWLEVEL) {
  1415. timeout = gnllHooksTimeout;
  1416. if (phkCall->fLastHookHung) {
  1417. /*
  1418. * WindowsBug: 307738
  1419. * Ever Quest hooks the Low Level hook.
  1420. * If the timeout occurred in the last hook
  1421. * callback, let's make the timeout shorter
  1422. * so that the RIT is not blocked by that.
  1423. */
  1424. TAGMSG1(DBGTAG_KBD, "xxxCallHook2: LL Hook target pti=%p is marked as hung, adjusting timeout to 20", GETPTI(phkCall));
  1425. timeout = 20;
  1426. }
  1427. }
  1428. /*
  1429. * CONSIDER(adams): Why should a journaling hook be allowed to
  1430. * hang the console or a system thread? Will that interfere with
  1431. * the user's ability to cancel journaling through Ctrl+Esc?
  1432. */
  1433. if (((bHookFlags & HKF_LOWLEVEL) == 0) &&
  1434. ( (bHookFlags & HKF_JOURNAL) ||
  1435. !(ptiCurrent->TIF_flags & (TIF_CSRSSTHREAD | TIF_SYSTEMTHREAD)))) {
  1436. nRet = xxxInterSendMsgEx(NULL, WM_HOOKMSG, wParam,
  1437. (LPARAM)&hkmp, ptiCurrent, GETPTI(phkCall), NULL);
  1438. } else {
  1439. /*
  1440. * We are a server thread (console/desktop) and we aren't
  1441. * journalling, so we can't allow the hookproc to hang us -
  1442. * we must use a timeout.
  1443. */
  1444. INTRSENDMSGEX ism;
  1445. ism.fuCall = ISM_TIMEOUT;
  1446. ism.fuSend = SMTO_ABORTIFHUNG | SMTO_NORMAL;
  1447. ism.uTimeout = timeout;
  1448. ism.lpdwResult = &nRet;
  1449. /*
  1450. * Don't hook DOS apps connected to the emulator - they often
  1451. * grab too much CPU for the callback to the hookproc to
  1452. * complete in a timely fashion, causing poor response.
  1453. */
  1454. if ((ptiCurrent->TIF_flags & TIF_DOSEMULATOR) ||
  1455. FHungApp(GETPTI(phkCall), CMSHUNGAPPTIMEOUT) ||
  1456. !xxxInterSendMsgEx(NULL, WM_HOOKMSG, wParam,
  1457. (LPARAM)&hkmp, ptiCurrent, GETPTI(phkCall), &ism)) {
  1458. nRet = ampiHookError[iHook + 1];
  1459. }
  1460. /*
  1461. * If the low-level hook is eaten, the app may wake up from
  1462. * MsgWaitForMultipleObjects, clear the wake mask, but not get
  1463. * anything in GetMessage / PeekMessage and we will think it's
  1464. * hung. This causes problems in DirectInput because then the
  1465. * app may miss some hooks if FHungApp returns true, see bug
  1466. * NTBug 430342 for more details on this.
  1467. */
  1468. if ((bHookFlags & HKF_LOWLEVEL) && nRet) {
  1469. SET_TIME_LAST_READ(GETPTI(phkCall));
  1470. }
  1471. }
  1472. Lock(&ptiCurrent->sphkCurrent, phkSave);
  1473. if (ptiCurrent->pClientInfo) {
  1474. try {
  1475. ptiCurrent->pClientInfo->phkCurrent = phkSave;
  1476. } except (W32ExceptionHandler(TRUE, RIP_WARNING)) {
  1477. nRet = 0;
  1478. }
  1479. }
  1480. Error:
  1481. ThreadUnlock(&tlphkSave);
  1482. ThreadUnlock(&tlphkCall);
  1483. return nRet;
  1484. }
  1485. // fall-through
  1486. LoopAgain:
  1487. phkCall = PhkNextValid(phkCall);
  1488. ThreadUnlock(&tlphkCall);
  1489. } while (phkCall != NULL);
  1490. return ampiHookError[iHook + 1];
  1491. }
  1492. /***************************************************************************\
  1493. * xxxCallMouseHook
  1494. *
  1495. * This is a helper routine that packages up a MOUSEHOOKSTRUCTEX and calls
  1496. * the WH_MOUSE hook.
  1497. *
  1498. * History:
  1499. * 02-09-91 DavidPe Created.
  1500. \***************************************************************************/
  1501. BOOL xxxCallMouseHook(
  1502. UINT message,
  1503. PMOUSEHOOKSTRUCTEX pmhs,
  1504. BOOL fRemove)
  1505. {
  1506. BOOL bAnsiHook;
  1507. /*
  1508. * Call the mouse hook.
  1509. */
  1510. if (xxxCallHook2(PhkFirstValid(PtiCurrent(), WH_MOUSE), fRemove ?
  1511. HC_ACTION : HC_NOREMOVE, (DWORD)message, (LPARAM)pmhs, &bAnsiHook)) {
  1512. return TRUE;
  1513. }
  1514. return FALSE;
  1515. }
  1516. /***************************************************************************\
  1517. * xxxCallJournalRecordHook
  1518. *
  1519. * This is a helper routine that packages up an EVENTMSG and calls
  1520. * the WH_JOURNALRECORD hook.
  1521. *
  1522. * History:
  1523. * 02-28-91 DavidPe Created.
  1524. \***************************************************************************/
  1525. void xxxCallJournalRecordHook(
  1526. PQMSG pqmsg)
  1527. {
  1528. EVENTMSG emsg;
  1529. BOOL bAnsiHook;
  1530. /*
  1531. * Setup the EVENTMSG structure.
  1532. */
  1533. emsg.message = pqmsg->msg.message;
  1534. emsg.time = pqmsg->msg.time;
  1535. if (RevalidateHwnd(pqmsg->msg.hwnd)) {
  1536. emsg.hwnd = pqmsg->msg.hwnd;
  1537. } else {
  1538. emsg.hwnd = NULL;
  1539. }
  1540. if ((emsg.message >= WM_MOUSEFIRST) && (emsg.message <= WM_MOUSELAST)) {
  1541. emsg.paramL = (UINT)pqmsg->msg.pt.x;
  1542. emsg.paramH = (UINT)pqmsg->msg.pt.y;
  1543. } else if ((emsg.message >= WM_KEYFIRST) && (emsg.message <= WM_KEYLAST)) {
  1544. BYTE bScanCode = LOBYTE(HIWORD(pqmsg->msg.lParam));
  1545. /*
  1546. * Build up a Win 3.1 compatible journal record key
  1547. * Win 3.1 ParamL 00 00 SC VK (SC=scan code VK=virtual key)
  1548. * Also set ParamH 00 00 00 SC to be compatible with our Playback
  1549. *
  1550. * If WM_*CHAR messages ever come this way we would have a problem
  1551. * because we would lose the top byte of the Unicode character. We'd
  1552. * We'd get ParamL 00 00 SC CH (SC=scan code, CH = low byte of WCHAR)
  1553. *
  1554. */
  1555. if ((LOWORD(pqmsg->msg.wParam) == VK_PACKET) && (bScanCode == 0)) {
  1556. /*
  1557. * If we have an injected Unicode char (from SendInput), the
  1558. * character value was cached, let's give that to them too.
  1559. */
  1560. emsg.paramL = (UINT)MAKELONG(pqmsg->msg.wParam, PtiCurrent()->wchInjected);
  1561. } else {
  1562. emsg.paramL = MAKELONG(MAKEWORD(pqmsg->msg.wParam, bScanCode),0);
  1563. }
  1564. emsg.paramH = bScanCode;
  1565. UserAssert((emsg.message != WM_CHAR) &&
  1566. (emsg.message != WM_DEADCHAR) &&
  1567. (emsg.message != WM_SYSCHAR) &&
  1568. (emsg.message != WM_SYSDEADCHAR));
  1569. /*
  1570. * Set extended-key bit.
  1571. */
  1572. if (pqmsg->msg.lParam & 0x01000000) {
  1573. emsg.paramH |= 0x8000;
  1574. }
  1575. } else {
  1576. RIPMSG2(RIP_WARNING,
  1577. "Bad journal record message!\n"
  1578. " message = 0x%08lx\n"
  1579. " dwQEvent = 0x%08lx",
  1580. pqmsg->msg.message,
  1581. pqmsg->dwQEvent);
  1582. }
  1583. /*
  1584. * Call the journal recording hook.
  1585. */
  1586. xxxCallHook2(PhkFirstGlobalValid(PtiCurrent(), WH_JOURNALRECORD), HC_ACTION, 0,
  1587. (LPARAM)&emsg, &bAnsiHook);
  1588. /*
  1589. * Write the MSG parameters back because the app may have modified it.
  1590. * AfterDark's screen saver password actually zero's out the keydown
  1591. * chars.
  1592. *
  1593. * If it was a mouse message patch up the mouse point. If it was a
  1594. * WM_KEYxxx message convert the Win 3.1 compatible journal record key
  1595. * back into a half backed WM_KEYxxx format. Only the VK and SC fields
  1596. * where initialized at this point.
  1597. *
  1598. * wParam 00 00 00 VK lParam 00 SC 00 00
  1599. */
  1600. if ((pqmsg->msg.message >= WM_MOUSEFIRST) && (pqmsg->msg.message <= WM_MOUSELAST)) {
  1601. pqmsg->msg.pt.x = emsg.paramL;
  1602. pqmsg->msg.pt.y = emsg.paramH;
  1603. } else if ((pqmsg->msg.message >= WM_KEYFIRST) && (pqmsg->msg.message <= WM_KEYLAST)) {
  1604. (BYTE)pqmsg->msg.wParam = (BYTE)emsg.paramL;
  1605. ((PBYTE)&pqmsg->msg.lParam)[2] = HIBYTE(LOWORD(emsg.paramL));
  1606. }
  1607. }
  1608. /***************************************************************************\
  1609. * xxxCallJournalPlaybackHook
  1610. *
  1611. *
  1612. * History:
  1613. * 03-01-91 DavidPe Created.
  1614. \***************************************************************************/
  1615. DWORD xxxCallJournalPlaybackHook(
  1616. PQMSG pqmsg)
  1617. {
  1618. EVENTMSG emsg;
  1619. LONG dt;
  1620. PWND pwnd;
  1621. WPARAM wParam;
  1622. LPARAM lParam;
  1623. POINT pt;
  1624. PTHREADINFO ptiCurrent;
  1625. BOOL bAnsiHook = FALSE;
  1626. PHOOK phkCall;
  1627. TL tlphkCall;
  1628. UserAssert(IsWinEventNotifyDeferredOK());
  1629. TryNextEvent:
  1630. /*
  1631. * Initialized to the current time for compatibility with
  1632. * <= 3.0.
  1633. */
  1634. emsg.time = NtGetTickCount();
  1635. ptiCurrent = PtiCurrent();
  1636. pwnd = NULL;
  1637. phkCall = PhkFirstGlobalValid(ptiCurrent, WH_JOURNALPLAYBACK);
  1638. ThreadLockWithPti(ptiCurrent, phkCall, &tlphkCall);
  1639. dt = (DWORD)xxxCallHook2(phkCall, HC_GETNEXT, 0, (LPARAM)&emsg, &bAnsiHook);
  1640. /*
  1641. * -1 means some error occured. Return -1 for error.
  1642. */
  1643. if (dt == 0xFFFFFFFF) {
  1644. ThreadUnlock(&tlphkCall);
  1645. return dt;
  1646. }
  1647. /*
  1648. * Update the message id. Need this if we decide to sleep.
  1649. */
  1650. pqmsg->msg.message = emsg.message;
  1651. if (dt > 0) {
  1652. if (ptiCurrent->TIF_flags & TIF_IGNOREPLAYBACKDELAY) {
  1653. /*
  1654. * This flag tells us to ignore the requested delay (set in mnloop)
  1655. * We clear it to indicate that we did so.
  1656. */
  1657. RIPMSG1(RIP_WARNING, "Journal Playback delay ignored (%lx)", emsg.message);
  1658. ptiCurrent->TIF_flags &= ~TIF_IGNOREPLAYBACKDELAY;
  1659. dt = 0;
  1660. } else {
  1661. ThreadUnlock(&tlphkCall);
  1662. return dt;
  1663. }
  1664. }
  1665. /*
  1666. * The app is ready to be asked for the next event
  1667. */
  1668. if ((emsg.message >= WM_MOUSEFIRST) && (emsg.message <= WM_MOUSELAST)) {
  1669. pt.x = (int)emsg.paramL;
  1670. pt.y = (int)emsg.paramH;
  1671. lParam = MAKELONG(LOWORD(pt.x), LOWORD(pt.y));
  1672. wParam = 0;
  1673. /*
  1674. * If the message has changed the mouse position,
  1675. * update the cursor.
  1676. */
  1677. if (pt.x != gpsi->ptCursor.x || pt.y != gpsi->ptCursor.y) {
  1678. zzzInternalSetCursorPos(pt.x, pt.y);
  1679. }
  1680. } else if ((emsg.message >= WM_KEYFIRST) && (emsg.message < WM_KEYLAST)) {
  1681. UINT wExtraStuff = 0;
  1682. if ((emsg.message == WM_KEYUP) || (emsg.message == WM_SYSKEYUP)) {
  1683. wExtraStuff |= 0x8000;
  1684. }
  1685. if ((emsg.message == WM_SYSKEYUP) || (emsg.message == WM_SYSKEYDOWN)) {
  1686. wExtraStuff |= 0x2000;
  1687. }
  1688. if (emsg.paramH & 0x8000) {
  1689. wExtraStuff |= 0x0100;
  1690. }
  1691. if (TestKeyStateDown(ptiCurrent->pq, (BYTE)emsg.paramL)) {
  1692. wExtraStuff |= 0x4000;
  1693. }
  1694. lParam = MAKELONG(1, (UINT)((emsg.paramH & 0xFF) | wExtraStuff));
  1695. if ((LOWORD(emsg.paramL) == VK_PACKET) && (LOBYTE(emsg.paramH) == 0)) {
  1696. /*
  1697. * We are playing back an injected Unicode char (see SendInput)
  1698. * save the character for TranslateMessage to pick up.
  1699. */
  1700. ptiCurrent->wchInjected = HIWORD(emsg.paramL);
  1701. } else {
  1702. /*
  1703. * Raid# 65331
  1704. * WM_KEY* and WM_SYSKEY* messages should only contain 8bit Virtual Keys.
  1705. * Some applications passes scan code in HIBYTE and could mess up
  1706. * the system. E.g. Tab Keydown, paramL: 0x0f09 where 0f is scan code
  1707. */
  1708. DWORD dwMask = 0xff;
  1709. /*
  1710. * There are old ANSI apps that only fill in the byte for when
  1711. * they generate journal playback so we used to strip everything
  1712. * else off. That however breaks unicode journalling; 22645
  1713. * (Yes, some apps apparently do Playback WM_*CHAR msgs!)
  1714. *
  1715. */
  1716. if (!bAnsiHook || IS_DBCS_ENABLED()) {
  1717. if (IS_CHAR_MSG(emsg.message)) {
  1718. RIPMSG1(RIP_VERBOSE, "Unusual char message(%x) passed through JournalPlayback.", emsg.message);
  1719. /*
  1720. * Don't mask off HIBYTE(LOWORD(paramL)) for DBCS and UNICODE.
  1721. */
  1722. dwMask = 0xffff;
  1723. }
  1724. }
  1725. wParam = emsg.paramL & dwMask;
  1726. }
  1727. } else if (emsg.message == WM_QUEUESYNC) {
  1728. if (emsg.paramL == 0) {
  1729. pwnd = ptiCurrent->pq->spwndActive;
  1730. } else {
  1731. if ((pwnd = RevalidateHwnd((HWND)IntToPtr( emsg.paramL ))) == NULL)
  1732. pwnd = ptiCurrent->pq->spwndActive;
  1733. }
  1734. } else {
  1735. /*
  1736. * This event doesn't match up with what we're looking
  1737. * for. If the hook is still valid, then skip this message
  1738. * and try the next.
  1739. */
  1740. if (phkCall == NULL || phkCall->offPfn == 0L) {
  1741. /* Hook is nolonger valid, return -1 */
  1742. ThreadUnlock(&tlphkCall);
  1743. return 0xFFFFFFFF;
  1744. }
  1745. RIPMSG1(RIP_WARNING,
  1746. "Bad journal playback message=0x%08lx",
  1747. emsg.message);
  1748. xxxCallHook(HC_SKIP, 0, 0, WH_JOURNALPLAYBACK);
  1749. ThreadUnlock(&tlphkCall);
  1750. goto TryNextEvent;
  1751. }
  1752. StoreQMessage(pqmsg, pwnd, emsg.message, wParam, lParam, 0, 0, 0);
  1753. ThreadUnlock(&tlphkCall);
  1754. return 0;
  1755. }
  1756. /***************************************************************************\
  1757. * FreeHook
  1758. *
  1759. * Free hook unlinks the HOOK structure from its hook-list and removes
  1760. * any hmod dependencies on this hook. It also frees the HOOK structure.
  1761. *
  1762. * History:
  1763. * 01-31-91 DavidPe Created.
  1764. \***************************************************************************/
  1765. VOID FreeHook(
  1766. PHOOK phkFree)
  1767. {
  1768. /*
  1769. * Paranoia...
  1770. */
  1771. UserAssert(!(phkFree->flags & HF_FREED));
  1772. /*
  1773. * If we came from zzzUnhookWindowsHookEx, journalling hooks have
  1774. * already been cleaned up. Otherwise, they'll get cleaned up in
  1775. * xxxInternalGetMessage through the gpdeskRecalcQueueAttach mechanism.
  1776. */
  1777. /*
  1778. * Clear fsHooks bits the first time around (and mark it as destroyed).
  1779. */
  1780. if (!(phkFree->flags & HF_DESTROYED)) {
  1781. DbgValidateHooks (phkFree, phkFree->iHook);
  1782. phkFree->flags |= HF_DESTROYED;
  1783. /*
  1784. * This hook has been marked as destroyed so CheckWHSBits
  1785. * won't take it into account when updating the fsHooks bits.
  1786. * However, this means that right at this moment fsHooks is
  1787. * out of sync. So we need a flag to make the assertion
  1788. * happy.
  1789. */
  1790. #if DBG
  1791. phkFree->flags |= HF_INCHECKWHF;
  1792. #endif
  1793. UserAssert((phkFree->ptiHooked != NULL) || (phkFree->flags & HF_GLOBAL));
  1794. CheckWHFBits(phkFree->ptiHooked != NULL
  1795. ? phkFree->ptiHooked
  1796. : GETPTI(phkFree),
  1797. phkFree->iHook);
  1798. #if DBG
  1799. phkFree->flags &= ~HF_INCHECKWHF;
  1800. #endif
  1801. }
  1802. /*
  1803. * Mark it for destruction. If it the object is locked it can't
  1804. * be freed right now.
  1805. */
  1806. if (!HMMarkObjectDestroy((PVOID)phkFree)) {
  1807. return;
  1808. }
  1809. /*
  1810. * We're going to free this hook so get it off the list.
  1811. */
  1812. UnlinkHook(phkFree);
  1813. /*
  1814. * Now remove the hmod dependency and free the
  1815. * HOOK structure.
  1816. */
  1817. if (phkFree->ihmod >= 0) {
  1818. RemoveHmodDependency(phkFree->ihmod);
  1819. }
  1820. #ifdef HOOKBATCH
  1821. /*
  1822. * Free the cached Events
  1823. */
  1824. if (phkFree->aEventCache) {
  1825. UserFreePool(phkFree->aEventCache);
  1826. phkFree->aEventCache = NULL;
  1827. }
  1828. #endif //HOOKBATCH
  1829. #if DBG
  1830. phkFree->flags |= HF_FREED;
  1831. #endif
  1832. HMFreeObject((PVOID)phkFree);
  1833. return;
  1834. }
  1835. /***************************************************************************\
  1836. * UnlinkHook
  1837. *
  1838. * Gets a hook out of its chain. Note that FreeThreadsWindowHooks unlinks
  1839. * some hooks but don't free them. So this function doesn't assume that
  1840. * the hook is going away.
  1841. *
  1842. * History:
  1843. * 04-25-97 GerardoB Added Header
  1844. \***************************************************************************/
  1845. void UnlinkHook(
  1846. PHOOK phkFree)
  1847. {
  1848. PHOOK *pphkNext;
  1849. PTHREADINFO ptiT;
  1850. CheckCritIn();
  1851. /*
  1852. * Since we have the HOOK structure, we can tell if this a global
  1853. * or local hook and start on the right list.
  1854. */
  1855. if (phkFree->flags & HF_GLOBAL) {
  1856. pphkNext = &GETPTI(phkFree)->pDeskInfo->aphkStart[phkFree->iHook + 1];
  1857. } else {
  1858. ptiT = phkFree->ptiHooked;
  1859. if (ptiT == NULL) {
  1860. /*
  1861. * Already unlinked (by FreeThreadsWindowHooks)
  1862. */
  1863. return;
  1864. } else {
  1865. /*
  1866. * Clear ptiHooked so we won't try to unlink it again.
  1867. */
  1868. phkFree->ptiHooked = NULL;
  1869. }
  1870. pphkNext = &(ptiT->aphkStart[phkFree->iHook + 1]);
  1871. /*
  1872. * There must be at least one hook in the chain
  1873. */
  1874. UserAssert(*pphkNext != NULL);
  1875. }
  1876. /*
  1877. * Find the address of the phkNext pointing to phkFree
  1878. */
  1879. while ((*pphkNext != phkFree) && (*pphkNext != NULL)) {
  1880. pphkNext = &(*pphkNext)->phkNext;
  1881. }
  1882. /*
  1883. * If we haven't found it, it must be global hook whose owner is gone or
  1884. * has switched desktops.
  1885. */
  1886. if (*pphkNext == NULL) {
  1887. UserAssert(phkFree->flags & HF_GLOBAL);
  1888. /*
  1889. * if we saved a pdesk, use it. Else use the one we allocated it from
  1890. */
  1891. if (phkFree->rpdesk != NULL) {
  1892. UserAssert(GETPTI(phkFree) == gptiRit);
  1893. UserAssert(phkFree->rpdesk != NULL);
  1894. UserAssert(phkFree->rpdesk->pDeskInfo != gptiRit->pDeskInfo);
  1895. pphkNext = &phkFree->rpdesk->pDeskInfo->aphkStart[phkFree->iHook + 1];
  1896. } else {
  1897. UserAssert(GETPTI(phkFree)->pDeskInfo != phkFree->head.rpdesk->pDeskInfo);
  1898. pphkNext = &phkFree->head.rpdesk->pDeskInfo->aphkStart[phkFree->iHook + 1];
  1899. }
  1900. UserAssert(*pphkNext != NULL);
  1901. while ((*pphkNext != phkFree) && (*pphkNext != NULL)) {
  1902. pphkNext = &(*pphkNext)->phkNext;
  1903. }
  1904. }
  1905. /*
  1906. * We're supposed to find it
  1907. */
  1908. UserAssert(*pphkNext == phkFree);
  1909. /*
  1910. * Unlink it
  1911. */
  1912. *pphkNext = phkFree->phkNext;
  1913. phkFree->phkNext = NULL;
  1914. /*
  1915. * If we had a desktop, unlock it
  1916. */
  1917. if (phkFree->rpdesk != NULL) {
  1918. UserAssert(phkFree->flags & HF_GLOBAL);
  1919. UserAssert(GETPTI(phkFree) == gptiRit);
  1920. UnlockDesktop(&phkFree->rpdesk, LDU_HOOK_DESK, 0);
  1921. }
  1922. }
  1923. /***************************************************************************\
  1924. * PhkFirstGlobalValid
  1925. *
  1926. * Returns the first not-destroyed hook on the given desktop info.
  1927. *
  1928. * History:
  1929. * 03/24/97 GerardoB Created
  1930. \***************************************************************************/
  1931. PHOOK PhkFirstGlobalValid(PTHREADINFO pti, int nFilterType)
  1932. {
  1933. PHOOK phk;
  1934. CheckCritIn();
  1935. phk = pti->pDeskInfo->aphkStart[nFilterType + 1];
  1936. /*
  1937. * Return the first hook that it's not destroyed (i.e, the
  1938. * first valid one).
  1939. */
  1940. if ((phk != NULL) && (phk->flags & HF_DESTROYED)) {
  1941. phk = PhkNextValid(phk);
  1942. }
  1943. /*
  1944. * Good place to check fsHooks. If the bits are out of sync,
  1945. * someone must be adjusting them.
  1946. */
  1947. DbgValidatefsHook(phk, nFilterType, pti, TRUE);
  1948. DbgValidateHooks(phk, nFilterType);
  1949. return phk;
  1950. }
  1951. /***************************************************************************\
  1952. * PhkFirstValid
  1953. *
  1954. * Given a filter-type PhkFirstValid() returns the first hook, if any, of the
  1955. * specified type.
  1956. *
  1957. * History:
  1958. * 02-10-91 DavidPe Created.
  1959. \***************************************************************************/
  1960. PHOOK PhkFirstValid(
  1961. PTHREADINFO pti,
  1962. int nFilterType)
  1963. {
  1964. PHOOK phk;
  1965. CheckCritIn();
  1966. /*
  1967. * Grab the first hook off the local hook-list
  1968. * for the current queue.
  1969. */
  1970. phk = pti->aphkStart[nFilterType + 1];
  1971. /*
  1972. * If there aren't any local hooks, try the global hooks.
  1973. */
  1974. if (phk == NULL) {
  1975. phk = pti->pDeskInfo->aphkStart[nFilterType + 1];
  1976. }
  1977. /*
  1978. * Return the first hook that it's not destroyed (i.e, the
  1979. * first valid one).
  1980. */
  1981. if ((phk != NULL) && (phk->flags & HF_DESTROYED)) {
  1982. phk = PhkNextValid(phk);
  1983. }
  1984. /*
  1985. * Good place to check fsHooks. If the bits are out of sync,
  1986. * someone must be adjusting them.
  1987. */
  1988. DbgValidatefsHook(phk, nFilterType, pti, FALSE);
  1989. DbgValidateHooks(phk, nFilterType);
  1990. return phk;
  1991. }
  1992. /***************************************************************************\
  1993. * FreeThreadsWindowHooks
  1994. *
  1995. * During 'exit-list' processing this function is called to free any hooks
  1996. * created on, or set for the current queue.
  1997. *
  1998. * History:
  1999. * 02-10-91 DavidPe Created.
  2000. \***************************************************************************/
  2001. VOID FreeThreadsWindowHooks(VOID)
  2002. {
  2003. int iHook;
  2004. PHOOK phk, phkNext;
  2005. PTHREADINFO ptiCurrent = PtiCurrent();
  2006. /*
  2007. * If there is not thread info, there are not hooks to worry about
  2008. */
  2009. if (ptiCurrent == NULL || ptiCurrent->rpdesk == NULL) {
  2010. return;
  2011. }
  2012. /*
  2013. * In case we have a hook locked in as the current hook unlock it
  2014. * so it can be freed
  2015. */
  2016. Unlock(&ptiCurrent->sphkCurrent);
  2017. UserAssert(ptiCurrent->TIF_flags & TIF_INCLEANUP);
  2018. /*
  2019. * Loop through all the hook types.
  2020. */
  2021. for (iHook = WH_MIN ; iHook <= WH_MAX ; ++iHook) {
  2022. /*
  2023. * Loop through all the hooks of this type, including the
  2024. * ones already marked as destroyed (so don't call
  2025. * PhkFirstValid and PhkNextValid).
  2026. */
  2027. phk = ptiCurrent->aphkStart[iHook + 1];
  2028. if (phk == NULL) {
  2029. phk = ptiCurrent->pDeskInfo->aphkStart[iHook + 1];
  2030. UserAssert((phk == NULL) || (phk->flags & HF_GLOBAL));
  2031. }
  2032. while (phk != NULL) {
  2033. /*
  2034. * We might free phk below, so grab the next now
  2035. * If at end of local chain, jump to the global chain
  2036. */
  2037. phkNext = phk->phkNext;
  2038. if ((phkNext == NULL) && !(phk->flags & HF_GLOBAL)) {
  2039. phkNext = ptiCurrent->pDeskInfo->aphkStart[iHook + 1];
  2040. UserAssert((phkNext == NULL) || (phkNext->flags & HF_GLOBAL));
  2041. }
  2042. /*
  2043. * If this is a local(thread) hook, unlink it and mark it as
  2044. * destroyed so we won't call it anymore. We want to do
  2045. * this even if not calling FreeHook; also note that
  2046. * FreeHook won't unlink it if locked so we do it here anyway.
  2047. */
  2048. if (!(phk->flags & HF_GLOBAL)) {
  2049. UserAssert(ptiCurrent == phk->ptiHooked);
  2050. UnlinkHook(phk);
  2051. phk->flags |= HF_DESTROYED;
  2052. phk->phkNext = NULL;
  2053. }
  2054. /*
  2055. * If this hook was created by this thread, free it
  2056. */
  2057. if (GETPTI(phk) == ptiCurrent) {
  2058. FreeHook(phk);
  2059. }
  2060. phk = phkNext;
  2061. }
  2062. /*
  2063. * All local hooks should be unlinked
  2064. */
  2065. UserAssert(ptiCurrent->aphkStart[iHook + 1] == NULL);
  2066. } /* for (iHook = WH_MIN....*/
  2067. /*
  2068. * Keep fsHooks in sync.
  2069. */
  2070. ptiCurrent->fsHooks = 0;
  2071. }
  2072. /***************************************************************************\
  2073. * zzzRegisterSystemThread: Private API
  2074. *
  2075. * Used to set various attributes pertaining to a thread.
  2076. *
  2077. * History:
  2078. * 21-Jun-1994 from Chicago Created.
  2079. \***************************************************************************/
  2080. VOID zzzRegisterSystemThread (DWORD dwFlags, DWORD dwReserved)
  2081. {
  2082. PTHREADINFO ptiCurrent;
  2083. UserAssert(dwReserved == 0);
  2084. if (dwReserved != 0)
  2085. return;
  2086. ptiCurrent = PtiCurrent();
  2087. if (dwFlags & RST_DONTATTACHQUEUE)
  2088. ptiCurrent->TIF_flags |= TIF_DONTATTACHQUEUE;
  2089. if (dwFlags & RST_DONTJOURNALATTACH) {
  2090. ptiCurrent->TIF_flags |= TIF_DONTJOURNALATTACH;
  2091. /*
  2092. * If we are already journaling, then this queue was already
  2093. * journal attached. We need to unattach and reattach journaling
  2094. * so that we are removed from the journal attached queues.
  2095. */
  2096. if (FJOURNALPLAYBACK() || FJOURNALRECORD()) {
  2097. zzzJournalAttach(ptiCurrent, FALSE);
  2098. zzzJournalAttach(ptiCurrent, TRUE);
  2099. }
  2100. }
  2101. }
  2102. /***************************************************************************\
  2103. * xxxGetCursorPos
  2104. *
  2105. \***************************************************************************/
  2106. BOOL
  2107. xxxGetCursorPos(
  2108. LPPOINT lpPt)
  2109. {
  2110. POINT pt;
  2111. PTHREADINFO ptiCurrent = PtiCurrent();
  2112. CheckCritIn();
  2113. if (ptiCurrent->rpdesk && ptiCurrent->rpdesk != grpdeskRitInput) {
  2114. return FALSE;
  2115. }
  2116. pt.x = gpsi->ptCursor.x;
  2117. pt.y = gpsi->ptCursor.y;
  2118. try {
  2119. ProbeForWrite(lpPt, sizeof(POINT), DATAALIGN);
  2120. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  2121. return FALSE;
  2122. }
  2123. #ifdef REDIRECTION
  2124. /*
  2125. * If there is no CBT hook installed bail out.
  2126. */
  2127. if (IsHooked(PtiCurrent(), WHF_CBT)) {
  2128. xxxCallHook(HCBT_GETCURSORPOS, 0, (LPARAM)&pt, WH_CBT);
  2129. }
  2130. #endif // REDIRECTION
  2131. try {
  2132. RtlCopyMemory(lpPt, &pt, sizeof(POINT));
  2133. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  2134. return FALSE;
  2135. }
  2136. return TRUE;
  2137. }
  2138. /***************************************************************************\
  2139. * _RegisterUserApiHook
  2140. *
  2141. * History:
  2142. * 03-Mar-2000 JerrySh Created.
  2143. \***************************************************************************/
  2144. BOOL _RegisterUserApiHook(
  2145. PUNICODE_STRING pstrLib,
  2146. ULONG_PTR offPfnInitUserApiHook)
  2147. {
  2148. /*
  2149. * If we've already registered the UserApiHook, don't do it again.
  2150. */
  2151. if (IsInsideUserApiHook()) {
  2152. RIPERR0(ERROR_ACCESS_DENIED, RIP_WARNING, "UserApiHook already registered");
  2153. return FALSE;
  2154. }
  2155. /*
  2156. * If this thread is restricted, don't let it install a hook.
  2157. */
  2158. if (IsRestricted(PsGetCurrentThread())) {
  2159. RIPERR0(ERROR_ACCESS_DENIED, RIP_WARNING, "Thread is restricted");
  2160. return FALSE;
  2161. }
  2162. /*
  2163. * Register the library with the library management routines so we
  2164. * can assure it's loaded into all the processes necessary.
  2165. */
  2166. gihmodUserApiHook = GetHmodTableIndex(pstrLib);
  2167. if (gihmodUserApiHook == -1) {
  2168. RIPERR0(ERROR_MOD_NOT_FOUND, RIP_VERBOSE, "");
  2169. return FALSE;
  2170. }
  2171. /*
  2172. * Add a dependency on this module - meaning, increment a count
  2173. * that simply counts the number of hooks set into this module.
  2174. */
  2175. AddHmodDependency(gihmodUserApiHook);
  2176. /*
  2177. * Remember which process registered the hook.
  2178. */
  2179. gppiUserApiHook = PpiCurrent();
  2180. /*
  2181. * Remember the offset to the DefWindowProc init routine.
  2182. */
  2183. goffPfnInitUserApiHook = offPfnInitUserApiHook;
  2184. /*
  2185. * Set the global flag in gpsi to signal hooking ( later used
  2186. * as a check at beginning of hooked API's for loading DLL. )
  2187. */
  2188. SET_SRVIF(SRVIF_HOOKED);
  2189. return TRUE;
  2190. }
  2191. /***************************************************************************\
  2192. * _UnregisterUserApiHook
  2193. *
  2194. * History:
  2195. * 03-Mar-2000 JerrySh Created.
  2196. \***************************************************************************/
  2197. BOOL _UnregisterUserApiHook(VOID)
  2198. {
  2199. /*
  2200. * The window proc handler must be unregistered by the same process that
  2201. * registered it.
  2202. */
  2203. if (PpiCurrent() != gppiUserApiHook) {
  2204. RIPERR0(ERROR_ACCESS_DENIED, RIP_WARNING, "Process hasn't registered UserApiHook");
  2205. return FALSE;
  2206. }
  2207. gppiUserApiHook = NULL;
  2208. /*
  2209. * Make sure each hooked thread will unload the hook proc DLL
  2210. */
  2211. if (gihmodUserApiHook >= 0) {
  2212. RemoveHmodDependency(gihmodUserApiHook);
  2213. gihmodUserApiHook = -1;
  2214. }
  2215. /*
  2216. * Clear the global flag in gpsi to signal disabled hooking ( later used
  2217. * as a check at beginning of hooked API's for loading DLL. )
  2218. */
  2219. CLEAR_SRVIF(SRVIF_HOOKED);
  2220. return TRUE;
  2221. }
  2222. #ifdef MESSAGE_PUMP_HOOK
  2223. /***************************************************************************\
  2224. * _DoInitMessagePumpHook
  2225. *
  2226. * History:
  2227. * 05-Dec-2000 JStall Created
  2228. \***************************************************************************/
  2229. BOOL _DoInitMessagePumpHook(VOID)
  2230. {
  2231. PCLIENTTHREADINFO pcti = gptiCurrent->pcti;
  2232. ++pcti->cMessagePumpHooks;
  2233. return TRUE;
  2234. }
  2235. /***************************************************************************\
  2236. * _DoUninitMessagePumpHook
  2237. *
  2238. * History:
  2239. * 05-Dec-2000 JStall Created
  2240. \***************************************************************************/
  2241. BOOL _DoUninitMessagePumpHook(VOID)
  2242. {
  2243. PCLIENTTHREADINFO pcti = gptiCurrent->pcti;
  2244. if (pcti->cMessagePumpHooks <= 0) {
  2245. return FALSE;
  2246. }
  2247. --pcti->cMessagePumpHooks;
  2248. return TRUE;
  2249. }
  2250. #endif // MESSAGE_PUMP_HOOK
  2251. /***************************************************************************\
  2252. * xxxLoadUserApiHook
  2253. *
  2254. * History:
  2255. * 03-Mar-2000 JerrySh Created.
  2256. \***************************************************************************/
  2257. BOOL xxxLoadUserApiHook(VOID)
  2258. {
  2259. PTHREADINFO ptiCurrent = PtiCurrent();
  2260. /*
  2261. * A check is made here to see if hooking is still applicable after we
  2262. * dropped to kernel. There is a timing issue that could create a problem
  2263. * where we check for hooking in user mode and decide to load but as we make the
  2264. * drop to kernel, someone else is unregistering hooks and resetting all
  2265. * globals.
  2266. */
  2267. if (!IsInsideUserApiHook()){
  2268. return FALSE;
  2269. }
  2270. /*
  2271. * If the DLL is already loaded in this process, we're done.
  2272. */
  2273. if (TESTHMODLOADED(ptiCurrent, gihmodUserApiHook)) {
  2274. return TRUE;
  2275. }
  2276. /*
  2277. * Don't load the DLL if:
  2278. * - it's a system or CSRSS thread, it's in cleanup, or it's disabled hooks
  2279. * - it's another user who hasn't granted access
  2280. * - it's Win64 status doesn't match
  2281. */
  2282. if ((ptiCurrent->TIF_flags & (TIF_SYSTEMTHREAD | TIF_CSRSSTHREAD | TIF_INCLEANUP | TIF_DISABLEHOOKS)) ||
  2283. (!RtlEqualLuid(&gppiUserApiHook->luidSession, &luidSystem) &&
  2284. !RtlEqualLuid(&gppiUserApiHook->luidSession, &ptiCurrent->ppi->luidSession) &&
  2285. !(ptiCurrent->TIF_flags & TIF_ALLOWOTHERACCOUNTHOOK)) ||
  2286. ((gppiUserApiHook->W32PF_Flags & W32PF_WOW64) != (ptiCurrent->ppi->W32PF_Flags & W32PF_WOW64))) {
  2287. return FALSE;
  2288. }
  2289. /*
  2290. * Try loading the library, since it isn't loaded in this processes
  2291. * context.
  2292. */
  2293. return (xxxLoadHmodIndex(gihmodUserApiHook) != NULL);
  2294. }