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.

6869 lines
221 KiB

  1. /****************************** Module Header ******************************\
  2. * Module Name: queue.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * This module contains the low-level code for working with the Q structure.
  7. *
  8. * History:
  9. * 12-02-90 DavidPe Created.
  10. * 02-06-91 IanJa HWND revalidation added
  11. \***************************************************************************/
  12. #include "precomp.h"
  13. #pragma hdrstop
  14. VOID DestroyProcessesObjects(PPROCESSINFO ppi);
  15. VOID DestroyThreadsMessages(PQ pq, PTHREADINFO pti);
  16. NTSTATUS CheckProcessForeground(PTHREADINFO pti);
  17. DWORD xxxPollAndWaitForSingleObject(PKEVENT pEvent, PVOID pExecObject,
  18. DWORD dwMilliseconds);
  19. NTSTATUS InitiateShutdown(PETHREAD Thread, PULONG lpdwFlags);
  20. NTSTATUS EndShutdown(PETHREAD Thread, NTSTATUS StatusShutdown);
  21. VOID SetVDMCursorBounds(LPRECT lprc);
  22. NTSTATUS InitQEntryLookaside(VOID);
  23. VOID SetAppStarting(PPROCESSINFO ppi);
  24. #if defined(_WIN64)
  25. /*
  26. * For Win64 ask winlogon to play sounds for accessibility events
  27. * (IA64 machines do not have internal speakers so we have to go
  28. * thru the sound card). Post a message to winlogon with an lParam
  29. * whose high word is ACCESS_SOUND_RANGE and low word is the index
  30. * of the sound to make. If a new RITSOUND_xx macro is added then
  31. * winlogon has to be updated too.
  32. */
  33. #define ACCESS_SOUND_RANGE 1
  34. #endif
  35. #pragma alloc_text(INIT, InitQEntryLookaside)
  36. PW32PROCESS gpwpCalcFirst;
  37. PPAGED_LOOKASIDE_LIST QLookaside;
  38. PPAGED_LOOKASIDE_LIST QEntryLookaside;
  39. #if DBG
  40. VOID DebugValidateMLIST(
  41. PMLIST pml)
  42. {
  43. int c;
  44. PQMSG pqmsg;
  45. /*
  46. * Check that the message list is properly terminated.
  47. */
  48. UserAssert(!pml->pqmsgRead || !pml->pqmsgRead->pqmsgPrev);
  49. UserAssert(!pml->pqmsgWriteLast || !pml->pqmsgWriteLast->pqmsgNext);
  50. /*
  51. * Check that there aren't loops in the Next list.
  52. */
  53. c = pml->cMsgs;
  54. UserAssert(c >= 0);
  55. pqmsg = pml->pqmsgRead;
  56. while (--c >= 0) {
  57. UserAssert(pqmsg);
  58. if (c == 0) {
  59. UserAssert(pqmsg == pml->pqmsgWriteLast);
  60. }
  61. pqmsg = pqmsg->pqmsgNext;
  62. }
  63. UserAssert(!pqmsg);
  64. /*
  65. * Check that there aren't loops in the Prev list.
  66. */
  67. c = pml->cMsgs;
  68. pqmsg = pml->pqmsgWriteLast;
  69. while (--c >= 0) {
  70. UserAssert(pqmsg);
  71. if (c == 0) {
  72. UserAssert(pqmsg == pml->pqmsgRead);
  73. }
  74. pqmsg = pqmsg->pqmsgPrev;
  75. }
  76. UserAssert(!pqmsg);
  77. }
  78. VOID DebugValidateMLISTandQMSG(
  79. PMLIST pml,
  80. PQMSG pqmsg)
  81. {
  82. PQMSG pqmsgT;
  83. DebugValidateMLIST(pml);
  84. for (pqmsgT = pml->pqmsgRead; pqmsgT; pqmsgT = pqmsgT->pqmsgNext) {
  85. if (pqmsgT == pqmsg) {
  86. return;
  87. }
  88. }
  89. UserAssert(pqmsgT == pqmsg);
  90. }
  91. #else
  92. #define DebugValidateMLIST(pml)
  93. #define DebugValidateMLISTandQMSG(pml, pqmsg)
  94. #endif
  95. VOID
  96. _AllowForegroundActivation(
  97. VOID)
  98. {
  99. SET_PUDF(PUDF_ALLOWFOREGROUNDACTIVATE);
  100. TAGMSG0(DBGTAG_FOREGROUND, "AllowSetForegroundWindows set PUDF.");
  101. }
  102. /***************************************************************************\
  103. * xxxSetProcessInitState
  104. *
  105. * Set process initialization state. What state is set depends
  106. * on whether another process is waiting on this process.
  107. *
  108. * 04-02-95 JimA Created.
  109. \***************************************************************************/
  110. BOOL xxxSetProcessInitState(
  111. PEPROCESS Process,
  112. DWORD dwFlags)
  113. {
  114. PW32PROCESS W32Process;
  115. NTSTATUS Status;
  116. CheckCritIn();
  117. UserAssert(IsWinEventNotifyDeferredOK());
  118. /*
  119. * If the W32Process structure has not been allocated, do it now.
  120. */
  121. W32Process = (PW32PROCESS)PsGetProcessWin32Process(Process);
  122. if (W32Process == NULL) {
  123. Status = AllocateW32Process(Process);
  124. if (!NT_SUCCESS(Status)) {
  125. return FALSE;
  126. }
  127. W32Process = (PW32PROCESS)PsGetProcessWin32Process(Process);
  128. #if DBG
  129. /*
  130. * The above AllocateW32Process(Process, FALSE) won't set the
  131. * W32PF_PROCESSCONNECTED flag (and if it wasn't previously set),
  132. * make sure we're not on the gppiStarting list, because if we are,
  133. * we will not be removed without the W32PF_PROCESSCONNECTED bit.
  134. */
  135. if ((W32Process->W32PF_Flags & W32PF_PROCESSCONNECTED) == 0) {
  136. UserAssert((W32Process->W32PF_Flags & W32PF_APPSTARTING) == 0);
  137. }
  138. #endif
  139. }
  140. /*
  141. * Defer WinEvent notifications, because the thread isn't initialized yet.
  142. */
  143. DeferWinEventNotify();
  144. if (dwFlags == 0) {
  145. if (!(W32Process->W32PF_Flags & W32PF_WOW)) {
  146. /*
  147. * Check to see if the startglass is on, and if so turn it off and update.
  148. */
  149. if (W32Process->W32PF_Flags & W32PF_STARTGLASS) {
  150. W32Process->W32PF_Flags &= ~W32PF_STARTGLASS;
  151. zzzCalcStartCursorHide(NULL, 0);
  152. }
  153. /*
  154. * Found it. Set the console bit and reset the wait event so any sleepers
  155. * wake up.
  156. */
  157. W32Process->W32PF_Flags |= W32PF_CONSOLEAPPLICATION;
  158. SET_PSEUDO_EVENT(&W32Process->InputIdleEvent);
  159. }
  160. } else if (!(W32Process->W32PF_Flags & W32PF_INITIALIZED)) {
  161. W32Process->W32PF_Flags |= W32PF_INITIALIZED;
  162. /*
  163. * Set global state to allow the new process to become
  164. * foreground. xxxInitProcessInfo() will set
  165. * W32PF_ALLOWFOREGROUNDACTIVATE when the process initializes.
  166. */
  167. SET_PUDF(PUDF_ALLOWFOREGROUNDACTIVATE);
  168. TAGMSG1(DBGTAG_FOREGROUND, "xxxSetProcessInitState set PUDF. %#p", W32Process);
  169. /*
  170. * If this is the win32 server process, force off start glass feedback
  171. */
  172. if (Process == gpepCSRSS) {
  173. dwFlags |= STARTF_FORCEOFFFEEDBACK;
  174. }
  175. /*
  176. * Show the app start cursor for 2 seconds if it was requested from
  177. * the application.
  178. */
  179. if (dwFlags & STARTF_FORCEOFFFEEDBACK) {
  180. W32Process->W32PF_Flags |= W32PF_FORCEOFFFEEDBACK;
  181. zzzCalcStartCursorHide(NULL, 0);
  182. } else if (dwFlags & STARTF_FORCEONFEEDBACK) {
  183. zzzCalcStartCursorHide(W32Process, 2000);
  184. }
  185. }
  186. /*
  187. * Have to defer without processing, because we don't have a ptiCurrent yet
  188. */
  189. EndDeferWinEventNotifyWithoutProcessing();
  190. return TRUE;
  191. }
  192. /***************************************************************************\
  193. * CheckAllowForeground
  194. *
  195. * Bug 273518 - joejo
  196. *
  197. * Removed this loop from xxxInitProcessInfo to allow code shareing between
  198. * that function and xxxUserNotifyConsoleApplication. This will allow console
  199. * windows to set foreground correctly on new process' it launches, as opposed
  200. * it just forcing foreground.
  201. \***************************************************************************/
  202. BOOL CheckAllowForeground(
  203. PEPROCESS pep)
  204. {
  205. BOOL fCreator = TRUE;
  206. HANDLE hpid = PsGetProcessInheritedFromUniqueProcessId(pep);
  207. LUID luid;
  208. PACCESS_TOKEN pat;
  209. PEPROCESS pepParent;
  210. PPROCESSINFO ppiParent;
  211. UINT uAncestors = 0;
  212. BOOL fAllowForeground = FALSE;
  213. NTSTATUS Status;
  214. do {
  215. /*
  216. * Get the ppi for the parent process.
  217. */
  218. Status = LockProcessByClientId(hpid, &pepParent);
  219. if (!NT_SUCCESS(Status)) {
  220. /*
  221. * Bug 294193 - joejo
  222. *
  223. * If this is a process that was created after it'a creator was
  224. * destroyed, then lets attempt to give it foreground. This is a
  225. * typical scenario when a stub exe trys to create another process
  226. * in it's place.
  227. */
  228. CheckForegroundActivateRight:
  229. if (HasForegroundActivateRight(PsGetProcessInheritedFromUniqueProcessId(pep))) {
  230. fAllowForeground = TRUE;
  231. }
  232. break;
  233. }
  234. ppiParent = PpiFromProcess(pepParent);
  235. if (ppiParent == NULL) {
  236. UnlockProcess(pepParent);
  237. goto CheckForegroundActivateRight;
  238. }
  239. /*
  240. * If we're walking the parent chain,
  241. * stop when we get to the shell or to a process that
  242. * is not running on the IO winsta
  243. */
  244. if (!fCreator
  245. && (IsShellProcess(ppiParent)
  246. || ((ppiParent->rpwinsta != NULL)
  247. && (ppiParent->rpwinsta->dwWSF_Flags & WSF_NOIO)))) {
  248. UnlockProcess(pepParent);
  249. break;
  250. }
  251. fAllowForeground = CanForceForeground(ppiParent FG_HOOKLOCK_PARAM(NULL));
  252. if (!fAllowForeground) {
  253. /*
  254. * Bug 285639 - joejo
  255. *
  256. * If the first thread of the parent process has allow set foreground
  257. * than we allow the setting of the foreground.
  258. */
  259. if (ppiParent->ptiList != NULL
  260. && (ppiParent->ptiList->TIF_flags & TIF_ALLOWFOREGROUNDACTIVATE)) {
  261. fAllowForeground = TRUE;
  262. }
  263. if (!fAllowForeground) {
  264. /*
  265. * Let's try an ancestor (this might be a worker process).
  266. */
  267. hpid = PsGetProcessInheritedFromUniqueProcessId(pepParent);
  268. /*
  269. * If this is launched by a system process, let it come to
  270. * the foreground (i.e. CSRSS launching an OLE server).
  271. */
  272. if (fCreator) {
  273. fCreator = FALSE;
  274. pat = PsReferencePrimaryToken(pepParent);
  275. if (pat != NULL) {
  276. Status = SeQueryAuthenticationIdToken(pat, &luid);
  277. if (NT_SUCCESS(Status)) {
  278. fAllowForeground = RtlEqualLuid(&luid, &luidSystem);
  279. /*
  280. * If it is a system process, give it the
  281. * permanent right so we won't have to check
  282. * its luid again
  283. */
  284. if (fAllowForeground) {
  285. ppiParent->W32PF_Flags |= W32PF_ALLOWSETFOREGROUND;
  286. }
  287. }
  288. ObDereferenceObject(pat);
  289. }
  290. }
  291. }
  292. }
  293. UnlockProcess(pepParent);
  294. /*
  295. * InheritedFromUniqueProcessId cannot be quite trusted because
  296. * process ids get reused very often. So we just check few levels up
  297. */
  298. } while (!fAllowForeground && (uAncestors++ < 5));
  299. return fAllowForeground || GiveUpForeground();
  300. }
  301. /***************************************************************************\
  302. * xxxUserNotifyConsoleApplication
  303. *
  304. * This is called by the console init code - it tells us that the starting
  305. * application is a console application. We want to know this for various
  306. * reasons, one being that WinExec() doesn't wait on a starting console
  307. * application.
  308. *
  309. * 09-18-91 ScottLu Created.
  310. * 01-12-99 JoeJo Bug 273518
  311. \***************************************************************************/
  312. VOID xxxUserNotifyConsoleApplication(
  313. PCONSOLE_PROCESS_INFO pcpi)
  314. {
  315. NTSTATUS Status;
  316. PEPROCESS Process;
  317. BOOL retval;
  318. /*
  319. * First search for this process in our process information list.
  320. */
  321. Status = LockProcessByClientId(LongToHandle(pcpi->dwProcessID), &Process);
  322. if (!NT_SUCCESS(Status)) {
  323. RIPMSGF2(RIP_WARNING,
  324. "Failed with Process ID == 0x%x, Status = 0x%x",
  325. pcpi->dwProcessID,
  326. Status);
  327. return;
  328. }
  329. retval = xxxSetProcessInitState(Process, 0);
  330. /*
  331. * Bug 273518 - joejo
  332. *
  333. * This will allow console windows to set foreground correctly on new
  334. * process' it launches, as opposed it just forcing foreground.
  335. */
  336. if (retval) {
  337. if (pcpi->dwFlags & CPI_NEWPROCESSWINDOW) {
  338. PPROCESSINFO ppiCurrent = PpiCurrent();
  339. if (CheckAllowForeground(Process)) {
  340. if (!(ppiCurrent->W32PF_Flags & W32PF_APPSTARTING)) {
  341. SetAppStarting(ppiCurrent);
  342. }
  343. SET_PUDF(PUDF_ALLOWFOREGROUNDACTIVATE);
  344. TAGMSG0(DBGTAG_FOREGROUND, "xxxUserNotifyConsoleApplication set PUDF");
  345. ppiCurrent->W32PF_Flags |= W32PF_ALLOWFOREGROUNDACTIVATE;
  346. }
  347. TAGMSG3(DBGTAG_FOREGROUND, "xxxUserNotifyConsoleApplication %s W32PF %#p-%#p",
  348. ((ppiCurrent->W32PF_Flags & W32PF_ALLOWFOREGROUNDACTIVATE) ? "set" : "NOT"),
  349. ppiCurrent, PpiFromProcess(Process));
  350. }
  351. } else {
  352. RIPMSG1(RIP_WARNING, "xxxUserNotifyConsoleApplication - SetProcessInitState failed on %#p", Process);
  353. }
  354. UnlockProcess(Process);
  355. }
  356. /***************************************************************************\
  357. * UserSetConsoleProcessWindowStation
  358. *
  359. * This is called by the console init code - it tells us that the starting
  360. * application is a console application and which window station they are
  361. * associated with. The window station pointer is stored in the EPROCESS for
  362. * the Global atom calls to find the correct global atom table when called from
  363. * a console application
  364. *
  365. \***************************************************************************/
  366. VOID UserSetConsoleProcessWindowStation(
  367. DWORD idProcess,
  368. HWINSTA hwinsta)
  369. {
  370. NTSTATUS Status;
  371. PEPROCESS Process;
  372. /*
  373. * First search for this process in our process information list.
  374. */
  375. Status = LockProcessByClientId(LongToHandle(idProcess), &Process);
  376. if (!NT_SUCCESS(Status)) {
  377. RIPMSGF2(RIP_WARNING, "Failed with Process ID == 0x%x, Status = 0x%x",
  378. idProcess,
  379. Status);
  380. return;
  381. }
  382. PsSetProcessWindowStation(Process, hwinsta);
  383. UnlockProcess(Process);
  384. }
  385. /***************************************************************************\
  386. * xxxUserNotifyProcessCreate
  387. *
  388. * This is a special notification that we get from the base while process data
  389. * structures are being created, but before the process has started. We use
  390. * this notification for startup synchronization matters (winexec, startup
  391. * activation, type ahead, etc).
  392. *
  393. * This notification is called on the server thread for the client thread
  394. * starting the process.
  395. *
  396. * 09-09-91 ScottLu Created.
  397. \***************************************************************************/
  398. BOOL xxxUserNotifyProcessCreate(
  399. DWORD idProcess,
  400. DWORD idParentThread,
  401. ULONG_PTR dwData,
  402. DWORD dwFlags)
  403. {
  404. PEPROCESS Process;
  405. PETHREAD Thread;
  406. PTHREADINFO pti;
  407. NTSTATUS Status;
  408. BOOL retval;
  409. CheckCritIn();
  410. GiveForegroundActivateRight(LongToHandle(idProcess));
  411. /*
  412. * 0x1 bit means give feedback (app start cursor).
  413. * 0x2 bit means this is a gui app (meaning, call CreateProcessInfo()
  414. * so we get app start synchronization (WaitForInputIdle()).
  415. * 0x8 bit means this process is a WOW process, set W32PF_WOW. 0x1
  416. * and 0x2 bits will also be set.
  417. * 0x4 value means this is really a shared WOW task starting
  418. */
  419. /*
  420. * If we want feedback, we need to create a process info structure,
  421. * so do it: it will be properly cleaned up.
  422. */
  423. if ((dwFlags & 0xb) != 0) {
  424. Status = LockProcessByClientId(LongToHandle(idProcess), &Process);
  425. if (!NT_SUCCESS(Status)) {
  426. RIPMSGF2(RIP_WARNING,
  427. "Failed with Process ID == 0x%x, Status = 0x%x",
  428. idProcess,
  429. Status);
  430. return FALSE;
  431. }
  432. retval = xxxSetProcessInitState(Process, ((dwFlags & 1) ? STARTF_FORCEONFEEDBACK : STARTF_FORCEOFFFEEDBACK));
  433. if (!retval) {
  434. RIPMSG1(RIP_WARNING, "xxxUserNotifyProcessCreate - SetProcessInitState failed on %#p", Process);
  435. }
  436. if (dwFlags & 0x8) {
  437. PPROCESSINFO ppi;
  438. ppi = PsGetProcessWin32Process(Process);
  439. if (ppi != NULL)
  440. ppi->W32PF_Flags |= W32PF_WOW;
  441. }
  442. UnlockProcess(Process);
  443. /*
  444. * Find out who is starting this app. If it is a 16 bit app, allow
  445. * it to bring itself back to the foreground if it calls
  446. * SetActiveWindow() or SetFocus(). This is because this could be
  447. * related to OLE to DDE activation. Notes has a case where after it
  448. * lauches pbrush to edit an embedded bitmap, it brings up a message
  449. * box on top if the bitmap is read only. This message box won't appear
  450. * foreground unless we allow it to. This usually isn't a problem
  451. * because most apps don't bring up windows on top of editors
  452. * like this. 32 bit apps will call SetForegroundWindow().
  453. */
  454. Status = LockThreadByClientId(LongToHandle(idParentThread), &Thread);
  455. if (!NT_SUCCESS(Status)) {
  456. RIPMSGF2(RIP_WARNING,
  457. "Failed with Thread ID == 0x%x, Status = 0x%x",
  458. idParentThread,
  459. Status);
  460. return FALSE;
  461. }
  462. pti = PtiFromThread(Thread);
  463. if (pti && (pti->TIF_flags & TIF_16BIT)) {
  464. pti->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE;
  465. TAGMSG1(DBGTAG_FOREGROUND,
  466. "xxxUserNotifyProcessCreate set TIF on pti 0x%p",
  467. pti);
  468. }
  469. UnlockThread(Thread);
  470. } else if (dwFlags == 4) {
  471. /*
  472. * A WOW task is starting up. Create the WOW per thread info
  473. * structure here in case someone calls WaitForInputIdle
  474. * before the thread is created.
  475. */
  476. PWOWTHREADINFO pwti;
  477. /*
  478. * Look for a matching thread in the WOW thread info list.
  479. */
  480. for (pwti = gpwtiFirst; pwti != NULL; pwti = pwti->pwtiNext) {
  481. if (pwti->idTask == idProcess) {
  482. break;
  483. }
  484. }
  485. /*
  486. * If we didn't find one, allocate a new one and add it to
  487. * the head of the list.
  488. */
  489. if (pwti == NULL) {
  490. pwti = (PWOWTHREADINFO)UserAllocPoolWithQuota(
  491. sizeof(WOWTHREADINFO), TAG_WOWTHREADINFO);
  492. if (pwti == NULL) {
  493. return FALSE;
  494. }
  495. INIT_PSEUDO_EVENT(&pwti->pIdleEvent);
  496. pwti->idTask = idProcess;
  497. pwti->pwtiNext = gpwtiFirst;
  498. gpwtiFirst = pwti;
  499. } else {
  500. RESET_PSEUDO_EVENT(&pwti->pIdleEvent);
  501. }
  502. pwti->idWaitObject = dwData;
  503. Status = LockThreadByClientId(LongToHandle(idParentThread), &Thread);
  504. if (!NT_SUCCESS(Status)) {
  505. RIPMSGF2(RIP_WARNING,
  506. "Failed with Thread ID == 0x%x, Status = 0x%x",
  507. idParentThread,
  508. Status);
  509. return FALSE;
  510. }
  511. pwti->idParentProcess = HandleToUlong(PsGetThreadProcessId(Thread));
  512. UnlockThread(Thread);
  513. }
  514. return TRUE;
  515. }
  516. /***************************************************************************\
  517. * zzzCalcStartCursorHide
  518. *
  519. * Calculates when to hide the startup cursor.
  520. *
  521. * 05-14-92 ScottLu Created.
  522. \***************************************************************************/
  523. VOID zzzCalcStartCursorHide(
  524. PW32PROCESS pwp,
  525. DWORD timeAdd)
  526. {
  527. DWORD timeNow = NtGetTickCount();
  528. PW32PROCESS pwpT;
  529. PW32PROCESS *ppwpT;
  530. if (pwp != NULL) {
  531. /*
  532. * We were passed in a timeout. Recalculate when we timeout and add
  533. * the pwp to the starting list.
  534. */
  535. if (!(pwp->W32PF_Flags & W32PF_STARTGLASS)) {
  536. /*
  537. * Add it to the list only if it is not already in the list.
  538. */
  539. for (pwpT = gpwpCalcFirst; pwpT != NULL; pwpT = pwpT->NextStart) {
  540. if (pwpT == pwp) {
  541. break;
  542. }
  543. }
  544. if (pwpT != pwp) {
  545. pwp->NextStart = gpwpCalcFirst;
  546. gpwpCalcFirst = pwp;
  547. }
  548. }
  549. pwp->StartCursorHideTime = timeAdd + timeNow;
  550. pwp->W32PF_Flags |= W32PF_STARTGLASS;
  551. }
  552. gtimeStartCursorHide = 0;
  553. for (ppwpT = &gpwpCalcFirst; (pwpT = *ppwpT) != NULL;) {
  554. /*
  555. * If the app isn't starting or feedback is forced off, remove
  556. * it from the list so we don't look at it again.
  557. */
  558. if (!(pwpT->W32PF_Flags & W32PF_STARTGLASS) ||
  559. (pwpT->W32PF_Flags & W32PF_FORCEOFFFEEDBACK)) {
  560. *ppwpT = pwpT->NextStart;
  561. continue;
  562. }
  563. /*
  564. * Find the greatest hide cursor timeout value.
  565. */
  566. if (gtimeStartCursorHide < pwpT->StartCursorHideTime) {
  567. gtimeStartCursorHide = pwpT->StartCursorHideTime;
  568. }
  569. /*
  570. * If this app has timed out, it isn't starting anymore! Remove it
  571. * from the list.
  572. */
  573. if (ComputeTickDelta(timeNow, pwpT->StartCursorHideTime) > 0) {
  574. pwpT->W32PF_Flags &= ~W32PF_STARTGLASS;
  575. *ppwpT = pwpT->NextStart;
  576. continue;
  577. }
  578. /*
  579. * Step to the next pwp in the list.
  580. */
  581. ppwpT = &pwpT->NextStart;
  582. }
  583. /*
  584. * If the hide time is still less than the current time, then turn off
  585. * the app starting cursor.
  586. */
  587. if (gtimeStartCursorHide <= timeNow) {
  588. gtimeStartCursorHide = 0;
  589. }
  590. /*
  591. * Update the cursor image with the new info (doesn't do anything unless
  592. * the cursor is really changing).
  593. */
  594. zzzUpdateCursorImage();
  595. }
  596. #define QUERY_VALUE_BUFFER 80
  597. /*
  598. * Install hack.
  599. *
  600. * We have a hack inherited from Chicago that allows the shell to
  601. * clean up registry information after a setup program runs. A
  602. * setup program is defined as an app with one of a list of names.
  603. */
  604. PUNICODE_STRING gpastrSetupExe; // These are initialized in the routine
  605. int giSetupExe; // CreateSetupNameArray in setup.c
  606. /***************************************************************************\
  607. * SetAppImeCompatFlags - NOTE pstrModName->Buffer must be zero terminated.
  608. *
  609. *
  610. * History:
  611. * 07-17-97 DaveHart Split from SetAppCompatFlags -- misleadingly it also
  612. * returns a BOOL indicating whether the filename is
  613. * recognized as a setup program. Used by SetAppCompatFlags
  614. * for 32-bit apps and zzzInitTask for 16-bit ones.
  615. \***************************************************************************/
  616. BOOL SetAppImeCompatFlags(
  617. PTHREADINFO pti,
  618. PUNICODE_STRING pstrModName,
  619. PUNICODE_STRING pstrBaseFileName)
  620. {
  621. DWORD dwImeFlags = 0;
  622. WCHAR szHex[QUERY_VALUE_BUFFER];
  623. WORD wPrimaryLangID;
  624. LCID lcid;
  625. int iSetup;
  626. BOOL fSetup = FALSE;
  627. int iAppName;
  628. int cAppNames;
  629. PUNICODE_STRING rgpstrAppNames[2];
  630. UNICODE_STRING strHex;
  631. /*
  632. * Because can't access pClientInfo of another process
  633. */
  634. UserAssert(pti->ppi == PpiCurrent());
  635. /*
  636. * Because it is used as a zero-terminated profile key name.
  637. */
  638. UserAssert(pstrModName->Buffer[pstrModName->Length / sizeof(WCHAR)] == 0);
  639. if (FastGetProfileStringW(
  640. NULL,
  641. PMAP_IMECOMPAT,
  642. pstrModName->Buffer,
  643. NULL,
  644. szHex,
  645. ARRAY_SIZE(szHex),
  646. 0)) {
  647. /*
  648. * Found some flags. Attempt to convert the hex string
  649. * into numeric value. Specify base 0, so
  650. * RtlUnicodeStringToInteger will handle the 0x format.
  651. */
  652. RtlInitUnicodeString(&strHex, szHex);
  653. RtlUnicodeStringToInteger(&strHex, 0, (PULONG)&dwImeFlags);
  654. }
  655. /*
  656. * If current layout is not IME layout, we don't need to get
  657. * compatible flags for IME. But now, we don't have any scheme
  658. * to get this flags when the keyboard layout is switched. Then
  659. * we get it here, even this flags are not nessesary for non-IME
  660. * keyboard layouts.
  661. */
  662. ZwQueryDefaultLocale(FALSE, &lcid);
  663. wPrimaryLangID = PRIMARYLANGID(lcid);
  664. if ((wPrimaryLangID == LANG_KOREAN || wPrimaryLangID == LANG_JAPANESE) &&
  665. (LOWORD(pti->dwExpWinVer) <= VER31)) {
  666. /*
  667. * IME compatibility flags are needed even if it's a 32 bit app.
  668. */
  669. pti->ppi->dwImeCompatFlags = dwImeFlags;
  670. } else {
  671. pti->ppi->dwImeCompatFlags = dwImeFlags & (IMECOMPAT_NOFINALIZECOMPSTR | IMECOMPAT_HYDRACLIENT);
  672. if (dwImeFlags & IMECOMPAT_NOFINALIZECOMPSTR) {
  673. RIPMSG1(RIP_WARNING, "IMECOMPAT_NOFINALIZECOMPSTR is set to ppi=%#p", pti->ppi);
  674. }
  675. if (dwImeFlags & IMECOMPAT_HYDRACLIENT) {
  676. RIPMSG1(RIP_WARNING, "IMECOMPAT_HYDRACLIENT is set to ppi=%#p", pti->ppi);
  677. }
  678. }
  679. if (gpastrSetupExe == NULL) {
  680. return fSetup;
  681. }
  682. rgpstrAppNames[0] = pstrModName;
  683. cAppNames = 1;
  684. if (pstrBaseFileName) {
  685. rgpstrAppNames[1] = pstrBaseFileName;
  686. cAppNames = 2;
  687. }
  688. for (iAppName = 0; iAppName < cAppNames && !fSetup; iAppName++) {
  689. iSetup = 0;
  690. while (iSetup < giSetupExe) {
  691. if (RtlCompareUnicodeString(rgpstrAppNames[iAppName], &(gpastrSetupExe[iSetup]), TRUE) == 0) {
  692. fSetup = TRUE;
  693. break;
  694. }
  695. iSetup++;
  696. }
  697. }
  698. return fSetup;
  699. }
  700. /***************************************************************************\
  701. * SetAppCompatFlags
  702. *
  703. *
  704. * History:
  705. * 03-23-92 JimA Created.
  706. * 07-17-97 FritzS add return for fSetup -- returns TRUE if app is a setup app.
  707. * 09-03-97 DaveHart Split out IME, WOW doesn't use this function anymore.
  708. * 07-14-98 MCostea Add Compatibility2 flags
  709. * 01-21-99 MCostea Add DesiredOSVersion
  710. \***************************************************************************/
  711. BOOL SetAppCompatFlags(
  712. PTHREADINFO pti)
  713. {
  714. DWORD dwFlags = 0;
  715. DWORD dwFlags2 = 0;
  716. WCHAR szHex[QUERY_VALUE_BUFFER];
  717. WCHAR szKey[90];
  718. WCHAR *pchStart, *pchEnd;
  719. DWORD cb;
  720. PUNICODE_STRING pstrAppName;
  721. UNICODE_STRING strKey;
  722. UNICODE_STRING strImageName;
  723. /*
  724. * Because can't access pClientInfo of another process.
  725. */
  726. UserAssert(pti->ppi == PpiCurrent());
  727. UserAssert(pti->ppi->ptiList);
  728. UserAssert(!(pti->TIF_flags & TIF_16BIT));
  729. /*
  730. * We assume here that pti was just inserted in at the head of ptiList
  731. */
  732. UserAssert(pti == pti->ppi->ptiList);
  733. try {
  734. PPEB ppeb = PsGetProcessPeb(PsGetThreadProcess(pti->pEThread));
  735. struct _RTL_USER_PROCESS_PARAMETERS *ProcessParameters;
  736. if (pti->ptiSibling) {
  737. pti->pClientInfo->dwCompatFlags = pti->dwCompatFlags = pti->ptiSibling->dwCompatFlags;
  738. pti->pClientInfo->dwCompatFlags2 = pti->dwCompatFlags2 = pti->ptiSibling->dwCompatFlags2;
  739. return FALSE;
  740. }
  741. /*
  742. * Find end of app name
  743. */
  744. if (pti->pstrAppName != NULL) {
  745. pstrAppName = pti->pstrAppName;
  746. } else {
  747. ProbeForRead(ppeb, sizeof(PEB), sizeof(BYTE));
  748. ProcessParameters = ppeb->ProcessParameters;
  749. ProbeForRead(ProcessParameters, sizeof(*ProcessParameters), sizeof(BYTE));
  750. strImageName = ProbeAndReadUnicodeString(&ProcessParameters->ImagePathName);
  751. ProbeForReadUnicodeStringBuffer(strImageName);
  752. pstrAppName = &strImageName;
  753. }
  754. pchStart = pchEnd = pstrAppName->Buffer +
  755. (pstrAppName->Length / sizeof(WCHAR));
  756. /*
  757. * Locate start of extension
  758. */
  759. while (TRUE) {
  760. if (pchEnd == pstrAppName->Buffer) {
  761. pchEnd = pchStart;
  762. break;
  763. }
  764. if (*pchEnd == TEXT('.')) {
  765. break;
  766. }
  767. pchEnd--;
  768. }
  769. /*
  770. * Locate start of filename
  771. */
  772. pchStart = pchEnd;
  773. while (pchStart != pstrAppName->Buffer) {
  774. if (*pchStart == TEXT('\\') || *pchStart == TEXT(':')) {
  775. pchStart++;
  776. break;
  777. }
  778. pchStart--;
  779. }
  780. #define MODULESUFFIXSIZE (8 * sizeof(WCHAR))
  781. #define MAXMODULENAMELEN (sizeof(szKey) - MODULESUFFIXSIZE)
  782. /*
  783. * Get a copy of the filename
  784. * Allow extra spaces for the 'ImageSubsystemMajorVersionMinorVersion'
  785. * i.e. 3.5 that will get appended at the end of the module name
  786. */
  787. cb = (DWORD)(pchEnd - pchStart) * sizeof(WCHAR);
  788. if (cb >= MAXMODULENAMELEN)
  789. cb = MAXMODULENAMELEN - sizeof(WCHAR);
  790. RtlCopyMemory(szKey, pchStart, cb);
  791. /*
  792. * Get the compat2 flags from the PEB. The appcompat infrastructure
  793. * gets the flags from the shim database.
  794. */
  795. pti->dwCompatFlags2 = ppeb->AppCompatFlagsUser.LowPart;
  796. pti->pClientInfo->dwCompatFlags2 = pti->dwCompatFlags2;
  797. } except (W32ExceptionHandler(FALSE, RIP_ERROR)) {
  798. return FALSE;
  799. }
  800. szKey[(cb / sizeof(WCHAR))] = 0;
  801. #undef MAXMODULENAMELEN
  802. if (FastGetProfileStringW(
  803. NULL,
  804. PMAP_COMPAT32,
  805. szKey,
  806. NULL,
  807. szHex,
  808. ARRAY_SIZE(szHex),
  809. 0)) {
  810. UNICODE_STRING strHex;
  811. /*
  812. * Found some flags. Attempt to convert the hex string
  813. * into numeric value. Specify base 0, so
  814. * RtlUnicodeStringToInteger will handle the 0x format
  815. */
  816. RtlInitUnicodeString(&strHex, szHex);
  817. RtlUnicodeStringToInteger(&strHex, 0, (PULONG)&dwFlags);
  818. }
  819. try {
  820. pti->pClientInfo->dwCompatFlags = dwFlags;
  821. } except (W32ExceptionHandler(FALSE, RIP_ERROR)) {
  822. return FALSE;
  823. }
  824. pti->dwCompatFlags = dwFlags;
  825. /*
  826. * Restore the string
  827. */
  828. szKey[(cb / sizeof(WCHAR))] = 0;
  829. RtlInitUnicodeString(&strKey, szKey);
  830. return SetAppImeCompatFlags(pti, &strKey, NULL);
  831. }
  832. /***************************************************************************\
  833. * GetAppCompatFlags
  834. *
  835. * Compatibility flags for < Win 3.1 apps running on 3.1
  836. *
  837. * History:
  838. * 04-??-92 ScottLu Created.
  839. * 05-04-92 DarrinM Moved to USERRTL.DLL.
  840. \***************************************************************************/
  841. DWORD GetAppCompatFlags(
  842. PTHREADINFO pti)
  843. {
  844. /*
  845. * GRE calls this with pti == NULL.
  846. */
  847. if (pti == NULL) {
  848. pti = PtiCurrentShared();
  849. }
  850. return pti->dwCompatFlags;
  851. }
  852. /***************************************************************************\
  853. * GetAppCompatFlags2
  854. *
  855. * Compatibility flags for < wVer apps
  856. *
  857. * History:
  858. * 07-01-98 MCostea Created.
  859. \***************************************************************************/
  860. DWORD GetAppCompatFlags2(
  861. WORD wVer)
  862. {
  863. return GetAppCompatFlags2ForPti(PtiCurrentShared(), wVer);
  864. }
  865. DWORD GetAppImeCompatFlags(
  866. PTHREADINFO pti)
  867. {
  868. if (pti == NULL) {
  869. pti = PtiCurrentShared();
  870. }
  871. return pti->ppi->dwImeCompatFlags;
  872. }
  873. /***************************************************************************\
  874. * CheckAppStarting
  875. *
  876. * This is a timer proc (see SetAppStarting) which removes ppi's from the
  877. * starting list once their initialization time has expired.
  878. *
  879. * History:
  880. * 08/26/97 GerardoB Created
  881. \***************************************************************************/
  882. VOID CheckAppStarting(
  883. PWND pwnd,
  884. UINT message,
  885. UINT_PTR nID,
  886. LPARAM lParam)
  887. {
  888. LARGE_INTEGER liStartingTimeout;
  889. PPROCESSINFO *pppi = &gppiStarting;
  890. KeQuerySystemTime(&liStartingTimeout); /* 1 unit == 100ns */
  891. liStartingTimeout.QuadPart -= (LONGLONG)(CMSAPPSTARTINGTIMEOUT * 10000);
  892. while (*pppi != NULL) {
  893. if (liStartingTimeout.QuadPart > PsGetProcessCreateTimeQuadPart((*pppi)->Process)) {
  894. (*pppi)->W32PF_Flags &= ~(W32PF_APPSTARTING | W32PF_ALLOWFOREGROUNDACTIVATE);
  895. TAGMSG1(DBGTAG_FOREGROUND, "CheckAppStarting clear W32PF %#p", *pppi);
  896. *pppi = (*pppi)->ppiNext;
  897. } else {
  898. pppi = &(*pppi)->ppiNext;
  899. }
  900. }
  901. TAGMSG0(DBGTAG_FOREGROUND, "Removing all entries from ghCanActivateForegroundPIDs array");
  902. RtlZeroMemory(ghCanActivateForegroundPIDs, sizeof(ghCanActivateForegroundPIDs));
  903. UNREFERENCED_PARAMETER(pwnd);
  904. UNREFERENCED_PARAMETER(message);
  905. UNREFERENCED_PARAMETER(nID);
  906. UNREFERENCED_PARAMETER(lParam);
  907. }
  908. /***************************************************************************\
  909. * SetAppStarting
  910. *
  911. * Add a process to the starting list and mark it as such. The process will
  912. * remain in the list until it activates a window, our timer goes off or the
  913. * process goes away, whichever happens first.
  914. *
  915. * History:
  916. * 08/26/97 GerardoB Created
  917. \***************************************************************************/
  918. VOID SetAppStarting(
  919. PPROCESSINFO ppi)
  920. {
  921. static UINT_PTR guAppStartingId = 0;
  922. /*
  923. * This ppi had better not be in the list already, or we will be creating
  924. * a loop (as seen in stress).
  925. */
  926. UserAssert((ppi->W32PF_Flags & W32PF_APPSTARTING) == 0);
  927. /*
  928. * If we add this to the gppiStartingList without this bit set, we will
  929. * skip removing it from the list in DestroyProcessInfo(), but continue
  930. * to free it in FreeW32Process called by W32pProcessCallout.
  931. */
  932. UserAssert((ppi->W32PF_Flags & W32PF_PROCESSCONNECTED));
  933. ppi->W32PF_Flags |= W32PF_APPSTARTING;
  934. ppi->ppiNext = gppiStarting;
  935. gppiStarting = ppi;
  936. /*
  937. * Some system processes are initialized before the RIT has setup the master
  938. * timer, so check for it.
  939. */
  940. if (gptmrMaster != NULL) {
  941. guAppStartingId = InternalSetTimer(NULL, guAppStartingId,
  942. CMSAPPSTARTINGTIMEOUT + CMSHUNGAPPTIMEOUT,
  943. CheckAppStarting, TMRF_RIT | TMRF_ONESHOT);
  944. }
  945. }
  946. /***************************************************************************\
  947. * ClearAppStarting
  948. *
  949. * Remove a process from the app starting list and clear the W32PF_APPSTARTING
  950. * flag. No major action here, just a centralized place to take care of this.
  951. *
  952. * History:
  953. * 08/26/97 GerardoB Created
  954. \***************************************************************************/
  955. VOID ClearAppStarting(
  956. PPROCESSINFO ppi)
  957. {
  958. REMOVE_FROM_LIST(PROCESSINFO, gppiStarting, ppi, ppiNext);
  959. ppi->W32PF_Flags &= ~W32PF_APPSTARTING;
  960. }
  961. /***************************************************************************\
  962. * zzzInitTask -- called by WOW startup for each app
  963. *
  964. *
  965. * History:
  966. * 02-21-91 MikeHar Created.
  967. * 02-23-92 MattFe Altered for WOW
  968. * 09-03-97 DaveHart WOW supplies compat flags, we tell it about setup apps.
  969. \***************************************************************************/
  970. NTSTATUS zzzInitTask(
  971. UINT dwExpWinVer,
  972. DWORD dwAppCompatFlags,
  973. DWORD dwUserWOWCompatFlags,
  974. PUNICODE_STRING pstrModName,
  975. PUNICODE_STRING pstrBaseFileName,
  976. DWORD hTaskWow,
  977. DWORD dwHotkey,
  978. DWORD idTask,
  979. DWORD dwX,
  980. DWORD dwY,
  981. DWORD dwXSize,
  982. DWORD dwYSize)
  983. {
  984. PTHREADINFO ptiCurrent;
  985. PTDB ptdb;
  986. PPROCESSINFO ppi;
  987. PWOWTHREADINFO pwti;
  988. ULONG ProcessInfo;
  989. NTSTATUS Status;
  990. ptiCurrent = PtiCurrent();
  991. ppi = ptiCurrent->ppi;
  992. /*
  993. * Set the real name of the module. (Instead of 'NTVDM')
  994. * We've already probed pstrModName->Buffer for Length+sizeof(WCHAR) so
  995. * we can copy the UNICODE_NULL terminator as well.
  996. */
  997. if (ptiCurrent->pstrAppName != NULL) {
  998. UserFreePool(ptiCurrent->pstrAppName);
  999. }
  1000. ptiCurrent->pstrAppName = NULL;
  1001. //
  1002. // Check the Target Process to see if this is a Wx86 process
  1003. //
  1004. if (ptiCurrent->ptdb) {
  1005. /*
  1006. * Shouldn't be called more than once on a thread.
  1007. */
  1008. return STATUS_ACCESS_DENIED;
  1009. }
  1010. Status = ZwQueryInformationProcess(NtCurrentProcess(),
  1011. ProcessWx86Information,
  1012. &ProcessInfo,
  1013. sizeof(ProcessInfo),
  1014. NULL);
  1015. if (!NT_SUCCESS(Status) || ProcessInfo == 0) {
  1016. return STATUS_ACCESS_DENIED;
  1017. }
  1018. ptiCurrent->pstrAppName = UserAllocPoolWithQuota(sizeof(UNICODE_STRING) +
  1019. pstrModName->Length + sizeof(WCHAR), TAG_TEXT);
  1020. if (ptiCurrent->pstrAppName != NULL) {
  1021. ptiCurrent->pstrAppName->Buffer = (PWCHAR)(ptiCurrent->pstrAppName + 1);
  1022. try {
  1023. RtlCopyMemory(ptiCurrent->pstrAppName->Buffer, pstrModName->Buffer,
  1024. pstrModName->Length);
  1025. ptiCurrent->pstrAppName->Buffer[pstrModName->Length / sizeof(WCHAR)] = 0;
  1026. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  1027. UserFreePool(ptiCurrent->pstrAppName);
  1028. ptiCurrent->pstrAppName = NULL;
  1029. return STATUS_OBJECT_NAME_INVALID;
  1030. }
  1031. ptiCurrent->pstrAppName->MaximumLength = pstrModName->Length + sizeof(WCHAR);
  1032. ptiCurrent->pstrAppName->Length = pstrModName->Length;
  1033. } else {
  1034. return STATUS_OBJECT_NAME_INVALID;
  1035. }
  1036. /*
  1037. * An app is starting!
  1038. */
  1039. if (!(ppi->W32PF_Flags & W32PF_APPSTARTING)) {
  1040. SetAppStarting(ppi);
  1041. }
  1042. /*
  1043. * We never want to use the ShowWindow defaulting mechanism for WOW
  1044. * apps. If STARTF_USESHOWWINDOW was set in the client-side
  1045. * STARTUPINFO structure, WOW has already picked it up and used
  1046. * it for the first (command-line) app.
  1047. */
  1048. ppi->usi.dwFlags &= ~STARTF_USESHOWWINDOW;
  1049. /*
  1050. * If WOW passed us a hotkey for this app, save it for CreateWindow's use.
  1051. */
  1052. if (dwHotkey != 0) {
  1053. ppi->dwHotkey = dwHotkey;
  1054. }
  1055. /*
  1056. * If WOW passed us a non-default window position use it, otherwise clear it.
  1057. */
  1058. ppi->usi.cb = sizeof(ppi->usi);
  1059. if (dwX == CW_USEDEFAULT || dwX == CW2_USEDEFAULT) {
  1060. ppi->usi.dwFlags &= ~STARTF_USEPOSITION;
  1061. } else {
  1062. ppi->usi.dwFlags |= STARTF_USEPOSITION;
  1063. ppi->usi.dwX = dwX;
  1064. ppi->usi.dwY = dwY;
  1065. }
  1066. /*
  1067. * If WOW passed us a non-default window size use it, otherwise clear it.
  1068. */
  1069. if (dwXSize == CW_USEDEFAULT || dwXSize == CW2_USEDEFAULT) {
  1070. ppi->usi.dwFlags &= ~STARTF_USESIZE;
  1071. } else {
  1072. ppi->usi.dwFlags |= STARTF_USESIZE;
  1073. ppi->usi.dwXSize = dwXSize;
  1074. ppi->usi.dwYSize = dwYSize;
  1075. }
  1076. /*
  1077. * Alloc and Link in new task into the task list
  1078. */
  1079. if ((ptdb = (PTDB)UserAllocPoolWithQuota(sizeof(TDB), TAG_WOWTDB)) == NULL)
  1080. return STATUS_NO_MEMORY;
  1081. RtlZeroMemory(ptdb, sizeof(TDB));
  1082. ptiCurrent->ptdb = ptdb;
  1083. /*
  1084. * Save away the 16 bit task handle: we use this later when calling
  1085. * wow back to close a WOW task.
  1086. */
  1087. ptdb->hTaskWow = LOWORD(hTaskWow);
  1088. try {
  1089. ptiCurrent->pClientInfo->dwCompatFlags = dwAppCompatFlags;
  1090. UserAssert(ptiCurrent->ppi->ptiList);
  1091. dwUserWOWCompatFlags &= COMPATFLAGS2_FORWOW;
  1092. ptiCurrent->pClientInfo->dwCompatFlags2 = dwUserWOWCompatFlags;
  1093. /*
  1094. * HIWORD: != 0 if wants proportional font
  1095. * LOWORD: Expected windows version (3.00 [300], 3.10 [30A], etc)
  1096. */
  1097. ptiCurrent->pClientInfo->dwExpWinVer = dwExpWinVer;
  1098. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  1099. }
  1100. ptiCurrent->dwCompatFlags = dwAppCompatFlags;
  1101. ptiCurrent->dwCompatFlags2 = dwUserWOWCompatFlags;
  1102. ptiCurrent->dwExpWinVer = dwExpWinVer;
  1103. /*
  1104. * We haven't captured pstrBaseFileName's buffer, we
  1105. * may fault touching it in SetAppImeCompatFlags. If
  1106. * so the IME flags have been set already and we
  1107. * can safely assume it's not a setup app.
  1108. */
  1109. try {
  1110. if (SetAppImeCompatFlags(ptiCurrent, ptiCurrent->pstrAppName,
  1111. pstrBaseFileName)) {
  1112. /*
  1113. * Flag task as a setup app.
  1114. */
  1115. ptdb->TDB_Flags = TDBF_SETUP;
  1116. ppi->W32PF_Flags |= W32PF_SETUPAPP;
  1117. }
  1118. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  1119. }
  1120. /*
  1121. * Set the flags to say this is a TIF_MEOW queues.
  1122. */
  1123. if (hTaskWow & HTW_ISMEOW) {
  1124. ptiCurrent->TIF_flags |= TIF_MEOW;
  1125. }
  1126. ptiCurrent->TIF_flags |= TIF_16BIT | TIF_FIRSTIDLE;
  1127. /*
  1128. * Set the flags to say this is a 16-bit thread before attaching
  1129. * queues.
  1130. */
  1131. /*
  1132. * If this task is running in the shared WOW VDM, we handle
  1133. * WaitForInputIdle a little differently than separate WOW
  1134. * VDMs. This is because CreateProcess returns a real process
  1135. * handle when you start a separate WOW VDM, so the "normal"
  1136. * WaitForInputIdle works. For the shared WOW VDM, CreateProcess
  1137. * returns an event handle.
  1138. */
  1139. ptdb->pwti = NULL;
  1140. if (idTask) {
  1141. ptiCurrent->TIF_flags |= TIF_SHAREDWOW;
  1142. /*
  1143. * Look for a matching thread in the WOW thread info list.
  1144. */
  1145. if (idTask != (DWORD)-1) {
  1146. for (pwti = gpwtiFirst; pwti != NULL; pwti = pwti->pwtiNext) {
  1147. if (pwti->idTask == idTask) {
  1148. ptdb->pwti = pwti;
  1149. break;
  1150. }
  1151. }
  1152. #if DBG
  1153. if (pwti == NULL) {
  1154. RIPMSG0(RIP_WARNING, "InitTask couldn't find WOW struct\n");
  1155. }
  1156. #endif
  1157. }
  1158. }
  1159. try {
  1160. ptiCurrent->pClientInfo->dwTIFlags |= ptiCurrent->TIF_flags;
  1161. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  1162. }
  1163. /*
  1164. * We need this thread to share the queue of other win16 apps.
  1165. * If we're journalling, all apps are sharing a queue, so we wouldn't
  1166. * want to interrupt that - so only cause queue recalculation
  1167. * if we aren't journalling.
  1168. * ptdb may be freed by DestroyTask during a callback, so defer WinEvent
  1169. * notifications until we don't need ptdb any more.
  1170. */
  1171. DeferWinEventNotify();
  1172. if (!FJOURNALRECORD() && !FJOURNALPLAYBACK()) {
  1173. zzzReattachThreads(FALSE);
  1174. }
  1175. /*
  1176. * Setup the app start cursor for 5 second timeout.
  1177. */
  1178. zzzCalcStartCursorHide((PW32PROCESS)ppi, 5000);
  1179. /*
  1180. * Mark this guy and add him to the global task list so he can run.
  1181. */
  1182. #define NORMAL_PRIORITY_TASK 10
  1183. /*
  1184. * To be Compatible it super important that the new task run immediately
  1185. * Set its priority accordingly. No other task should ever be set to
  1186. * CREATION priority
  1187. */
  1188. ptdb->nPriority = NORMAL_PRIORITY_TASK;
  1189. ptdb->pti = ptiCurrent;
  1190. InsertTask(ppi, ptdb);
  1191. zzzEndDeferWinEventNotify();
  1192. /*
  1193. * Force this new task to be the active task (WOW will ensure the
  1194. * currently running task does a Yield which will put it into the
  1195. * non preemptive scheduler.
  1196. */
  1197. ppi->pwpi->ptiScheduled = ptiCurrent;
  1198. ppi->pwpi->CSLockCount = -1;
  1199. EnterWowCritSect(ptiCurrent, ppi->pwpi);
  1200. /*
  1201. * Ensure app gets focus.
  1202. */
  1203. zzzShowStartGlass(10000);
  1204. return STATUS_SUCCESS;
  1205. }
  1206. /***************************************************************************\
  1207. * zzzShowStartGlass
  1208. *
  1209. * This routine is called by WOW when first starting or when starting an
  1210. * additional WOW app.
  1211. *
  1212. * 12-07-92 ScottLu Created.
  1213. \***************************************************************************/
  1214. VOID zzzShowStartGlass(
  1215. DWORD dwTimeout)
  1216. {
  1217. PPROCESSINFO ppi;
  1218. /*
  1219. * If this is the first call to zzzShowStartGlass(), then the
  1220. * W32PF_ALLOWFOREGROUNDACTIVATE bit has already been set in the process
  1221. * info - we don't want to set it again because it may have been
  1222. * purposefully cleared when the user hit a key or mouse clicked.
  1223. */
  1224. ppi = PpiCurrent();
  1225. if (ppi->W32PF_Flags & W32PF_SHOWSTARTGLASSCALLED) {
  1226. /*
  1227. * Allow this wow app to come to the foreground. This'll be cancelled
  1228. * if the user mouse clicks or hits any keys.
  1229. */
  1230. SET_PUDF(PUDF_ALLOWFOREGROUNDACTIVATE);
  1231. TAGMSG0(DBGTAG_FOREGROUND, "zzzShowStartGlass set PUDF");
  1232. ppi->W32PF_Flags |= W32PF_ALLOWFOREGROUNDACTIVATE;
  1233. TAGMSG1(DBGTAG_FOREGROUND, "zzzShowStartGlass set W32PF %#p", ppi);
  1234. }
  1235. ppi->W32PF_Flags |= W32PF_SHOWSTARTGLASSCALLED;
  1236. /*
  1237. * Show the start glass cursor for this much longer.
  1238. */
  1239. zzzCalcStartCursorHide((PW32PROCESS)ppi, dwTimeout);
  1240. }
  1241. /***************************************************************************\
  1242. * GetJournallingQueue
  1243. *
  1244. * 03/21/97 GerardoB Created
  1245. \***************************************************************************/
  1246. PQ GetJournallingQueue(
  1247. PTHREADINFO pti)
  1248. {
  1249. PHOOK phook;
  1250. /*
  1251. * Fail if we cannot journal this thread.
  1252. */
  1253. if ((pti->TIF_flags & TIF_DONTJOURNALATTACH) || pti->rpdesk == NULL) {
  1254. return NULL;
  1255. }
  1256. /*
  1257. * Get the journalling hook if any.
  1258. */
  1259. phook = PhkFirstGlobalValid(pti, WH_JOURNALPLAYBACK);
  1260. if (phook == NULL) {
  1261. phook = PhkFirstGlobalValid(pti, WH_JOURNALRECORD);
  1262. }
  1263. /*
  1264. * Validate fsHooks bits.
  1265. */
  1266. UserAssert((phook == NULL)
  1267. ^ IsHooked(pti, (WHF_FROM_WH(WH_JOURNALPLAYBACK) | WHF_FROM_WH(WH_JOURNALRECORD))));
  1268. /*
  1269. * Return the queue if we found a journalling hook.
  1270. */
  1271. return ((phook == NULL) ? NULL : GETPTI(phook)->pq);
  1272. }
  1273. /***************************************************************************\
  1274. * ClearQueueServerEvent
  1275. *
  1276. * This function should be called when a thread needs to wait for some kind of
  1277. * input. This clears pEventQueueServer which means we won't return from the
  1278. * wait until new input of the required type arrives. Setting the wake mask
  1279. * controls what input will wake us up. WOW apps skip this since their
  1280. * scheduler controls when they wake up.
  1281. *
  1282. * History:
  1283. * 09/12/97 GerardoB Created
  1284. \***************************************************************************/
  1285. VOID ClearQueueServerEvent(
  1286. WORD wWakeMask)
  1287. {
  1288. PTHREADINFO ptiCurrent = PtiCurrent();
  1289. UserAssert(wWakeMask != 0);
  1290. ptiCurrent->pcti->fsWakeMask = wWakeMask;
  1291. KeClearEvent(ptiCurrent->pEventQueueServer);
  1292. }
  1293. ULONG ParseReserved(
  1294. WCHAR *cczpReserved,
  1295. WCHAR *pchFind)
  1296. {
  1297. ULONG dw;
  1298. WCHAR *cczpch, *cczpchT, ch;
  1299. UNICODE_STRING cczuString;
  1300. dw = 0;
  1301. try {
  1302. if (cczpReserved != NULL && (cczpch = wcsstr(cczpReserved, pchFind)) != NULL) {
  1303. cczpch += wcslen(pchFind);
  1304. cczpchT = cczpch;
  1305. while (*cczpchT >= '0' && *cczpchT <= '9')
  1306. cczpchT++;
  1307. ch = *cczpchT;
  1308. *cczpchT = 0;
  1309. RtlInitUnicodeString(&cczuString, cczpch);
  1310. *cczpchT = ch;
  1311. RtlUnicodeStringToInteger(&cczuString, 0, &dw);
  1312. }
  1313. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  1314. return 0;
  1315. }
  1316. return dw;
  1317. }
  1318. /*
  1319. * Structure USER_PROCESS_PARAMETERS is used to capture all the fields that
  1320. * we touch in RTL_USER_PROCESS_PARAMETERS (ppeb->ProcessParameters) since the
  1321. * PEB can be trashed from the client side.
  1322. */
  1323. typedef struct tagUSER_PROCESS_PARAMETERS {
  1324. HANDLE StandardInput;
  1325. HANDLE StandardOutput;
  1326. ULONG StartingX;
  1327. ULONG StartingY;
  1328. ULONG CountX;
  1329. ULONG CountY;
  1330. ULONG WindowFlags;
  1331. ULONG ShowWindowFlags;
  1332. UNICODE_STRING DesktopInfo; // ProcessParameters
  1333. UNICODE_STRING ShellInfo; // ProcessParameters
  1334. } USER_PROCESS_PARAMETERS, *PUSER_PROCESS_PARAMETERS;
  1335. /***************************************************************************\
  1336. * xxxCreateThreadInfo
  1337. *
  1338. * Allocate the main thread information structure
  1339. *
  1340. * History:
  1341. * 03-18-95 JimA Created.
  1342. * 04-18-01 Mohamed Modified error recovery wrt hEventQueueClient.
  1343. \***************************************************************************/
  1344. NTSTATUS xxxCreateThreadInfo(
  1345. PETHREAD pEThread)
  1346. {
  1347. DWORD dwTIFlags = 0;
  1348. PPROCESSINFO ppi;
  1349. PTHREADINFO ptiCurrent;
  1350. PEPROCESS pEProcess = PsGetThreadProcess(pEThread);
  1351. PUSERSTARTUPINFO pusi;
  1352. PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
  1353. PUSER_PROCESS_PARAMETERS ProcessParams = NULL;
  1354. USER_PROCESS_PARAMETERS ProcessParamsData;
  1355. PDESKTOP pdesk = NULL;
  1356. HDESK hdesk = NULL;
  1357. HWINSTA hwinsta;
  1358. PQ pq;
  1359. NTSTATUS Status;
  1360. BOOL fFirstThread;
  1361. PTEB pteb = NtCurrentTeb();
  1362. TL tlpdesk, tlPool;
  1363. PPEB ppeb;
  1364. PVOID pTmpPool = NULL;
  1365. CheckCritIn();
  1366. UserAssert(IsWinEventNotifyDeferredOK());
  1367. ValidateProcessSessionId(pEProcess);
  1368. /*
  1369. * If CleanupResources was called for the last GUI thread then
  1370. * we should not allow any more GUI threads
  1371. */
  1372. if (gbCleanedUpResources) {
  1373. RIPMSG0(RIP_ERROR, "No more GUI threads should be created");
  1374. return STATUS_PROCESS_IS_TERMINATING;
  1375. }
  1376. /*
  1377. * Increment the number of GUI threads in the session
  1378. */
  1379. gdwGuiThreads++;
  1380. if (pEProcess == gpepCSRSS) {
  1381. dwTIFlags = TIF_CSRSSTHREAD | TIF_DONTATTACHQUEUE | TIF_DISABLEIME;
  1382. }
  1383. ptiCurrent = (PTHREADINFO)PsGetThreadWin32Thread(pEThread);
  1384. ProcessParamsData.DesktopInfo.Buffer = NULL;
  1385. ppeb = PsGetProcessPeb(pEProcess);
  1386. try {
  1387. /*
  1388. * NOTE: We allocate memory for the DesktopInfo.Buffer and free it
  1389. * later. For ShellInfo we do not since ParseReserved handles user-mode
  1390. * pointers.
  1391. */
  1392. if (ppeb != NULL) {
  1393. ProbeForRead(ppeb, sizeof(PEB), sizeof(BYTE));
  1394. ProcessParameters = ppeb->ProcessParameters;
  1395. ProcessParams = &ProcessParamsData;
  1396. ProbeForRead(ProcessParameters, sizeof(RTL_USER_PROCESS_PARAMETERS), sizeof(BYTE));
  1397. ProcessParamsData.StandardInput = ProcessParameters->StandardInput;
  1398. ProcessParamsData.StandardOutput = ProcessParameters->StandardOutput;
  1399. ProcessParamsData.StartingX = ProcessParameters->StartingX;
  1400. ProcessParamsData.StartingY = ProcessParameters->StartingY;
  1401. ProcessParamsData.CountX = ProcessParameters->CountX;
  1402. ProcessParamsData.CountY = ProcessParameters->CountY;
  1403. ProcessParamsData.WindowFlags = ProcessParameters->WindowFlags;
  1404. ProcessParamsData.ShowWindowFlags = ProcessParameters->ShowWindowFlags;
  1405. ProcessParamsData.DesktopInfo = ProbeAndReadUnicodeString(&ProcessParameters->DesktopInfo);
  1406. if (ProcessParamsData.DesktopInfo.Length > 0) {
  1407. PWSTR pszCapture = ProcessParamsData.DesktopInfo.Buffer;
  1408. ProbeForReadUnicodeStringBuffer(ProcessParamsData.DesktopInfo);
  1409. /*
  1410. * The pool pointer is stored in pTmpPool as well as in
  1411. * DesktopInfo.Buffer. The reason is that in case of corrupt
  1412. * user mode DesktopInfo an exception is raised which bails
  1413. * out of this try-except block without properly allocating
  1414. * the new pool. Therefore, pTmpPool is used in the check
  1415. * before freeing this pool.
  1416. */
  1417. pTmpPool = UserAllocPoolWithQuota(ProcessParamsData.DesktopInfo.Length, TAG_TEXT2);
  1418. ProcessParamsData.DesktopInfo.Buffer = pTmpPool;
  1419. if (ProcessParamsData.DesktopInfo.Buffer) {
  1420. ThreadLockPool(ptiCurrent, ProcessParamsData.DesktopInfo.Buffer, &tlPool);
  1421. RtlCopyMemory(ProcessParamsData.DesktopInfo.Buffer, pszCapture, ProcessParamsData.DesktopInfo.Length);
  1422. } else {
  1423. ExRaiseStatus(STATUS_NO_MEMORY);
  1424. }
  1425. } else {
  1426. ProcessParamsData.DesktopInfo.Buffer = NULL;
  1427. }
  1428. ProcessParamsData.ShellInfo = ProbeAndReadUnicodeString(&ProcessParameters->ShellInfo);
  1429. ProbeForReadUnicodeStringBuffer(ProcessParamsData.ShellInfo);
  1430. }
  1431. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  1432. Status = GetExceptionCode();
  1433. goto CreateThreadInfoFailed;
  1434. }
  1435. /*
  1436. * Locate the processinfo structure for the new thread.
  1437. */
  1438. ppi = PpiCurrent();
  1439. #ifdef _WIN64
  1440. /*
  1441. * If the process is marked as an emulated 32bit app mark the thread as
  1442. * an emulated 32bit thread. This is to be consistent with the way WOW16
  1443. * marks threads.
  1444. */
  1445. if (ppi->W32PF_Flags & W32PF_WOW64) {
  1446. dwTIFlags |= TIF_WOW64;
  1447. }
  1448. #endif
  1449. /*
  1450. * For Winlogon, only the first thread can have IME processing.
  1451. */
  1452. if (gpidLogon == PsGetThreadProcessId(pEThread)) {
  1453. if (ppi->ptiList != NULL) {
  1454. dwTIFlags |= TIF_DISABLEIME;
  1455. RIPMSG1(RIP_VERBOSE, "WinLogon, second or other thread. pti=%#p", PsGetThreadWin32Thread(pEThread));
  1456. }
  1457. }
  1458. ptiCurrent->TIF_flags = dwTIFlags;
  1459. Lock(&ptiCurrent->spklActive, gspklBaseLayout);
  1460. ptiCurrent->pcti = &(ptiCurrent->cti);
  1461. /*
  1462. * Check if no IME processing for all threads
  1463. * in the same process.
  1464. */
  1465. if (ppi->W32PF_Flags & W32PF_DISABLEIME) {
  1466. ptiCurrent->TIF_flags |= TIF_DISABLEIME;
  1467. }
  1468. /*
  1469. * Hook up this queue to this process info structure, increment
  1470. * the count of threads using this process info structure. Set up
  1471. * the ppi before calling SetForegroundPriority().
  1472. */
  1473. UserAssert(ppi != NULL);
  1474. ptiCurrent->ppi = ppi;
  1475. ptiCurrent->ptiSibling = ppi->ptiList;
  1476. ppi->ptiList = ptiCurrent;
  1477. ppi->cThreads++;
  1478. if (pteb != NULL) {
  1479. try {
  1480. pteb->Win32ThreadInfo = ptiCurrent;
  1481. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  1482. Status = GetExceptionCode();
  1483. goto CreateThreadInfoFailed;
  1484. }
  1485. }
  1486. /*
  1487. * Point to the client info.
  1488. */
  1489. if (dwTIFlags & TIF_SYSTEMTHREAD) {
  1490. ptiCurrent->pClientInfo = UserAllocPoolWithQuota(sizeof(CLIENTINFO),
  1491. TAG_CLIENTTHREADINFO);
  1492. if (ptiCurrent->pClientInfo == NULL) {
  1493. Status = STATUS_NO_MEMORY;
  1494. goto CreateThreadInfoFailed;
  1495. }
  1496. } else {
  1497. /*
  1498. * If this is not a system thread then grab the user mode client
  1499. * info elsewhere we use the GetClientInfo macro which looks here.
  1500. */
  1501. UserAssert(pteb != NULL);
  1502. try {
  1503. ptiCurrent->pClientInfo = ((PCLIENTINFO)((pteb)->Win32ClientInfo));
  1504. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  1505. Status = GetExceptionCode();
  1506. goto CreateThreadInfoFailed;
  1507. }
  1508. /*
  1509. * Set the restricted flag in the thread flags if this is a secure
  1510. * process.
  1511. */
  1512. if (((PW32PROCESS)ppi)->W32PF_Flags & W32PF_RESTRICTED) {
  1513. ptiCurrent->TIF_flags |= TIF_RESTRICTED;
  1514. }
  1515. }
  1516. /*
  1517. * Create the input event.
  1518. */
  1519. Status = ZwCreateEvent(&ptiCurrent->hEventQueueClient,
  1520. EVENT_ALL_ACCESS,
  1521. NULL,
  1522. SynchronizationEvent,
  1523. FALSE);
  1524. if (NT_SUCCESS(Status)) {
  1525. Status = ObReferenceObjectByHandle(ptiCurrent->hEventQueueClient,
  1526. EVENT_ALL_ACCESS,
  1527. *ExEventObjectType,
  1528. UserMode,
  1529. &ptiCurrent->pEventQueueServer,
  1530. NULL);
  1531. if (NT_SUCCESS(Status)) {
  1532. Status = ProtectHandle(ptiCurrent->hEventQueueClient, *ExEventObjectType, TRUE);
  1533. } else if (Status != STATUS_INVALID_HANDLE) {
  1534. ObCloseHandle(ptiCurrent->hEventQueueClient, UserMode);
  1535. }
  1536. }
  1537. if (!NT_SUCCESS(Status)) {
  1538. RIPMSG1(RIP_WARNING, "xxxCreateThreadInfo: failed a handle routine for hEventQueueClient handle, status=%08x", Status);
  1539. ptiCurrent->hEventQueueClient = NULL;
  1540. goto CreateThreadInfoFailed;
  1541. }
  1542. /*
  1543. * Mark the process as having threads that need cleanup. See
  1544. * DestroyProcessesObjects().
  1545. */
  1546. fFirstThread = !(ppi->W32PF_Flags & W32PF_THREADCONNECTED);
  1547. ppi->W32PF_Flags |= W32PF_THREADCONNECTED;
  1548. /*
  1549. * If we haven't copied over our startup info yet, do it now.
  1550. * Don't bother copying the info if we aren't going to use it.
  1551. */
  1552. if (ProcessParams) {
  1553. pusi = &ppi->usi;
  1554. if ((pusi->cb == 0) && (ProcessParams->WindowFlags != 0)) {
  1555. pusi->cb = sizeof(USERSTARTUPINFO);
  1556. pusi->dwX = ProcessParams->StartingX;
  1557. pusi->dwY = ProcessParams->StartingY;
  1558. pusi->dwXSize = ProcessParams->CountX;
  1559. pusi->dwYSize = ProcessParams->CountY;
  1560. pusi->dwFlags = ProcessParams->WindowFlags;
  1561. pusi->wShowWindow = (WORD)ProcessParams->ShowWindowFlags;
  1562. }
  1563. if (fFirstThread) {
  1564. /*
  1565. * Set up the hot key, if there is one.
  1566. *
  1567. * If the STARTF_USEHOTKEY flag is given in the startup info, then
  1568. * the hStdInput is the hotkey (new from Chicago). Otherwise, parse
  1569. * it out in string format from the lpReserved string.
  1570. */
  1571. if (ProcessParams->WindowFlags & STARTF_USEHOTKEY) {
  1572. ppi->dwHotkey = HandleToUlong(ProcessParams->StandardInput);
  1573. } else {
  1574. if (ProcessParams->ShellInfo.Length > 0) {
  1575. ppi->dwHotkey = ParseReserved(ProcessParams->ShellInfo.Buffer,
  1576. L"hotkey.");
  1577. } else {
  1578. ppi->dwHotkey = 0;
  1579. }
  1580. }
  1581. /*
  1582. * Copy the monitor handle, if there is one.
  1583. */
  1584. UserAssert(!ppi->hMonitor);
  1585. if (ProcessParams->WindowFlags & STARTF_HASSHELLDATA) {
  1586. HMONITOR hMonitor;
  1587. hMonitor = (HMONITOR)ProcessParams->StandardOutput;
  1588. if (ValidateHmonitor(hMonitor)) {
  1589. ppi->hMonitor = hMonitor;
  1590. }
  1591. }
  1592. }
  1593. }
  1594. if ((pq = AllocQueue(NULL, NULL)) == NULL) {
  1595. Status = STATUS_NO_MEMORY;
  1596. goto CreateThreadInfoFailed;
  1597. }
  1598. /*
  1599. * Attach the Q to the THREADINFO.
  1600. */
  1601. ptiCurrent->pq = pq;
  1602. pq->ptiMouse = pq->ptiKeyboard = ptiCurrent;
  1603. pq->cThreads++;
  1604. /*
  1605. * Open the windowstation and desktop. If this is a system
  1606. * thread only use the desktop that might be stored in the teb.
  1607. */
  1608. UserAssert(ptiCurrent->rpdesk == NULL);
  1609. if (!(ptiCurrent->TIF_flags & (TIF_SYSTEMTHREAD | TIF_CSRSSTHREAD)) &&
  1610. grpWinStaList) {
  1611. BOOL bShutDown = FALSE;
  1612. hdesk = xxxResolveDesktop(
  1613. NtCurrentProcess(),
  1614. &ProcessParams->DesktopInfo,
  1615. &hwinsta, (ProcessParams->WindowFlags & STARTF_DESKTOPINHERIT),
  1616. &bShutDown);
  1617. if (hdesk == NULL) {
  1618. if (bShutDown) {
  1619. /*
  1620. * Trying to create a new process during logoff.
  1621. */
  1622. ULONG_PTR adwParameters[5] = {0, 0, 0, 0, MB_DEFAULT_DESKTOP_ONLY};
  1623. ULONG ErrorResponse;
  1624. LeaveCrit();
  1625. ExRaiseHardError((NTSTATUS)STATUS_DLL_INIT_FAILED_LOGOFF,
  1626. ARRAY_SIZE(adwParameters),
  1627. 0,
  1628. adwParameters,
  1629. OptionOkNoWait,
  1630. &ErrorResponse);
  1631. ZwTerminateProcess(NtCurrentProcess(), STATUS_DLL_INIT_FAILED);
  1632. EnterCrit();
  1633. }
  1634. Status = STATUS_DLL_INIT_FAILED;
  1635. goto CreateThreadInfoFailed;
  1636. } else {
  1637. Status = _SetProcessWindowStation(hwinsta, UserMode);
  1638. if (!NT_SUCCESS(Status)) {
  1639. goto CreateThreadInfoFailed;
  1640. }
  1641. /*
  1642. * Reference the desktop handle
  1643. */
  1644. Status = ObReferenceObjectByHandle(hdesk,
  1645. 0,
  1646. *ExDesktopObjectType,
  1647. KernelMode,
  1648. &pdesk,
  1649. NULL);
  1650. if (!NT_SUCCESS(Status)) {
  1651. UserAssert(pdesk == NULL);
  1652. goto CreateThreadInfoFailed;
  1653. }
  1654. ThreadLockDesktop(ptiCurrent, pdesk, &tlpdesk, LDLT_FN_CREATETHREADINFO);
  1655. ObDereferenceObject(pdesk);
  1656. /*
  1657. * Map the desktop into the current process.
  1658. */
  1659. {
  1660. WIN32_OPENMETHOD_PARAMETERS OpenParams;
  1661. OpenParams.OpenReason = ObOpenHandle;
  1662. OpenParams.Process = PsGetCurrentProcess();
  1663. OpenParams.Object = pdesk;
  1664. OpenParams.GrantedAccess = 0;
  1665. OpenParams.HandleCount = 1;
  1666. if (!NT_SUCCESS(MapDesktop(&OpenParams))) {
  1667. RIPMSGF2(RIP_WARNING,
  1668. "Could't map pdesk %p in ppi %p",
  1669. pdesk,
  1670. PpiCurrent());
  1671. Status = STATUS_NO_MEMORY;
  1672. goto CreateThreadInfoFailed;
  1673. }
  1674. }
  1675. /*
  1676. * The first desktop is the default for all succeeding threads.
  1677. */
  1678. if (ppi->hdeskStartup == NULL &&
  1679. PsGetProcessId(pEProcess) != gpidLogon) {
  1680. LockDesktop(&ppi->rpdeskStartup, pdesk, LDL_PPI_DESKSTARTUP2, (ULONG_PTR)ppi);
  1681. ppi->hdeskStartup = hdesk;
  1682. }
  1683. }
  1684. }
  1685. /*
  1686. * Remember dwExpWinVer. This is used to return GetAppVer() (and
  1687. * GetExpWinVer(NULL)).
  1688. */
  1689. if (PsGetProcessPeb(pEProcess) != NULL) {
  1690. ptiCurrent->dwExpWinVer = RtlGetExpWinVer(PsGetProcessSectionBaseAddress(pEProcess));
  1691. } else {
  1692. ptiCurrent->dwExpWinVer = VER40;
  1693. }
  1694. INITCLIENTINFO(ptiCurrent);
  1695. /*
  1696. * Set the desktop even if it is NULL to ensure that ptiCurrent->pDeskInfo
  1697. * is set.
  1698. * NOTE: This adds the pti to the desktop's PtiList, but we don't yet have
  1699. * a pti->pq. zzzRecalcThreadAttachment loops through this PtiList expects
  1700. * a pq, so we must not leave the critsect until we have a queue.
  1701. * zzzSetDesktop only zzz leaves the critsect if there is a pti->pq, so we
  1702. * can BEGINATOMICCHECK to ensure this, and make sure we allocate the queue
  1703. * before we leave the critical section.
  1704. */
  1705. BEGINATOMICCHECK();
  1706. if (zzzSetDesktop(ptiCurrent, pdesk, hdesk) == FALSE) {
  1707. EXITATOMICCHECK();
  1708. goto CreateThreadInfoFailed;
  1709. }
  1710. ENDATOMICCHECK();
  1711. /*
  1712. * If we have a desktop and are journalling on that desktop, use
  1713. * the journal queue, otherwise create a new queue.
  1714. */
  1715. if (pdesk == grpdeskRitInput) {
  1716. UserAssert((pdesk == NULL) || (ptiCurrent->pDeskInfo == pdesk->pDeskInfo));
  1717. UserAssert(ptiCurrent->rpdesk == pdesk);
  1718. pq = GetJournallingQueue(ptiCurrent);
  1719. if (pq != NULL && pq != ptiCurrent->pq) {
  1720. DestroyThreadsMessages(ptiCurrent->pq, ptiCurrent);
  1721. zzzDestroyQueue(ptiCurrent->pq, ptiCurrent);
  1722. ptiCurrent->pq = pq;
  1723. pq->cThreads++;
  1724. }
  1725. }
  1726. /*
  1727. * Remember that this is a screen saver. That way we can set its
  1728. * priority appropriately when it is idle or when it needs to go
  1729. * away. At first we set it to normal priority, then we set the
  1730. * TIF_IDLESCREENSAVER bit so that when it activates it will get
  1731. * lowered in priority.
  1732. */
  1733. if (ProcessParams && ProcessParams->WindowFlags & STARTF_SCREENSAVER) {
  1734. if (fFirstThread) {
  1735. UserAssert(gppiScreenSaver == NULL);
  1736. /*
  1737. * Make sure the parent's process is WinLogon, since only
  1738. * WinLogon is allowed to use the STARTF_SCREENSAVER flag.
  1739. */
  1740. if (gpidLogon == 0 || PsGetProcessInheritedFromUniqueProcessId(pEProcess) != gpidLogon) {
  1741. RIPMSG0(RIP_WARNING,"Only the Logon process can launch a screen saver.");
  1742. ProcessParams->WindowFlags &= ~STARTF_SCREENSAVER;
  1743. goto NotAScreenSaver;
  1744. }
  1745. gppiScreenSaver = ppi;
  1746. gptSSCursor = gpsi->ptCursor;
  1747. ppi->W32PF_Flags |= W32PF_SCREENSAVER;
  1748. } else {
  1749. UserAssert(ppi->W32PF_Flags & W32PF_SCREENSAVER);
  1750. }
  1751. SetForegroundPriority(ptiCurrent, TRUE);
  1752. if (fFirstThread) {
  1753. ppi->W32PF_Flags |= W32PF_IDLESCREENSAVER;
  1754. }
  1755. /*
  1756. * Screen saver doesn't need any IME processing.
  1757. */
  1758. ptiCurrent->TIF_flags |= TIF_DISABLEIME;
  1759. }
  1760. NotAScreenSaver:
  1761. /*
  1762. * Do special processing for the first thread of a process.
  1763. */
  1764. if (!(ptiCurrent->TIF_flags & (TIF_SYSTEMTHREAD | TIF_CSRSSTHREAD))) {
  1765. #ifndef LAZY_CLASS_INIT
  1766. /*
  1767. * I changed the code a while ago to unregister classes when the last
  1768. * GUI thread is destroyed. Simply, there was too much stuff getting
  1769. * unlocked and destroyed to guarantee that it would work on a non-GUI
  1770. * thread. So if a process destroys its last GUI thread and then makes
  1771. * a thread GUI later, we need to re-register the classes.
  1772. */
  1773. if (!(ppi->W32PF_Flags & W32PF_CLASSESREGISTERED)) {
  1774. if (!LW_RegisterWindows()) {
  1775. RIPMSG0(RIP_WARNING, "xxxCreateThreadInfo: LW_RegisterWindows failed");
  1776. Status = STATUS_UNSUCCESSFUL;
  1777. goto CreateThreadInfoFailed;
  1778. }
  1779. }
  1780. #endif
  1781. if (fFirstThread) {
  1782. /*
  1783. * If this is an application starting (ie. not some thread of
  1784. * the server context), enable the app-starting cursor.
  1785. */
  1786. DeferWinEventNotify();
  1787. zzzCalcStartCursorHide((PW32PROCESS)PsGetProcessWin32Process(pEProcess), 5000);
  1788. EndDeferWinEventNotifyWithoutProcessing();
  1789. /*
  1790. * Open the windowstation.
  1791. */
  1792. if (grpWinStaList && ppi->rpwinsta == NULL) {
  1793. RIPERR0(ERROR_CAN_NOT_COMPLETE,
  1794. RIP_WARNING,
  1795. "System is not initialized");
  1796. Status = STATUS_UNSUCCESSFUL;
  1797. goto CreateThreadInfoFailed;
  1798. }
  1799. }
  1800. #ifndef LAZY_CLASS_INIT
  1801. } else {
  1802. /*
  1803. * Don't register system windows until cursors and icons have been
  1804. * loaded.
  1805. */
  1806. if ((SYSCUR(ARROW) != NULL) &&
  1807. !(ppi->W32PF_Flags & W32PF_CLASSESREGISTERED)) {
  1808. if (!LW_RegisterWindows()) {
  1809. RIPMSG0(RIP_WARNING, "xxxCreateThreadInfo: LW_RegisterWindows failed");
  1810. Status = STATUS_UNSUCCESSFUL;
  1811. goto CreateThreadInfoFailed;
  1812. }
  1813. }
  1814. #endif
  1815. }
  1816. /*
  1817. * Initialize hung timer value.
  1818. */
  1819. SET_TIME_LAST_READ(ptiCurrent);
  1820. /*
  1821. * If someone is waiting on this process propagate that info into the
  1822. * thread info.
  1823. */
  1824. if (ppi->W32PF_Flags & W32PF_WAITFORINPUTIDLE) {
  1825. ptiCurrent->TIF_flags |= TIF_WAITFORINPUTIDLE;
  1826. }
  1827. /*
  1828. * Mark the thread as initialized.
  1829. */
  1830. ptiCurrent->TIF_flags |= TIF_GUITHREADINITIALIZED;
  1831. /*
  1832. * Allow the thread to come to foreground when it is created if the
  1833. * current process is the foreground process or the last input owner.
  1834. * This Flag is a hack to fix Bug 28502. When we click on "Map Network
  1835. * Drive" button on the toolbar, explorer creates another thread to
  1836. * create the dialog box. This will create the dialog in the background.
  1837. * We are adding this fix at the request of the Shell team so that this
  1838. * dialog comes up as foreground.
  1839. *
  1840. * If the process already has the foreground right, we don't give it to
  1841. * this thread (it doesn't need it). We do this to narrow the number of
  1842. * ways this process can force the foreground. Also, if the process is
  1843. * starting, it already has the right unless the user has canceled it --
  1844. * in which case we don't want to give it back.
  1845. */
  1846. if (!(ppi->W32PF_Flags & (W32PF_ALLOWFOREGROUNDACTIVATE | W32PF_APPSTARTING))) {
  1847. if (((gptiForeground != NULL) && (ppi == gptiForeground->ppi))
  1848. || ((glinp.ptiLastWoken != NULL) && (ppi == glinp.ptiLastWoken->ppi))) {
  1849. ptiCurrent->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE;
  1850. TAGMSG1(DBGTAG_FOREGROUND, "xxxCreateThreadInfo set TIF %#p", ptiCurrent);
  1851. }
  1852. }
  1853. if (IS_IME_ENABLED()) {
  1854. /*
  1855. * Create per-thread default input context
  1856. */
  1857. CreateInputContext(0);
  1858. }
  1859. /*
  1860. * Call back to the client to finish initialization.
  1861. */
  1862. if (!(dwTIFlags & (TIF_SYSTEMTHREAD | TIF_CSRSSTHREAD))) {
  1863. if (SetAppCompatFlags(ptiCurrent)) {
  1864. /*
  1865. * Flag this process as a setup app.
  1866. */
  1867. ppi->W32PF_Flags |= W32PF_SETUPAPP;
  1868. }
  1869. Status = xxxClientThreadSetup();
  1870. if (!NT_SUCCESS(Status)) {
  1871. RIPMSG1(RIP_WARNING, "ClientThreadSetup failed with NTSTATUS %lx", Status);
  1872. goto CreateThreadInfoFailed;
  1873. }
  1874. }
  1875. if ((NT_SUCCESS(Status) && fFirstThread) &&
  1876. !(ppi->W32PF_Flags & W32PF_CONSOLEAPPLICATION)) {
  1877. /*
  1878. * Don't play the sound for console processes since we will play it
  1879. * when the console window is created.
  1880. */
  1881. PlayEventSound(USER_SOUND_OPEN);
  1882. }
  1883. /*
  1884. * Release desktop.
  1885. * Some other thread might have been waiting to destroy this desktop
  1886. * when xxxResolveDestktop got a handle to it. So let's double
  1887. * check this now that we have called back several times after getting
  1888. * the handle back.
  1889. */
  1890. if (pdesk != NULL) {
  1891. if (pdesk->dwDTFlags & DF_DESTROYED) {
  1892. RIPMSG1(RIP_WARNING, "xxxCreateThreadInfo: pdesk destroyed:%#p", pdesk);
  1893. Status = STATUS_UNSUCCESSFUL;
  1894. goto CreateThreadInfoFailed;
  1895. }
  1896. ThreadUnlockDesktop(ptiCurrent, &tlpdesk, LDUT_FN_CREATETHREADINFO1);
  1897. }
  1898. if (pTmpPool != NULL) {
  1899. ThreadUnlockAndFreePool(ptiCurrent, &tlPool);
  1900. }
  1901. UserAssert(NT_SUCCESS(Status));
  1902. return Status;
  1903. Error:
  1904. CreateThreadInfoFailed:
  1905. RIPMSG3(RIP_WARNING, "xxxCreateThreadInfo failed: pti %#p pdesk %#p status 0x%x", ptiCurrent, pdesk, Status);
  1906. if (pdesk != NULL) {
  1907. ThreadUnlockDesktop(ptiCurrent, &tlpdesk, LDUT_FN_CREATETHREADINFO2);
  1908. }
  1909. if (pTmpPool != NULL) {
  1910. ThreadUnlockAndFreePool(ptiCurrent, &tlPool);
  1911. }
  1912. xxxDestroyThreadInfo();
  1913. return Status;
  1914. }
  1915. /***************************************************************************\
  1916. * AllocQueue
  1917. *
  1918. * Allocates the memory for a TI structure and initializes its fields.
  1919. * Each Win32 queue has it's own TI while all Win16 threads share the same
  1920. * TI.
  1921. *
  1922. * History:
  1923. * 02-21-91 MikeHar Created.
  1924. \***************************************************************************/
  1925. PQ AllocQueue(
  1926. PTHREADINFO ptiKeyState, // If non-Null then use this key state.
  1927. // Otherwise use global AsyncKeyState.
  1928. PQ pq) // Non-NULL == preallocated object.
  1929. {
  1930. USHORT cLockCount;
  1931. if (pq == NULL) {
  1932. pq = ExAllocateFromPagedLookasideList(QLookaside);
  1933. if (pq == NULL) {
  1934. return NULL;
  1935. }
  1936. cLockCount = 0;
  1937. } else {
  1938. DebugValidateMLIST(&pq->mlInput);
  1939. /*
  1940. * Preserve lock count.
  1941. */
  1942. cLockCount = pq->cLockCount;
  1943. }
  1944. RtlZeroMemory(pq, sizeof(Q));
  1945. pq->cLockCount = cLockCount;
  1946. /*
  1947. * This is a new queue; we need to update its key state table before
  1948. * the first input event is put in the queue.
  1949. * We do this by copying the current keystate table and NULLing the recent
  1950. * down state table. If a key is really down it will be updated when
  1951. * we get it repeats.
  1952. *
  1953. * He is the old way that did not work because if the first key was say an
  1954. * alt key the Async table would be updated, then the UpdateKeyState
  1955. * message and it would look like the alt key was PREVIOUSLY down.
  1956. *
  1957. * The queue will get updated when it first reads input: to allow the
  1958. * app to query the key state before it calls GetMessage, set its initial
  1959. * key state to the asynchronous key state.
  1960. */
  1961. if (ptiKeyState) {
  1962. RtlCopyMemory(pq->afKeyState, ptiKeyState->pq->afKeyState, CBKEYSTATE);
  1963. } else {
  1964. RtlCopyMemory(pq->afKeyState, gafAsyncKeyState, CBKEYSTATE);
  1965. }
  1966. /*
  1967. * If there isn't a mouse set iCursorLevel to -1 so the
  1968. * mouse cursor won't be visible on the screen.
  1969. */
  1970. if (!TEST_GTERMF(GTERMF_MOUSE)) {
  1971. pq->iCursorLevel--;
  1972. }
  1973. /*
  1974. * While the thread is starting up ... it has the wait cursor.
  1975. */
  1976. LockQCursor(pq, SYSCUR(WAIT));
  1977. DebugValidateMLIST(&pq->mlInput);
  1978. return pq;
  1979. }
  1980. /***************************************************************************\
  1981. * FreeQueue
  1982. *
  1983. * 04-04-96 GerardoB Created.
  1984. \***************************************************************************/
  1985. VOID FreeQueue(
  1986. PQ pq)
  1987. {
  1988. #if DBG
  1989. /*
  1990. * Turn off the flag indicating that this queue is in destruction.
  1991. * We do this in either case that we are putting this into the free
  1992. * list, or truly destroying the handle. We use this to try and
  1993. * track cases where someone tries to lock elements into the queue
  1994. * structure while it's going through destuction.
  1995. */
  1996. pq->QF_flags &= ~QF_INDESTROY;
  1997. #endif
  1998. UserAssert(pq != gpqForeground);
  1999. UserAssert(pq != gpqForegroundPrev);
  2000. UserAssert(pq != gpqCursor);
  2001. ExFreeToPagedLookasideList(QLookaside, pq);
  2002. }
  2003. /***************************************************************************\
  2004. * FreeCachedQueues
  2005. *
  2006. * 14-Jan-98 CLupu Created.
  2007. \***************************************************************************/
  2008. VOID FreeCachedQueues(
  2009. VOID)
  2010. {
  2011. if (QLookaside != NULL) {
  2012. ExDeletePagedLookasideList(QLookaside);
  2013. UserFreePool(QLookaside);
  2014. QLookaside = NULL;
  2015. }
  2016. }
  2017. /***************************************************************************\
  2018. * zzzDestroyQueue
  2019. *
  2020. *
  2021. * History:
  2022. * 05-20-91 MikeHar Created.
  2023. \***************************************************************************/
  2024. VOID zzzDestroyQueue(
  2025. PQ pq,
  2026. PTHREADINFO pti)
  2027. {
  2028. PTHREADINFO ptiT;
  2029. PTHREADINFO ptiAny, ptiBestMouse, ptiBestKey;
  2030. PLIST_ENTRY pHead, pEntry;
  2031. #if DBG
  2032. USHORT cDying = 0;
  2033. #endif
  2034. BOOL fSetFMouseMoved = FALSE;
  2035. DebugValidateMLIST(&pq->mlInput);
  2036. UserAssert(pq->cThreads);
  2037. pq->cThreads--;
  2038. if (pq->cThreads != 0) {
  2039. /*
  2040. * Since we aren't going to destroy this queue, make sure
  2041. * it isn't pointing to the THREADINFO that's going away.
  2042. */
  2043. if (pq->ptiSysLock == pti) {
  2044. CheckSysLock(6, pq, NULL);
  2045. pq->ptiSysLock = NULL;
  2046. }
  2047. if ((pq->ptiKeyboard == pti) || (pq->ptiMouse == pti)) {
  2048. /*
  2049. * Run through THREADINFOs looking for one pointing to pq.
  2050. */
  2051. ptiAny = NULL;
  2052. ptiBestMouse = NULL;
  2053. ptiBestKey = NULL;
  2054. pHead = &pti->rpdesk->PtiList;
  2055. for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) {
  2056. ptiT = CONTAINING_RECORD(pEntry, THREADINFO, PtiLink);
  2057. /*
  2058. * Skip threads that are going away or belong to a
  2059. * different queue.
  2060. */
  2061. if ((ptiT->TIF_flags & TIF_INCLEANUP) || (ptiT->pq != pq)) {
  2062. #if DBG
  2063. if (ptiT->pq == pq && (ptiT->TIF_flags & TIF_INCLEANUP)) {
  2064. cDying++;
  2065. }
  2066. #endif
  2067. continue;
  2068. }
  2069. ptiAny = ptiT;
  2070. if (pti->pcti->fsWakeBits & QS_MOUSE) {
  2071. if (ptiT->pcti->fsWakeMask & QS_MOUSE)
  2072. ptiBestMouse = ptiT;
  2073. }
  2074. if (pti->pcti->fsWakeBits & QS_KEY) {
  2075. if (ptiT->pcti->fsWakeMask & QS_KEY)
  2076. ptiBestKey = ptiT;
  2077. }
  2078. #ifdef GENERIC_INPUT
  2079. if (pti->pcti->fsWakeMask & QS_RAWINPUT) {
  2080. if (ptiT->pcti->fsWakeMask & QS_RAWINPUT) {
  2081. /* For now, let's use keyboard focus to route the raw input */
  2082. ptiBestKey = ptiT;
  2083. }
  2084. }
  2085. #endif
  2086. }
  2087. if (ptiBestMouse == NULL)
  2088. ptiBestMouse = ptiAny;
  2089. if (ptiBestKey == NULL)
  2090. ptiBestKey = ptiAny;
  2091. /*
  2092. * Transfer any wake-bits to this new queue. This
  2093. * is a common problem for QS_MOUSEMOVE which doesn't
  2094. * get set on coalesced WM_MOUSEMOVE events, so we
  2095. * need to make sure the new thread tries to process
  2096. * any input waiting in the queue.
  2097. */
  2098. if (ptiBestMouse != NULL)
  2099. SetWakeBit(ptiBestMouse, pti->pcti->fsWakeBits & QS_MOUSE);
  2100. if (ptiBestKey != NULL) {
  2101. SetWakeBit(ptiBestKey, pti->pcti->fsWakeBits & QS_KEY);
  2102. #ifdef GENERIC_INPUT
  2103. SetWakeBit(ptiBestKey, pti->pcti->fsWakeBits & QS_RAWINPUT);
  2104. #endif
  2105. }
  2106. if (pq->ptiKeyboard == pti)
  2107. pq->ptiKeyboard = ptiBestKey;
  2108. if (pq->ptiMouse == pti)
  2109. pq->ptiMouse = ptiBestMouse;
  2110. #if DBG
  2111. /*
  2112. * Bad things happen if ptiKeyboard or ptiMouse are NULL
  2113. */
  2114. if (pq->cThreads != cDying && (pq->ptiKeyboard == NULL || pq->ptiMouse == NULL)) {
  2115. RIPMSG6(RIP_ERROR,
  2116. "pq %#p pq->cThreads %x cDying %x pti %#p ptiK %#p ptiM %#p",
  2117. pq, pq->cThreads, cDying, pti, pq->ptiKeyboard, pq->ptiMouse);
  2118. }
  2119. #endif
  2120. }
  2121. return;
  2122. }
  2123. /*
  2124. * Unlock any potentially locked globals now that we know absolutely
  2125. * that this queue is going away.
  2126. */
  2127. UnlockCaptureWindow(pq);
  2128. Unlock(&pq->spwndFocus);
  2129. Unlock(&pq->spwndActive);
  2130. Unlock(&pq->spwndActivePrev);
  2131. Unlock(&pq->caret.spwnd);
  2132. LockQCursor(pq, NULL);
  2133. #if DBG
  2134. /*
  2135. * Mark this queue as being in the destruction process. This is
  2136. * cleared in FreeQueue() once we have determined it's safe to
  2137. * place in the free-list, or destroy the handle. We use this
  2138. * to track cases where someone will lock a cursor into the queue
  2139. * while it's in the middle of being destroyed.
  2140. */
  2141. pq->QF_flags |= QF_INDESTROY;
  2142. #endif
  2143. /*
  2144. * Free everything else that was allocated/created by AllocQueue.
  2145. */
  2146. FreeMessageList(&pq->mlInput);
  2147. /*
  2148. * If this queue is in the foreground, set gpqForeground
  2149. * to NULL so no input is routed. At some point we'll want
  2150. * to do slightly more clever assignment of gpqForeground here.
  2151. */
  2152. if (gpqForeground == pq) {
  2153. gpqForeground = NULL;
  2154. }
  2155. if (gpqForegroundPrev == pq) {
  2156. gpqForegroundPrev = NULL;
  2157. }
  2158. if (gpqCursor == pq) {
  2159. gpqCursor = NULL;
  2160. fSetFMouseMoved = TRUE;
  2161. }
  2162. if (pq->cLockCount == 0) {
  2163. FreeQueue(pq);
  2164. }
  2165. if (fSetFMouseMoved) {
  2166. zzzSetFMouseMoved();
  2167. }
  2168. }
  2169. /**************************************************************************\
  2170. * UserDeleteW32Thread
  2171. *
  2172. * This function is called when the W32THREAD reference count goes
  2173. * down to zero. So everything left around by xxxDestroyThreadInfo
  2174. * must be cleaned up here.
  2175. *
  2176. * SO VERY IMPORTANT:
  2177. * Note that this call is not in the context of the pti being cleaned up,
  2178. * in other words, pti != PtiCurrent(). So only kernel calls are allowed here.
  2179. *
  2180. * 04-01-96 GerardoB Created
  2181. \**************************************************************************/
  2182. VOID UserDeleteW32Thread(
  2183. PW32THREAD pW32Thread)
  2184. {
  2185. PTHREADINFO pti = (PTHREADINFO)pW32Thread;
  2186. BEGIN_REENTERCRIT();
  2187. /*
  2188. * Make sure the ref count didn't get bumped up while we were waiting.
  2189. */
  2190. if (pW32Thread->RefCount == 0) {
  2191. /*
  2192. * Events
  2193. */
  2194. if (pti->pEventQueueServer != NULL) {
  2195. ObDereferenceObject(pti->pEventQueueServer);
  2196. }
  2197. if (pti->apEvent != NULL) {
  2198. UserFreePool(pti->apEvent);
  2199. }
  2200. /*
  2201. * App name.
  2202. */
  2203. if (pti->pstrAppName != NULL) {
  2204. UserFreePool(pti->pstrAppName);
  2205. }
  2206. /*
  2207. * Unlock the queues and free them if no one is using them (the
  2208. * queues were already destroyed in DestroyThreadInfo).
  2209. */
  2210. if (pti->pq != NULL) {
  2211. UserAssert(pti->pq->cLockCount);
  2212. --(pti->pq->cLockCount);
  2213. if (pti->pq->cLockCount == 0 && pti->pq->cThreads == 0) {
  2214. FreeQueue(pti->pq);
  2215. }
  2216. }
  2217. /*
  2218. * zzzReattachThreads shouldn't call back while using pqAttach
  2219. */
  2220. UserAssert(pti->pqAttach == NULL);
  2221. /*
  2222. * Unlock the desktop (pti already unlinked from ptiList)
  2223. */
  2224. if (pti->rpdesk != NULL) {
  2225. UnlockDesktop(&pti->rpdesk, LDU_PTI_DESK, (ULONG_PTR)pti);
  2226. }
  2227. /*
  2228. * Remove the pointer to this W32Thread and free the associated memory.
  2229. */
  2230. PsSetThreadWin32Thread(pW32Thread->pEThread, NULL, pW32Thread);
  2231. Win32FreePool(pW32Thread);
  2232. }
  2233. END_REENTERCRIT();
  2234. }
  2235. /**************************************************************************\
  2236. * UserDeleteW32Process
  2237. *
  2238. * This function is called when the W32PROCESS reference count goes
  2239. * down to zero. So everything left around by DestroyProcessInfo
  2240. * must be cleaned up here.
  2241. *
  2242. * SO VERY IMPORTANT:
  2243. * Note that this call may not be in the context of the ppi being cleaned up,
  2244. * in other words, ppi != PpiCurrent(). So only kernel calls are allowed here.
  2245. *
  2246. * 04-01-96 GerardoB Created
  2247. \**************************************************************************/
  2248. VOID UserDeleteW32Process(
  2249. PW32PROCESS pW32Process)
  2250. {
  2251. PPROCESSINFO ppi = (PPROCESSINFO)pW32Process;
  2252. BEGIN_REENTERCRIT();
  2253. /*
  2254. * Make sure the ref count didn't get bumped up while we were waiting.
  2255. */
  2256. if (pW32Process->RefCount == 0) {
  2257. UserAssert(ppi->ptiMainThread == NULL && ppi->ptiList == NULL);
  2258. /*
  2259. * Grab the handle flags lock. We can't call into the object manager when
  2260. * we have this or we might deadlock.
  2261. */
  2262. EnterHandleFlagsCrit();
  2263. /*
  2264. * Delete handle flags attribute bitmap
  2265. */
  2266. if (ppi->bmHandleFlags.Buffer) {
  2267. UserFreePool(ppi->bmHandleFlags.Buffer);
  2268. RtlInitializeBitMap(&ppi->bmHandleFlags, NULL, 0);
  2269. }
  2270. /*
  2271. * Remove the pointer to this W32Process and free the associated memory.
  2272. */
  2273. PsSetProcessWin32Process(pW32Process->Process, NULL, pW32Process);
  2274. Win32FreePool(pW32Process);
  2275. /*
  2276. * Release the handle flags lock.
  2277. */
  2278. LeaveHandleFlagsCrit();
  2279. }
  2280. END_REENTERCRIT();
  2281. }
  2282. /***************************************************************************\
  2283. * FLastGuiThread
  2284. *
  2285. * Check if this is the last GUI thread in the process.
  2286. \***************************************************************************/
  2287. __inline BOOL FLastGuiThread(
  2288. PTHREADINFO pti)
  2289. {
  2290. return (pti->ppi != NULL &&
  2291. pti->ppi->ptiList == pti &&
  2292. pti->ptiSibling == NULL);
  2293. }
  2294. /***************************************************************************\
  2295. * xxxDestroyThreadInfo
  2296. *
  2297. * Destroys a THREADINFO created by xxxCreateThreadInfo().
  2298. *
  2299. * Note that the current pti can be locked so it might be used after this
  2300. * function returns, even though the thread execution has ended.
  2301. *
  2302. * We want to stop any activity on this thread so we clean up any USER stuff
  2303. * like messages, clipboard, queue, etc and specially anything that assumes
  2304. * to be running on a Win32 thread and client side stuff. The final cleanup
  2305. * will take place in UserDeleteW32Thread.
  2306. *
  2307. * This function must not go into the user mode because the ntos data
  2308. * structures may no longer support it and it may bluescreen the system.
  2309. *
  2310. * Make all callbacks before the thread objects are destroyed. If you callback
  2311. * afterwards, new objects might be created and won't be cleaned up.
  2312. *
  2313. * History:
  2314. * 02-15-91 DarrinM Created.
  2315. * 02-27-91 mikeke Made it work
  2316. * 02-27-91 Mikehar Removed queue from the global list
  2317. \***************************************************************************/
  2318. VOID xxxDestroyThreadInfo(
  2319. VOID)
  2320. {
  2321. PTHREADINFO ptiCurrent = PtiCurrent(), *ppti;
  2322. PTEB pteb = NtCurrentTeb();
  2323. UserAssert(ptiCurrent != NULL);
  2324. UserAssert(IsWinEventNotifyDeferredOK());
  2325. /*
  2326. * We must NULL out the Win32ThreadInfo pointer. Otherwise, a thread
  2327. * that is here due to failing to convert to GUI can call a USER
  2328. * function, at which point they'll access this (now bogus) pointer.
  2329. */
  2330. if (pteb != NULL) {
  2331. try {
  2332. pteb->Win32ThreadInfo = NULL;
  2333. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  2334. /*
  2335. * Do nothing. Worse case scenario, the app crashes later,
  2336. * but that's the full extent of what we were trying to avoid.
  2337. */
  2338. }
  2339. }
  2340. /*
  2341. * If this thread is blocking input, stop it.
  2342. */
  2343. if (gptiBlockInput == ptiCurrent) {
  2344. gptiBlockInput = NULL;
  2345. }
  2346. /*
  2347. * Don't mess with this ptiCurrent anymore.
  2348. */
  2349. ptiCurrent->TIF_flags |= (TIF_DONTATTACHQUEUE | TIF_INCLEANUP);
  2350. /*
  2351. * First do any preparation work: Windows need to be "patched" so that
  2352. * their window procs point to server only windowprocs, for example.
  2353. */
  2354. PatchThreadWindows(ptiCurrent);
  2355. /*
  2356. * If this thread terminated abnormally and was tracking tell
  2357. * GDI to hide the trackrect.
  2358. */
  2359. if (ptiCurrent->pmsd != NULL) {
  2360. xxxCancelTrackingForThread(ptiCurrent);
  2361. }
  2362. /*
  2363. * Unlock the pmsd window.
  2364. */
  2365. if (ptiCurrent->pmsd != NULL) {
  2366. Unlock(&ptiCurrent->pmsd->spwnd);
  2367. UserFreePool(ptiCurrent->pmsd);
  2368. ptiCurrent->pmsd = NULL;
  2369. }
  2370. /*
  2371. * Free the clipboard if owned by this thread.
  2372. */
  2373. {
  2374. PWINDOWSTATION pwinsta;
  2375. pwinsta = _GetProcessWindowStation(NULL);
  2376. if (pwinsta != NULL) {
  2377. if (pwinsta->ptiClipLock == ptiCurrent) {
  2378. xxxCloseClipboard(pwinsta);
  2379. }
  2380. if (pwinsta->ptiDrawingClipboard == ptiCurrent) {
  2381. pwinsta->ptiDrawingClipboard = NULL;
  2382. }
  2383. }
  2384. }
  2385. /*
  2386. * Unlock all the objects stored in the menustate structure.
  2387. */
  2388. while (ptiCurrent->pMenuState != NULL) {
  2389. PMENUSTATE pMenuState;
  2390. PPOPUPMENU ppopupmenuRoot;
  2391. pMenuState = ptiCurrent->pMenuState;
  2392. ppopupmenuRoot = pMenuState->pGlobalPopupMenu;
  2393. /*
  2394. * If menu mode was running on this thread
  2395. */
  2396. if (ptiCurrent == pMenuState->ptiMenuStateOwner) {
  2397. /*
  2398. * The menu's going away, so anyone who's locked it
  2399. * is SOL anyway. Windows NT Bug #375467.
  2400. */
  2401. pMenuState->dwLockCount = 0;
  2402. /*
  2403. * Close this menu.
  2404. */
  2405. if (pMenuState->fModelessMenu) {
  2406. xxxEndMenuLoop(pMenuState, ppopupmenuRoot);
  2407. xxxMNEndMenuState(TRUE);
  2408. } else {
  2409. pMenuState->fInsideMenuLoop = FALSE;
  2410. ptiCurrent->pq->QF_flags &= ~QF_CAPTURELOCKED;
  2411. xxxMNCloseHierarchy(ppopupmenuRoot, pMenuState);
  2412. xxxMNEndMenuState(ppopupmenuRoot->fIsMenuBar || ppopupmenuRoot->fDestroyed);
  2413. }
  2414. } else {
  2415. /*
  2416. * Menu mode is running on another thread. This thread
  2417. * must own spwndNotify which is going away soon.
  2418. *
  2419. * When spwndNotify is destroyed, we will clean up pMenuState
  2420. * from this pti. So do nothing now as we'll need this
  2421. * pMenuState at that time.
  2422. */
  2423. UserAssert((ppopupmenuRoot->spwndNotify != NULL)
  2424. && (GETPTI(ppopupmenuRoot->spwndNotify) == ptiCurrent));
  2425. /*
  2426. * Nested menus are not supposed to involve multiple threads.
  2427. */
  2428. UserAssert(pMenuState->pmnsPrev == NULL);
  2429. break;
  2430. }
  2431. }
  2432. /*
  2433. * Unlock all the objects stored in the sbstate structure.
  2434. */
  2435. if (ptiCurrent->pSBTrack) {
  2436. Unlock(&ptiCurrent->pSBTrack->spwndSB);
  2437. Unlock(&ptiCurrent->pSBTrack->spwndSBNotify);
  2438. Unlock(&ptiCurrent->pSBTrack->spwndTrack);
  2439. UserFreePool(ptiCurrent->pSBTrack);
  2440. ptiCurrent->pSBTrack = NULL;
  2441. }
  2442. /*
  2443. * If this is the main input thread of this application, zero out
  2444. * that field.
  2445. */
  2446. if (ptiCurrent->ppi != NULL && ptiCurrent->ppi->ptiMainThread == ptiCurrent) {
  2447. ptiCurrent->ppi->ptiMainThread = NULL;
  2448. }
  2449. while (ptiCurrent->psiiList != NULL) {
  2450. xxxDestroyThreadDDEObject(ptiCurrent, ptiCurrent->psiiList);
  2451. }
  2452. if (ptiCurrent->TIF_flags & TIF_PALETTEAWARE) {
  2453. PWND pwnd;
  2454. TL tlpwnd;
  2455. UserAssert(ptiCurrent->rpdesk != NULL);
  2456. pwnd = ptiCurrent->rpdesk->pDeskInfo->spwnd;
  2457. if (pwnd) {
  2458. ThreadLock(pwnd, &tlpwnd);
  2459. xxxFlushPalette(pwnd);
  2460. ThreadUnlock(&tlpwnd);
  2461. }
  2462. }
  2463. /*
  2464. * If this is the last GUI thread for the process that made a temporary
  2465. * (fullscreen) mode change, restore the mode to what's in the registry.
  2466. */
  2467. if (FLastGuiThread(ptiCurrent)) {
  2468. if ((gppiFullscreen == ptiCurrent->ppi) && !gbMDEVDisabled) {
  2469. LONG Status = xxxUserChangeDisplaySettings(NULL, NULL, NULL, 0, 0, KernelMode);
  2470. if (gppiFullscreen == ptiCurrent->ppi) {
  2471. RIPMSG1(RIP_WARNING, "xxxUserChangeDisplaySettings failed with status 0x%x", Status);
  2472. }
  2473. }
  2474. }
  2475. #ifdef GENERIC_INPUT
  2476. /*
  2477. * If the raw input is specified in this process,
  2478. * destroy thread-related objects and requests.
  2479. * This has to be done before the window reparenting.
  2480. */
  2481. if (ptiCurrent->ppi && ptiCurrent->ppi->pHidTable) {
  2482. DestroyThreadHidObjects(ptiCurrent);
  2483. }
  2484. #endif
  2485. /**************************************************************************\
  2486. *
  2487. * CLEANING THREAD OBJECTS. AVOID CALLING BACK AFTER THIS POINT.
  2488. *
  2489. * New objects might be created while calling back and won't be cleaned up.
  2490. *
  2491. \**************************************************************************/
  2492. /*
  2493. * This thread might have some outstanding timers. Destroy them.
  2494. */
  2495. DestroyThreadsTimers(ptiCurrent);
  2496. /*
  2497. * Free any windows hooks this thread has created.
  2498. */
  2499. FreeThreadsWindowHooks();
  2500. /*
  2501. * Cleanup any switch window info for any window switch window
  2502. * belonging to this thread.
  2503. */
  2504. RemoveThreadSwitchWindowInfo(ptiCurrent);
  2505. /*
  2506. * Free any hwnd lists the thread was using
  2507. */
  2508. {
  2509. PBWL pbwl, pbwlNext;
  2510. for (pbwl = gpbwlList; pbwl != NULL;) {
  2511. pbwlNext = pbwl->pbwlNext;
  2512. if (pbwl->ptiOwner == ptiCurrent) {
  2513. FreeHwndList(pbwl);
  2514. }
  2515. pbwl = pbwlNext;
  2516. }
  2517. }
  2518. /*
  2519. * Destroy all the public objects created by this thread.
  2520. */
  2521. DestroyThreadsHotKeys();
  2522. DestroyThreadsObjects();
  2523. /*
  2524. * Free any synchronous Notifies pending for this thread and
  2525. * free any Win Event Hooks this thread created.
  2526. */
  2527. FreeThreadsWinEvents(ptiCurrent);
  2528. /*
  2529. * Unlock the keyboard layouts here.
  2530. */
  2531. Unlock(&ptiCurrent->spklActive);
  2532. /*
  2533. * Cleanup the global resources if this is the last GUI thread for this
  2534. * session.
  2535. */
  2536. if (gdwGuiThreads == 1) {
  2537. CleanupResources();
  2538. }
  2539. if (FLastGuiThread(ptiCurrent)) {
  2540. /*
  2541. * Check if this was a setup app.
  2542. */
  2543. if (ptiCurrent->ppi->W32PF_Flags & W32PF_SETUPAPP) {
  2544. PDESKTOPINFO pdeskinfo = GETDESKINFO(ptiCurrent);
  2545. if (pdeskinfo->spwndShell) {
  2546. _PostMessage(pdeskinfo->spwndShell, DTM_SETUPAPPRAN, 0, 0);
  2547. }
  2548. }
  2549. DestroyProcessesClasses(ptiCurrent->ppi);
  2550. ptiCurrent->ppi->W32PF_Flags &= ~(W32PF_CLASSESREGISTERED);
  2551. try {
  2552. LPBOOL lpClassesRegistered = ptiCurrent->pClientInfo->lpClassesRegistered;
  2553. if (lpClassesRegistered) {
  2554. ProbeForWrite(lpClassesRegistered, sizeof(BOOL), sizeof(DWORD));
  2555. *lpClassesRegistered = FALSE;
  2556. }
  2557. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  2558. }
  2559. DestroyProcessesObjects(ptiCurrent->ppi);
  2560. }
  2561. /*
  2562. * Unlock default input context.
  2563. */
  2564. Unlock(&ptiCurrent->spDefaultImc);
  2565. if (ptiCurrent->pq != NULL) {
  2566. /*
  2567. * Remove this thread's cursor count from the queue.
  2568. */
  2569. ptiCurrent->pq->iCursorLevel -= ptiCurrent->iCursorLevel;
  2570. /*
  2571. * Have to recalc queue ownership after this thread leaves if it is
  2572. * a member of a shared input queue.
  2573. */
  2574. if (ptiCurrent->pq->cThreads != 1) {
  2575. gpdeskRecalcQueueAttach = ptiCurrent->rpdesk;
  2576. /*
  2577. * Because we are in thread cleanup, we won't callback due to
  2578. * WinEvents (zzzSetFMouseMoved calls zzzUpdateCursorImage).
  2579. */
  2580. UserAssert(ptiCurrent->TIF_flags & TIF_INCLEANUP);
  2581. UserAssert(gbExitInProgress == FALSE);
  2582. zzzSetFMouseMoved();
  2583. }
  2584. }
  2585. /*
  2586. * Remove from the process' list, also.
  2587. */
  2588. ppti = &PpiCurrent()->ptiList;
  2589. if (*ppti != NULL) {
  2590. while (*ppti != ptiCurrent && (*ppti)->ptiSibling != NULL) {
  2591. ppti = &((*ppti)->ptiSibling);
  2592. }
  2593. if (*ppti == ptiCurrent) {
  2594. *ppti = ptiCurrent->ptiSibling;
  2595. ptiCurrent->ptiSibling = NULL;
  2596. }
  2597. }
  2598. {
  2599. PDESKTOP rpdesk;
  2600. PATTACHINFO *ppai;
  2601. /*
  2602. * Temporarily lock the desktop until the THREADINFO structure is
  2603. * freed. Note that locking a NULL ptiCurrent->rpdesk is OK. Use a
  2604. * normal lock instead of a thread lock because the lock must
  2605. * exist past the freeing of the ptiCurrent.
  2606. */
  2607. rpdesk = NULL;
  2608. LockDesktop(&rpdesk, ptiCurrent->rpdesk, LDL_FN_DESTROYTHREADINFO, (ULONG_PTR)PtiCurrent());
  2609. /*
  2610. * Cleanup SMS structures attached to this thread. Handles both
  2611. * pending send and receive messages. MUST make sure we do
  2612. * SendMsgCleanup AFTER window cleanup.
  2613. */
  2614. SendMsgCleanup(ptiCurrent);
  2615. /*
  2616. * Allow this thread to be swapped
  2617. */
  2618. if (ptiCurrent->cEnterCount) {
  2619. BOOLEAN bool;
  2620. RIPMSG1(RIP_WARNING,
  2621. "Thread exiting with stack locked. pti: 0x%p",
  2622. ptiCurrent);
  2623. bool = KeSetKernelStackSwapEnable(TRUE);
  2624. ptiCurrent->cEnterCount = 0;
  2625. UserAssert(!bool);
  2626. }
  2627. if (ptiCurrent->ppi != NULL) {
  2628. ptiCurrent->ppi->cThreads--;
  2629. UserAssert(ptiCurrent->ppi->cThreads >= 0);
  2630. }
  2631. /*
  2632. * If this thread is a win16 task, remove it from the scheduler.
  2633. */
  2634. if (ptiCurrent->TIF_flags & TIF_16BIT) {
  2635. if ((ptiCurrent->ptdb) && (ptiCurrent->ptdb->hTaskWow != 0)) {
  2636. _WOWCleanup(NULL, ptiCurrent->ptdb->hTaskWow);
  2637. }
  2638. DestroyTask(ptiCurrent->ppi, ptiCurrent);
  2639. }
  2640. if (ptiCurrent->hEventQueueClient != NULL) {
  2641. NTSTATUS Status;
  2642. Status = ProtectHandle(ptiCurrent->hEventQueueClient, *ExEventObjectType, FALSE);
  2643. if (NT_SUCCESS(Status)) {
  2644. ObCloseHandle(ptiCurrent->hEventQueueClient, UserMode);
  2645. } else {
  2646. RIPMSG1(RIP_WARNING, "xxxDestroyThreadInfo: failed to unprotect the hEventQueueClient handle, status=%08x", Status);
  2647. }
  2648. ptiCurrent->hEventQueueClient = NULL;
  2649. }
  2650. if (gspwndInternalCapture != NULL) {
  2651. if (GETPTI(gspwndInternalCapture) == ptiCurrent) {
  2652. Unlock(&gspwndInternalCapture);
  2653. }
  2654. }
  2655. /*
  2656. * Set gptiForeground to NULL if equal to this pti before exiting
  2657. * this routine.
  2658. */
  2659. if (gptiForeground == ptiCurrent) {
  2660. /*
  2661. * Post these (WEF_ASYNC), since we can't make callbacks from here.
  2662. */
  2663. xxxWindowEvent(EVENT_OBJECT_FOCUS, NULL, OBJID_CLIENT, INDEXID_CONTAINER, WEF_ASYNC);
  2664. xxxWindowEvent(EVENT_SYSTEM_FOREGROUND, NULL, OBJID_WINDOW, INDEXID_CONTAINER, WEF_ASYNC);
  2665. /*
  2666. * Call the Shell to ask it to activate its main window. This
  2667. * will be accomplished with a PostMessage() to itself, so the
  2668. * actual activation will take place later.
  2669. */
  2670. UserAssert(rpdesk != NULL);
  2671. if (rpdesk->pDeskInfo->spwndProgman) {
  2672. _PostMessage(rpdesk->pDeskInfo->spwndProgman, guiActivateShellWindow, 0, 0);
  2673. }
  2674. /*
  2675. * Set gptiForeground to NULL because we're destroying it.
  2676. */
  2677. SetForegroundThread(NULL);
  2678. /*
  2679. * If this thread is attached to gpqForeground AND it's the
  2680. * last thread in the queue, then zzzDestroyQueue will NULL out
  2681. * qpqForeground. Due to journalling attaching, gptiForegrouund
  2682. * is not always attached to gpqForeground. This is one reason
  2683. * why we no longer NULL out gpqForeground as stated in the old
  2684. * comment. The other reason is that there might be other threads
  2685. * in the foreground queue so there is no need to zap it. This was
  2686. * messing up MsTest (now called VisualTest)
  2687. * This is the old comment:
  2688. * "Since gpqForeground is derived from the foreground thread
  2689. * structure, set it to NULL as well, since there now is no
  2690. * foreground thread structure"
  2691. *
  2692. * qpqForeground = NULL;
  2693. */
  2694. }
  2695. /*
  2696. * If this thread got the last input event, pass ownership to another
  2697. * thread in this process or to the foreground thread.
  2698. */
  2699. if (ptiCurrent == glinp.ptiLastWoken) {
  2700. UserAssert(PpiCurrent() == ptiCurrent->ppi);
  2701. if (ptiCurrent->ppi->ptiList != NULL) {
  2702. UserAssert (ptiCurrent != ptiCurrent->ppi->ptiList);
  2703. glinp.ptiLastWoken = ptiCurrent->ppi->ptiList;
  2704. } else {
  2705. glinp.ptiLastWoken = gptiForeground;
  2706. }
  2707. }
  2708. /*
  2709. * Make sure none of the other global thread pointers are pointing to us.
  2710. */
  2711. if (gptiShutdownNotify == ptiCurrent) {
  2712. gptiShutdownNotify = NULL;
  2713. }
  2714. if (gptiTasklist == ptiCurrent) {
  2715. gptiTasklist = NULL;
  2716. }
  2717. if (gHardErrorHandler.pti == ptiCurrent) {
  2718. gHardErrorHandler.pti = NULL;
  2719. }
  2720. /*
  2721. * Might be called from xxxCreateThreadInfo before the queue is created
  2722. * so check for NULL queue. Lock the queues since this pti might be
  2723. * locked. They will be unlocked in UserDeleteW32Thread
  2724. */
  2725. if (ptiCurrent->pq != NULL) {
  2726. UserAssert(ptiCurrent->pq != ptiCurrent->pqAttach);
  2727. DestroyThreadsMessages(ptiCurrent->pq, ptiCurrent);
  2728. (ptiCurrent->pq->cLockCount)++;
  2729. zzzDestroyQueue(ptiCurrent->pq, ptiCurrent);
  2730. }
  2731. /*
  2732. * zzzReattachThreads shouldn't call back while using pqAttach
  2733. */
  2734. UserAssert(ptiCurrent->pqAttach == NULL);
  2735. /*
  2736. * Remove the pti from its pti list and reset the pointers.
  2737. */
  2738. if (ptiCurrent->rpdesk != NULL) {
  2739. RemoveEntryList(&ptiCurrent->PtiLink);
  2740. InitializeListHead(&ptiCurrent->PtiLink);
  2741. }
  2742. FreeMessageList(&ptiCurrent->mlPost);
  2743. /*
  2744. * Free any attachinfo structures pointing to this thread
  2745. */
  2746. ppai = &gpai;
  2747. while ((*ppai) != NULL) {
  2748. if ((*ppai)->pti1 == ptiCurrent || (*ppai)->pti2 == ptiCurrent) {
  2749. PATTACHINFO paiKill = *ppai;
  2750. *ppai = (*ppai)->paiNext;
  2751. UserFreePool((HLOCAL)paiKill);
  2752. } else {
  2753. ppai = &(*ppai)->paiNext;
  2754. }
  2755. }
  2756. /*
  2757. * Change ownership of any objects that didn't get freed (because they
  2758. * are locked or we have a bug and the object didn't get destroyed).
  2759. */
  2760. MarkThreadsObjects(ptiCurrent);
  2761. /*
  2762. * Free thread information visible from client
  2763. */
  2764. if (rpdesk && ptiCurrent->pcti != NULL && ptiCurrent->pcti != &(ptiCurrent->cti)) {
  2765. DesktopFree(rpdesk, ptiCurrent->pcti);
  2766. ptiCurrent->pcti = &(ptiCurrent->cti);
  2767. }
  2768. /*
  2769. * Free the client info for system threads.
  2770. */
  2771. if (ptiCurrent->TIF_flags & TIF_SYSTEMTHREAD && ptiCurrent->pClientInfo != NULL) {
  2772. UserFreePool(ptiCurrent->pClientInfo);
  2773. ptiCurrent->pClientInfo = NULL;
  2774. }
  2775. /*
  2776. * Unlock the temporary desktop lock. ptiCurrent->rpdesk is still locked
  2777. * and will be unlocked in UserDeleteW32Thread.
  2778. */
  2779. UnlockDesktop(&rpdesk, LDU_FN_DESTROYTHREADINFO, (ULONG_PTR)PtiCurrent());
  2780. }
  2781. /*
  2782. * One more thread died.
  2783. */
  2784. gdwGuiThreads--;
  2785. }
  2786. /***************************************************************************\
  2787. * CleanEventMessage
  2788. *
  2789. * This routine takes a message and destroys and event message related pieces,
  2790. * which may be allocated.
  2791. *
  2792. * 12-10-92 ScottLu Created.
  2793. \***************************************************************************/
  2794. VOID CleanEventMessage(
  2795. PQMSG pqmsg)
  2796. {
  2797. PASYNCSENDMSG pmsg;
  2798. /*
  2799. * Certain special messages on the INPUT queue have associated
  2800. * bits of memory that need to be freed.
  2801. */
  2802. switch (pqmsg->dwQEvent) {
  2803. case QEVENT_SETWINDOWPOS:
  2804. UserFreePool((PSMWP)pqmsg->msg.wParam);
  2805. break;
  2806. case QEVENT_UPDATEKEYSTATE:
  2807. UserFreePool((PBYTE)pqmsg->msg.wParam);
  2808. break;
  2809. case QEVENT_NOTIFYWINEVENT:
  2810. DestroyNotify((PNOTIFY)pqmsg->msg.lParam);
  2811. break;
  2812. case QEVENT_ASYNCSENDMSG:
  2813. pmsg = (PASYNCSENDMSG)pqmsg->msg.wParam;
  2814. UserDeleteAtom((ATOM)pmsg->lParam);
  2815. UserFreePool(pmsg);
  2816. break;
  2817. }
  2818. }
  2819. /***************************************************************************\
  2820. * FreeMessageList
  2821. *
  2822. * History:
  2823. * 02-27-91 mikeke Created.
  2824. * 11-03-92 scottlu Changed to work with MLIST structure.
  2825. \***************************************************************************/
  2826. VOID FreeMessageList(
  2827. PMLIST pml)
  2828. {
  2829. PQMSG pqmsg;
  2830. DebugValidateMLIST(pml);
  2831. while ((pqmsg = pml->pqmsgRead) != NULL) {
  2832. CleanEventMessage(pqmsg);
  2833. DelQEntry(pml, pqmsg);
  2834. }
  2835. DebugValidateMLIST(pml);
  2836. }
  2837. /***************************************************************************\
  2838. * DestroyThreadsMessages
  2839. *
  2840. * History:
  2841. * 02-21-96 jerrysh Created.
  2842. \***************************************************************************/
  2843. VOID DestroyThreadsMessages(
  2844. PQ pq,
  2845. PTHREADINFO pti)
  2846. {
  2847. PQMSG pqmsg;
  2848. PQMSG pqmsgNext;
  2849. DebugValidateMLIST(&pq->mlInput);
  2850. pqmsg = pq->mlInput.pqmsgRead;
  2851. while (pqmsg != NULL) {
  2852. pqmsgNext = pqmsg->pqmsgNext;
  2853. if (pqmsg->pti == pti) {
  2854. /*
  2855. * Make sure we don't leave any bogus references to this message
  2856. * lying around.
  2857. */
  2858. if (pq->idSysPeek == (ULONG_PTR)pqmsg) {
  2859. CheckPtiSysPeek(8, pq, 0);
  2860. pq->idSysPeek = 0;
  2861. }
  2862. CleanEventMessage(pqmsg);
  2863. DelQEntry(&pq->mlInput, pqmsg);
  2864. }
  2865. pqmsg = pqmsgNext;
  2866. }
  2867. DebugValidateMLIST(&pq->mlInput);
  2868. }
  2869. /***************************************************************************\
  2870. * InitQEntryLookaside
  2871. *
  2872. * Initializes the Q entry lookaside list. This improves Q entry locality
  2873. * by keeping Q entries in a single page
  2874. *
  2875. * 09-09-93 Markl Created.
  2876. \***************************************************************************/
  2877. NTSTATUS
  2878. InitQEntryLookaside(
  2879. VOID)
  2880. {
  2881. QEntryLookaside = Win32AllocPoolNonPagedNS(sizeof(PAGED_LOOKASIDE_LIST),
  2882. TAG_LOOKASIDE);
  2883. if (QEntryLookaside == NULL) {
  2884. return STATUS_NO_MEMORY;
  2885. }
  2886. ExInitializePagedLookasideList(QEntryLookaside,
  2887. NULL,
  2888. NULL,
  2889. gSessionPoolMask,
  2890. sizeof(QMSG),
  2891. TAG_QMSG,
  2892. 16);
  2893. QLookaside = Win32AllocPoolNonPagedNS(sizeof(PAGED_LOOKASIDE_LIST),
  2894. TAG_LOOKASIDE);
  2895. if (QLookaside == NULL) {
  2896. return STATUS_NO_MEMORY;
  2897. }
  2898. ExInitializePagedLookasideList(QLookaside,
  2899. NULL,
  2900. NULL,
  2901. gSessionPoolMask,
  2902. sizeof(Q),
  2903. TAG_Q,
  2904. 16);
  2905. return STATUS_SUCCESS;
  2906. }
  2907. /***************************************************************************\
  2908. * AllocQEntry
  2909. *
  2910. * Allocates a message on a message list. DelQEntry deletes a message
  2911. * on a message list.
  2912. *
  2913. * 10-22-92 ScottLu Created.
  2914. \***************************************************************************/
  2915. PQMSG AllocQEntry(
  2916. PMLIST pml)
  2917. {
  2918. PQMSG pqmsg;
  2919. DebugValidateMLIST(pml);
  2920. if (pml->cMsgs >= gUserPostMessageLimit) {
  2921. RIPERR3(ERROR_NOT_ENOUGH_QUOTA, RIP_VERBOSE, "AllocQEntry: # of post messages exceeds the limit(%d) in pti=%#p, pml=%#p",
  2922. gUserPostMessageLimit, W32GetCurrentThread(), pml);
  2923. return NULL;
  2924. }
  2925. /*
  2926. * Allocate a Q message structure.
  2927. */
  2928. if ((pqmsg = ExAllocateFromPagedLookasideList(QEntryLookaside)) == NULL) {
  2929. RIPERR0(ERROR_NOT_ENOUGH_MEMORY, RIP_VERBOSE, "AllocQEntry: allocation failed, not enough memory");
  2930. return NULL;
  2931. }
  2932. RtlZeroMemory(pqmsg, sizeof(*pqmsg));
  2933. if (pml->pqmsgWriteLast != NULL) {
  2934. pml->pqmsgWriteLast->pqmsgNext = pqmsg;
  2935. pqmsg->pqmsgPrev = pml->pqmsgWriteLast;
  2936. pml->pqmsgWriteLast = pqmsg;
  2937. } else {
  2938. pml->pqmsgWriteLast = pml->pqmsgRead = pqmsg;
  2939. }
  2940. pml->cMsgs++;
  2941. DebugValidateMLISTandQMSG(pml, pqmsg);
  2942. return pqmsg;
  2943. }
  2944. /***************************************************************************\
  2945. * DelQEntry
  2946. *
  2947. * Simply removes a message from a message queue list.
  2948. *
  2949. * 10-20-92 ScottLu Created.
  2950. \***************************************************************************/
  2951. VOID DelQEntry(
  2952. PMLIST pml,
  2953. PQMSG pqmsg)
  2954. {
  2955. DebugValidateMLISTandQMSG(pml, pqmsg);
  2956. UserAssert((int)pml->cMsgs > 0);
  2957. UserAssert(pml->pqmsgRead);
  2958. UserAssert(pml->pqmsgWriteLast);
  2959. /*
  2960. * Unlink this pqmsg from the message list.
  2961. */
  2962. if (pqmsg->pqmsgPrev != NULL)
  2963. pqmsg->pqmsgPrev->pqmsgNext = pqmsg->pqmsgNext;
  2964. if (pqmsg->pqmsgNext != NULL)
  2965. pqmsg->pqmsgNext->pqmsgPrev = pqmsg->pqmsgPrev;
  2966. /*
  2967. * Update the read/write pointers if necessary.
  2968. */
  2969. if (pml->pqmsgRead == pqmsg)
  2970. pml->pqmsgRead = pqmsg->pqmsgNext;
  2971. if (pml->pqmsgWriteLast == pqmsg)
  2972. pml->pqmsgWriteLast = pqmsg->pqmsgPrev;
  2973. /*
  2974. * Adjust the message count and free the message structure.
  2975. */
  2976. pml->cMsgs--;
  2977. ExFreeToPagedLookasideList(QEntryLookaside, pqmsg);
  2978. DebugValidateMLIST(pml);
  2979. }
  2980. /***************************************************************************\
  2981. * CheckRemoveHotkeyBit
  2982. *
  2983. * We have a special bit for the WM_HOTKEY message - QS_HOTKEY. When there
  2984. * is a WM_HOTKEY message in the queue, that bit is on. When there isn't,
  2985. * that bit is off. This checks for more than one hot key, because the one
  2986. * is about to be deleted. If there is only one, the hot key bits are cleared.
  2987. *
  2988. * 11-12-92 ScottLu Created.
  2989. \***************************************************************************/
  2990. VOID CheckRemoveHotkeyBit(
  2991. PTHREADINFO pti,
  2992. PMLIST pml)
  2993. {
  2994. PQMSG pqmsg;
  2995. DWORD cHotkeys;
  2996. /*
  2997. * Remove the QS_HOTKEY bit if there is only one WM_HOTKEY message
  2998. * in this message list.
  2999. */
  3000. cHotkeys = 0;
  3001. for (pqmsg = pml->pqmsgRead; pqmsg != NULL; pqmsg = pqmsg->pqmsgNext) {
  3002. if (pqmsg->msg.message == WM_HOTKEY)
  3003. cHotkeys++;
  3004. }
  3005. /*
  3006. * If there is 1 or fewer hot keys, remove the hotkey bits.
  3007. */
  3008. if (cHotkeys <= 1) {
  3009. pti->pcti->fsWakeBits &= ~QS_HOTKEY;
  3010. pti->pcti->fsChangeBits &= ~QS_HOTKEY;
  3011. }
  3012. }
  3013. /***************************************************************************\
  3014. * FindQMsg
  3015. *
  3016. * Finds a qmsg that fits the filters by looping through the message list.
  3017. *
  3018. * 10-20-92 ScottLu Created.
  3019. * 06-06-97 CLupu added processing for WM_DDE_ACK messages
  3020. \***************************************************************************/
  3021. PQMSG FindQMsg(
  3022. PTHREADINFO pti,
  3023. PMLIST pml,
  3024. PWND pwndFilter,
  3025. UINT msgMin,
  3026. UINT msgMax,
  3027. BOOL bProcessAck)
  3028. {
  3029. PWND pwnd;
  3030. PQMSG pqmsgRead;
  3031. PQMSG pqmsgRet = NULL;
  3032. UINT message;
  3033. DebugValidateMLIST(pml);
  3034. pqmsgRead = pml->pqmsgRead;
  3035. while (pqmsgRead != NULL) {
  3036. /*
  3037. * Make sure this window is valid and doesn't have the destroy
  3038. * bit set (don't want to send it to any client side window procs
  3039. * if destroy window has been called on it).
  3040. */
  3041. pwnd = RevalidateHwnd(pqmsgRead->msg.hwnd);
  3042. if (pwnd == NULL && pqmsgRead->msg.hwnd != NULL) {
  3043. /*
  3044. * If we're removing a WM_HOTKEY message, we may need to
  3045. * clear the QS_HOTKEY bit, since we have a special bit
  3046. * for that message.
  3047. */
  3048. if (pqmsgRead->msg.message == WM_HOTKEY) {
  3049. CheckRemoveHotkeyBit(pti, pml);
  3050. }
  3051. /*
  3052. * If the current thread's queue is locked waiting for this message,
  3053. * we have to unlock it because we're eating the message. If there's
  3054. * no more input/messages for this thread, the thread is going to
  3055. * sleep; hence there might not be a next Get/PeekMessage call to
  3056. * unlock the queue (ie, updating pti->idLast is not enough);
  3057. * so we must unlock it now.
  3058. * Win95 doesn't have this problem because their FindQMsg doesn't
  3059. * eat messages; they call ReadPostMessage from FreeWindow
  3060. * to take care of this scenario (== message for a destroyed window).
  3061. * We could also do this if we have some problems with this fix.
  3062. */
  3063. if ((pti->pq->idSysLock == (ULONG_PTR)pqmsgRead)
  3064. && (pti->pq->ptiSysLock == pti)) {
  3065. /* CheckSysLock(What number?, pti->pq, NULL); */
  3066. RIPMSG2(RIP_VERBOSE, "FindQMsg: Unlocking queue:%#p. Msg:%#lx",
  3067. pti->pq, pqmsgRead->msg.message);
  3068. pti->pq->ptiSysLock = NULL;
  3069. }
  3070. DelQEntry(pml, pqmsgRead);
  3071. goto nextMsgFromPml;
  3072. }
  3073. /*
  3074. * Process the WM_DDE_ACK messages if bProcessAck is set.
  3075. */
  3076. if (bProcessAck && (PtoH(pwndFilter) == pqmsgRead->msg.hwnd) &&
  3077. (pqmsgRead->msg.message == (WM_DDE_ACK | MSGFLAG_DDE_MID_THUNK))) {
  3078. PXSTATE pxs;
  3079. pxs = (PXSTATE)HMValidateHandleNoRip((HANDLE)pqmsgRead->msg.lParam, TYPE_DDEXACT);
  3080. if (pxs != NULL && (pxs->flags & XS_FREEPXS)) {
  3081. FreeDdeXact(pxs);
  3082. DelQEntry(pml, pqmsgRead);
  3083. goto nextMsgFromPml;
  3084. }
  3085. }
  3086. /*
  3087. * Make sure this message fits both window handle and message
  3088. * filters.
  3089. */
  3090. if (!CheckPwndFilter(pwnd, pwndFilter))
  3091. goto nextMsg;
  3092. /*
  3093. * If this is a fixed up dde message, then turn it into a normal
  3094. * dde message for the sake of message filtering.
  3095. */
  3096. message = pqmsgRead->msg.message;
  3097. if (CheckMsgFilter(message,
  3098. (WM_DDE_FIRST + 1) | MSGFLAG_DDE_MID_THUNK,
  3099. WM_DDE_LAST | MSGFLAG_DDE_MID_THUNK)) {
  3100. message = message & ~MSGFLAG_DDE_MID_THUNK;
  3101. }
  3102. if (!CheckMsgFilter(message, msgMin, msgMax))
  3103. goto nextMsg;
  3104. /*
  3105. * Found it. If bProcessAck is set, remember this pointer and go on
  3106. * till we finish walking the list to process all WM_DDE_ACK messages.
  3107. */
  3108. if (!bProcessAck) {
  3109. DebugValidateMLIST(pml);
  3110. return pqmsgRead;
  3111. }
  3112. if (pqmsgRet == NULL) {
  3113. pqmsgRet = pqmsgRead;
  3114. }
  3115. nextMsg:
  3116. pqmsgRead = pqmsgRead->pqmsgNext;
  3117. continue;
  3118. nextMsgFromPml:
  3119. pqmsgRead = pml->pqmsgRead;
  3120. continue;
  3121. }
  3122. DebugValidateMLIST(pml);
  3123. return pqmsgRet;
  3124. }
  3125. /***************************************************************************\
  3126. * CheckQuitMessage
  3127. *
  3128. * Checks to see if a WM_QUIT message should be generated.
  3129. *
  3130. * 11-06-92 ScottLu Created.
  3131. \***************************************************************************/
  3132. BOOL CheckQuitMessage(
  3133. PTHREADINFO pti,
  3134. LPMSG lpMsg,
  3135. BOOL fRemoveMsg)
  3136. {
  3137. /*
  3138. * If there are no more posted messages in the queue and the app
  3139. * has already called PostQuitMessage, then generate a quit.
  3140. */
  3141. if ((pti->TIF_flags & TIF_QUITPOSTED) && pti->mlPost.cMsgs == 0) {
  3142. /*
  3143. * If we're "removing" the quit, clear TIF_QUITPOSTED so another one
  3144. * isn't generated.
  3145. */
  3146. if (fRemoveMsg) {
  3147. pti->TIF_flags &= ~TIF_QUITPOSTED;
  3148. }
  3149. StoreMessage(lpMsg, NULL, WM_QUIT, (DWORD)pti->exitCode, 0, 0);
  3150. return TRUE;
  3151. }
  3152. return FALSE;
  3153. }
  3154. /***************************************************************************\
  3155. * ReadPostMessage
  3156. *
  3157. * If queue is not empty, read message satisfying filter conditions from
  3158. * this queue to *lpMsg.
  3159. *
  3160. * 10-19-92 ScottLu Created.
  3161. \***************************************************************************/
  3162. BOOL xxxReadPostMessage(
  3163. PTHREADINFO pti,
  3164. LPMSG lpMsg,
  3165. PWND pwndFilter,
  3166. UINT msgMin,
  3167. UINT msgMax,
  3168. BOOL fRemoveMsg)
  3169. {
  3170. PQMSG pqmsg;
  3171. PMLIST pmlPost;
  3172. /*
  3173. * Check to see if it is time to generate a quit message.
  3174. */
  3175. if (CheckQuitMessage(pti, lpMsg, fRemoveMsg)) {
  3176. return TRUE;
  3177. }
  3178. /*
  3179. * Loop through the messages in this list looking for the one that
  3180. * fits the passed in filters.
  3181. */
  3182. pmlPost = &pti->mlPost;
  3183. pqmsg = FindQMsg(pti, pmlPost, pwndFilter, msgMin, msgMax, FALSE);
  3184. if (pqmsg == NULL) {
  3185. /*
  3186. * Check again for quit... FindQMsg deletes some messages
  3187. * in some instances, so we may match the conditions
  3188. * for quit generation here.
  3189. */
  3190. if (CheckQuitMessage(pti, lpMsg, fRemoveMsg)) {
  3191. return TRUE;
  3192. }
  3193. } else {
  3194. /*
  3195. * Update the thread info fields with the info from this qmsg.
  3196. */
  3197. pti->timeLast = pqmsg->msg.time;
  3198. if (!RtlEqualMemory(&pti->ptLast, &pqmsg->msg.pt, sizeof(POINT))) {
  3199. pti->TIF_flags |= TIF_MSGPOSCHANGED;
  3200. }
  3201. pti->ptLast = pqmsg->msg.pt;
  3202. pti->idLast = (ULONG_PTR)pqmsg;
  3203. pti->pq->ExtraInfo = pqmsg->ExtraInfo;
  3204. /*
  3205. * Are we supposed to yank out the message? If not, stick some
  3206. * random id into idLast so we don't unlock the input queue until we
  3207. * pull this message from the queue.
  3208. */
  3209. *lpMsg = pqmsg->msg;
  3210. if (!fRemoveMsg) {
  3211. pti->idLast = 1;
  3212. } else {
  3213. /*
  3214. * If we're removing a WM_HOTKEY message, we may need to
  3215. * clear the QS_HOTKEY bit, since we have a special bit
  3216. * for that message.
  3217. */
  3218. if (pmlPost->pqmsgRead->msg.message == WM_HOTKEY) {
  3219. CheckRemoveHotkeyBit(pti, pmlPost);
  3220. }
  3221. /*
  3222. * Since we're removing an event from the queue, we
  3223. * need to check priority. This resets the TIF_SPINNING
  3224. * since we're no longer spinning.
  3225. */
  3226. if (pti->TIF_flags & TIF_SPINNING) {
  3227. if (!NT_SUCCESS(CheckProcessForeground(pti))) {
  3228. return FALSE;
  3229. }
  3230. }
  3231. DelQEntry(pmlPost, pqmsg);
  3232. }
  3233. /*
  3234. * See if this is a dde message that needs to be fixed up.
  3235. */
  3236. if (CheckMsgFilter(lpMsg->message,
  3237. (WM_DDE_FIRST + 1) | MSGFLAG_DDE_MID_THUNK,
  3238. WM_DDE_LAST | MSGFLAG_DDE_MID_THUNK)) {
  3239. /*
  3240. * Fixup the message value.
  3241. */
  3242. lpMsg->message &= (UINT)~MSGFLAG_DDE_MID_THUNK;
  3243. /*
  3244. * Call back the client to allocate the dde data for this message.
  3245. */
  3246. xxxDDETrackGetMessageHook(lpMsg);
  3247. /*
  3248. * Copy these values back into the queue if this message hasn't
  3249. * been removed from the queue. Need to search through the
  3250. * queue again because the pqmsg may have been removed when
  3251. * we left the critical section above.
  3252. */
  3253. if (!fRemoveMsg) {
  3254. if (pqmsg == FindQMsg(pti, pmlPost, pwndFilter, msgMin, msgMax, FALSE)) {
  3255. pqmsg->msg = *lpMsg;
  3256. }
  3257. }
  3258. }
  3259. #if DBG
  3260. else if (CheckMsgFilter(lpMsg->message, WM_DDE_FIRST, WM_DDE_LAST)) {
  3261. if (fRemoveMsg) {
  3262. TraceDdeMsg(lpMsg->message, (HWND)lpMsg->wParam, lpMsg->hwnd, MSG_RECV);
  3263. } else {
  3264. TraceDdeMsg(lpMsg->message, (HWND)lpMsg->wParam, lpMsg->hwnd, MSG_PEEK);
  3265. }
  3266. }
  3267. #endif
  3268. }
  3269. /*
  3270. * If there are no posted messages available, clear the post message
  3271. * bit so we don't go looking for them again.
  3272. */
  3273. if (pmlPost->cMsgs == 0 && !(pti->TIF_flags & TIF_QUITPOSTED)) {
  3274. pti->pcti->fsWakeBits &= ~(QS_POSTMESSAGE | QS_ALLPOSTMESSAGE);
  3275. pti->pcti->fsChangeBits &= ~QS_ALLPOSTMESSAGE;
  3276. }
  3277. return pqmsg != NULL;
  3278. }
  3279. #ifdef HUNGAPP_GHOSTING
  3280. /***************************************************************************\
  3281. * xxxProcessHungThreadEvent
  3282. *
  3283. * We check when a thread gets unhung when it reads the posted queue message.
  3284. *
  3285. * 6-10-99 vadimg created
  3286. \***************************************************************************/
  3287. VOID xxxProcessHungThreadEvent(
  3288. PWND pwnd)
  3289. {
  3290. PTHREADINFO ptiCurrent = PtiCurrent();
  3291. PWND pwndGhost;
  3292. HWND hwnd, hwndGhost;
  3293. TL tlpwndT1, tlpwndT2;
  3294. BOOL fUnlockGhost = FALSE;
  3295. CheckLock(pwnd);
  3296. /*
  3297. * The app processed this queue message, so update time last read
  3298. * used for hung app calculations.
  3299. */
  3300. SET_TIME_LAST_READ(ptiCurrent);
  3301. pwndGhost = FindGhost(pwnd);
  3302. ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwndT1);
  3303. if (pwndGhost != NULL) {
  3304. ThreadLockAlwaysWithPti(ptiCurrent, pwndGhost, &tlpwndT2);
  3305. fUnlockGhost = TRUE;
  3306. /*
  3307. * Try to set the state of the hung window to the current state of
  3308. * the ghost window.
  3309. */
  3310. if (TestWF(pwndGhost, WFMAXIMIZED)) {
  3311. xxxMinMaximize(pwnd, SW_MAXIMIZE, MINMAX_KEEPHIDDEN);
  3312. } else if (TestWF(pwndGhost, WFMINIMIZED)) {
  3313. xxxMinMaximize(pwnd, SW_SHOWMINNOACTIVE, MINMAX_KEEPHIDDEN);
  3314. } else {
  3315. DWORD dwFlags;
  3316. PTHREADINFO pti = GETPTI(pwndGhost);
  3317. /*
  3318. * If the ghost is the active foreground window, allow this
  3319. * activation to bring the hung window to the foreground.
  3320. */
  3321. if (pti->pq == gpqForeground && pti->pq->spwndActive == pwndGhost) {
  3322. dwFlags = 0;
  3323. GETPTI(pwnd)->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE;
  3324. } else {
  3325. dwFlags = SWP_NOACTIVATE;
  3326. }
  3327. /*
  3328. * Unless the user explicitly moved or sized the ghost window,
  3329. * Don't copy it's location and size to the unhung window.
  3330. * See bug#s 415519 and 413418
  3331. */
  3332. if (!GhostSizedOrMoved(pwnd)) {
  3333. dwFlags |= (SWP_NOMOVE | SWP_NOSIZE) ;
  3334. }
  3335. /*
  3336. * This will appropriately zorder, activate, and position the
  3337. * hung window.
  3338. */
  3339. xxxSetWindowPos(pwnd, pwndGhost,
  3340. pwndGhost->rcWindow.left, pwndGhost->rcWindow.top,
  3341. pwndGhost->rcWindow.right - pwndGhost->rcWindow.left,
  3342. pwndGhost->rcWindow.bottom - pwndGhost->rcWindow.top,
  3343. dwFlags);
  3344. }
  3345. }
  3346. /*
  3347. * Toggle the visible bit of the hung window and remove the ghost window
  3348. * corresponding to this previously hung window.
  3349. */
  3350. if (TestWF(pwnd, WEFGHOSTMAKEVISIBLE)) {
  3351. SetVisible(pwnd, SV_SET);
  3352. }
  3353. RemoveGhost(pwnd);
  3354. /*
  3355. * Make the shell aware again of the hung window.
  3356. */
  3357. hwnd = PtoHq(pwnd);
  3358. hwndGhost = PtoH(pwndGhost);
  3359. PostShellHookMessages(HSHELL_WINDOWREPLACING, (LPARAM)hwnd);
  3360. PostShellHookMessages(HSHELL_WINDOWREPLACED, (LPARAM)hwndGhost);
  3361. xxxCallHook(HSHELL_WINDOWREPLACED, (WPARAM)hwndGhost, (LPARAM)hwnd, WH_SHELL);
  3362. /*
  3363. * Completely invalidate the hung window, since it became visible again.
  3364. */
  3365. if (TestWF(pwnd, WEFGHOSTMAKEVISIBLE)) {
  3366. xxxRedrawWindow(pwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE |
  3367. RDW_ALLCHILDREN | RDW_FRAME);
  3368. }
  3369. if (fUnlockGhost) {
  3370. ThreadUnlock(&tlpwndT2);
  3371. }
  3372. ThreadUnlock(&tlpwndT1);
  3373. }
  3374. #else // HUNGAPP_GHOSTING
  3375. VOID xxxProcessHungThreadEvent(
  3376. PWND pwnd)
  3377. {
  3378. CheckLock(pwnd);
  3379. if (TestWF(pwnd, WFVISIBLE)) {
  3380. RIPMSG0(RIP_WARNING, "xxxProcessHungThreadEvent: window is already visible");
  3381. } else {
  3382. SetVisible(pwnd, SV_SET);
  3383. if (TestWF(pwnd, WFMINIMIZED)) {
  3384. RIPMSG0(RIP_WARNING, "xxxProcessHungThreadEvent: window is already minmized");
  3385. } else {
  3386. xxxMinMaximize(pwnd, SW_SHOWMINNOACTIVE, MINMAX_KEEPHIDDEN);
  3387. }
  3388. }
  3389. }
  3390. #endif
  3391. #ifndef _WIN64
  3392. BEEPPROC pfnBP[] = {
  3393. UpSiren,
  3394. DownSiren,
  3395. LowBeep,
  3396. HighBeep,
  3397. KeyClick};
  3398. #endif
  3399. /***************************************************************************\
  3400. * xxxProcessEventMessage
  3401. *
  3402. * This handles our processing for 'event' messages. We return a BOOL here
  3403. * telling the system whether or not to continue processing messages.
  3404. *
  3405. * History:
  3406. * 06-17-91 DavidPe Created.
  3407. \***************************************************************************/
  3408. VOID xxxProcessEventMessage(
  3409. PTHREADINFO ptiCurrent,
  3410. PQMSG pqmsg)
  3411. {
  3412. PWND pwnd;
  3413. TL tlpwndT;
  3414. TL tlMsg;
  3415. PQ pq;
  3416. UserAssert(IsWinEventNotifyDeferredOK());
  3417. UserAssert(ptiCurrent == PtiCurrent());
  3418. ThreadLockPoolCleanup(ptiCurrent, pqmsg, &tlMsg, CleanEventMessage);
  3419. pq = ptiCurrent->pq;
  3420. switch (pqmsg->dwQEvent) {
  3421. case QEVENT_DESTROYWINDOW:
  3422. /*
  3423. * These events are posted from xxxDW_DestroyOwnedWindows
  3424. * for owned windows that are not owned by the owner
  3425. * window thread.
  3426. */
  3427. pwnd = RevalidateHwnd((HWND)pqmsg->msg.wParam);
  3428. if (pwnd != NULL) {
  3429. if (!TestWF(pwnd, WFCHILD)) {
  3430. xxxDestroyWindow(pwnd);
  3431. } else {
  3432. ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwndT);
  3433. xxxFreeWindow(pwnd, &tlpwndT);
  3434. }
  3435. }
  3436. break;
  3437. case QEVENT_SHOWWINDOW:
  3438. /*
  3439. * These events are mainly used from within CascadeChildWindows()
  3440. * and TileChildWindows() so that taskmgr doesn't hang while calling
  3441. * these apis if it is trying to tile or cascade a hung application.
  3442. */
  3443. /* The HIWORD of lParam now has the preserved state of gfAnimate at the
  3444. * time of the call.
  3445. */
  3446. pwnd = RevalidateHwnd((HWND)pqmsg->msg.wParam);
  3447. if (pwnd != NULL && !TestWF(pwnd, WFINDESTROY)) {
  3448. ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwndT);
  3449. xxxShowWindow(pwnd, (DWORD)pqmsg->msg.lParam);
  3450. /*
  3451. * If this is coming from an async SetWindowPlacement, update the
  3452. * check point settings if the window is minimized.
  3453. */
  3454. if ((pqmsg->msg.message & WPF_ASYNCWINDOWPLACEMENT)
  3455. && TestWF(pwnd, WFMINIMIZED)) {
  3456. WPUpdateCheckPointSettings(pwnd, (UINT)pqmsg->msg.message);
  3457. }
  3458. ThreadUnlock(&tlpwndT);
  3459. }
  3460. break;
  3461. case QEVENT_NOTIFYWINEVENT:
  3462. UserAssert(((PNOTIFY)pqmsg->msg.lParam)->dwWEFlags & WEF_POSTED);
  3463. UserAssert(((PNOTIFY)pqmsg->msg.lParam)->dwWEFlags & WEF_ASYNC);
  3464. xxxProcessNotifyWinEvent((PNOTIFY)pqmsg->msg.lParam);
  3465. break;
  3466. case QEVENT_SETWINDOWPOS:
  3467. /*
  3468. * QEVENT_SETWINDOWPOS events are generated when a thread calls
  3469. * SetWindowPos with a list of windows owned by threads other than
  3470. * itself. This way all WINDOWPOSing on a window is done the thread
  3471. * that owns (created) the window and we don't have any of those
  3472. * nasty inter-thread synchronization problems.
  3473. */
  3474. xxxProcessSetWindowPosEvent((PSMWP)pqmsg->msg.wParam);
  3475. break;
  3476. case QEVENT_UPDATEKEYSTATE:
  3477. /*
  3478. * Update the local key state with the state from those
  3479. * keys that have changed since the last time key state
  3480. * was synchronized.
  3481. */
  3482. ProcessUpdateKeyStateEvent(pq, (PBYTE)pqmsg->msg.wParam, (PBYTE)pqmsg->msg.wParam + CBKEYSTATE);
  3483. break;
  3484. case QEVENT_ACTIVATE:
  3485. {
  3486. if (pqmsg->msg.lParam == 0) {
  3487. /*
  3488. * Clear any visible tracking going on in system. We
  3489. * only bother to do this if lParam == 0 since
  3490. * xxxSetForegroundWindow2() deals with this in the
  3491. * other case.
  3492. */
  3493. xxxCancelTracking();
  3494. /*
  3495. * Remove the clip cursor rectangle - it is a global mode that
  3496. * gets removed when switching. Also remove any LockWindowUpdate()
  3497. * that's still around.
  3498. */
  3499. zzzClipCursor(NULL);
  3500. LockWindowUpdate2(NULL, TRUE);
  3501. /*
  3502. * Reload pq because it may have changed.
  3503. */
  3504. pq = ptiCurrent->pq;
  3505. /*
  3506. * If this event didn't originate from an initializing app
  3507. * coming to the foreground [wParam == 0] then go ahead
  3508. * and check if there's already an active window and if so make
  3509. * it visually active. Also make sure we're still the foreground
  3510. * queue.
  3511. */
  3512. if ((pqmsg->msg.wParam != 0) && (pq->spwndActive != NULL) &&
  3513. (pq == gpqForeground)) {
  3514. PWND pwndActive;
  3515. ThreadLockAlwaysWithPti(ptiCurrent, pwndActive = pq->spwndActive, &tlpwndT);
  3516. xxxSendMessage(pwndActive, WM_NCACTIVATE, TRUE, 0);
  3517. xxxUpdateTray(pwndActive);
  3518. xxxSetWindowPos(pwndActive, PWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
  3519. ThreadUnlock(&tlpwndT);
  3520. } else if (pq != gpqForeground) {
  3521. /*
  3522. * If we're not being activated, make sure we don't become foreground.
  3523. */
  3524. ptiCurrent->TIF_flags &= ~TIF_ALLOWFOREGROUNDACTIVATE;
  3525. TAGMSG1(DBGTAG_FOREGROUND, "xxxProcessEventMessage clear TIF %#p", ptiCurrent);
  3526. ptiCurrent->ppi->W32PF_Flags &= ~W32PF_ALLOWFOREGROUNDACTIVATE;
  3527. TAGMSG1(DBGTAG_FOREGROUND, "xxxProcessEventMessage clear W32PF %#p", ptiCurrent->ppi);
  3528. }
  3529. } else {
  3530. pwnd = RevalidateHwnd((HWND)pqmsg->msg.lParam);
  3531. if (pwnd == NULL)
  3532. break;
  3533. ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwndT);
  3534. /*
  3535. * If nobody is foreground, allow this app to become foreground.
  3536. */
  3537. if (gpqForeground == NULL) {
  3538. xxxSetForegroundWindow2(pwnd, ptiCurrent, 0);
  3539. } else {
  3540. if (pwnd != pq->spwndActive) {
  3541. if (xxxActivateThisWindow(pwnd, (UINT)pqmsg->msg.wParam,
  3542. (ATW_SETFOCUS | ATW_ASYNC) |
  3543. ((pqmsg->msg.message & PEM_ACTIVATE_NOZORDER) ? ATW_NOZORDER : 0))) {
  3544. /*
  3545. * This event was posted by SetForegroundWindow2
  3546. * (i.e. pqmsg->msg.lParam != 0) so make sure
  3547. * mouse is on this window.
  3548. */
  3549. if (TestUP(ACTIVEWINDOWTRACKING)) {
  3550. zzzActiveCursorTracking(pwnd);
  3551. }
  3552. }
  3553. } else {
  3554. BOOL fActive = (GETPTI(pwnd)->pq == gpqForeground);
  3555. xxxSendMessage(pwnd, WM_NCACTIVATE,
  3556. (DWORD)(fActive), 0);
  3557. if (fActive) {
  3558. xxxUpdateTray(pwnd);
  3559. }
  3560. /*
  3561. * Only bring the window to the top if it is becoming active.
  3562. */
  3563. if (fActive && !(pqmsg->msg.message & PEM_ACTIVATE_NOZORDER))
  3564. xxxSetWindowPos(pwnd, PWND_TOP, 0, 0, 0, 0,
  3565. SWP_NOSIZE | SWP_NOMOVE);
  3566. }
  3567. }
  3568. /*
  3569. * Check here to see if the window needs to be restored. This is a
  3570. * hack so that we're compatible with what msmail expects out of
  3571. * win3.1 alt-tab. msmail expects to always be active when it gets
  3572. * asked to be restored. This will ensure that during alt-tab
  3573. * activate.
  3574. */
  3575. if (pqmsg->msg.message & PEM_ACTIVATE_RESTORE) {
  3576. if (TestWF(pwnd, WFMINIMIZED)) {
  3577. _PostMessage(pwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
  3578. }
  3579. }
  3580. ThreadUnlock(&tlpwndT);
  3581. }
  3582. }
  3583. break;
  3584. case QEVENT_DEACTIVATE:
  3585. xxxDeactivate(ptiCurrent, (DWORD)pqmsg->msg.wParam);
  3586. break;
  3587. case QEVENT_CANCELMODE:
  3588. if (pq->spwndCapture != NULL) {
  3589. ThreadLockAlwaysWithPti(ptiCurrent, pq->spwndCapture, &tlpwndT);
  3590. xxxSendMessage(pq->spwndCapture, WM_CANCELMODE, 0, 0);
  3591. ThreadUnlock(&tlpwndT);
  3592. /*
  3593. * Set QS_MOUSEMOVE so any sleeping modal loops,
  3594. * like the move/size code, will wake up and figure
  3595. * out that it should abort.
  3596. */
  3597. SetWakeBit(ptiCurrent, QS_MOUSEMOVE);
  3598. }
  3599. break;
  3600. case QEVENT_POSTMESSAGE:
  3601. /*
  3602. * This event is used in situations where we need to ensure that posted
  3603. * messages are processed after previous QEVENT's. Normally, posting a
  3604. * queue event and then calling postmessage will result in the posted
  3605. * message being seen first by the app (because posted messages are
  3606. * processed before input.) Instead we will post a QEVENT_POSTMESSAGE
  3607. * instead of doing a postmessage directly, which will result in the
  3608. * correct ordering of messages.
  3609. *
  3610. */
  3611. if (pwnd = RevalidateHwnd((HWND)pqmsg->msg.hwnd)) {
  3612. _PostMessage(pwnd,pqmsg->msg.message,pqmsg->msg.wParam,pqmsg->msg.lParam);
  3613. }
  3614. break;
  3615. case QEVENT_ASYNCSENDMSG:
  3616. xxxProcessAsyncSendMessage((PASYNCSENDMSG)pqmsg->msg.wParam);
  3617. break;
  3618. case QEVENT_HUNGTHREAD:
  3619. pwnd = RevalidateHwnd((HWND)pqmsg->msg.hwnd);
  3620. if (pwnd != NULL) {
  3621. ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwndT);
  3622. xxxProcessHungThreadEvent(pwnd);
  3623. ThreadUnlock(&tlpwndT);
  3624. }
  3625. break;
  3626. case QEVENT_CANCELMOUSEMOVETRK: {
  3627. /*
  3628. * hwnd: hwndTrack. message: dwDTFlags.
  3629. * wParam: htEx. lParam: dwDTCancel
  3630. */
  3631. PDESKTOP pdesk = ptiCurrent->rpdesk;
  3632. pwnd = RevalidateHwnd((HWND)pqmsg->msg.hwnd);
  3633. /*
  3634. * Let's check that the app didn't manage to restart mouse leave
  3635. * tracking before we had a chance to cancel it.
  3636. */
  3637. UserAssert(!(pqmsg->msg.message & DF_TRACKMOUSELEAVE)
  3638. || !(pdesk->dwDTFlags & DF_TRACKMOUSELEAVE)
  3639. || (PtoHq(pdesk->spwndTrack) != pqmsg->msg.hwnd)
  3640. || !((pdesk->htEx == HTCLIENT) ^ ((int)pqmsg->msg.wParam == HTCLIENT)));
  3641. /*
  3642. * If we're back tracking at the same spot, bail
  3643. */
  3644. if ((pdesk->dwDTFlags & DF_MOUSEMOVETRK)
  3645. && (PtoHq(pdesk->spwndTrack) == pqmsg->msg.hwnd)
  3646. && (pdesk->htEx == (int)pqmsg->msg.wParam)) {
  3647. /*
  3648. * If we're tracking mouse leave,
  3649. */
  3650. break;
  3651. }
  3652. /*
  3653. * Don't nuke the tooltip if it has been reactivated.
  3654. */
  3655. if (pdesk->dwDTFlags & DF_TOOLTIPACTIVE) {
  3656. pqmsg->msg.lParam &= ~DF_TOOLTIP;
  3657. }
  3658. /*
  3659. * Cancel tracking if the window is still around
  3660. */
  3661. if (pwnd != NULL) {
  3662. ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwndT);
  3663. xxxCancelMouseMoveTracking(pqmsg->msg.message, pwnd,
  3664. (int)pqmsg->msg.wParam,
  3665. (DWORD)pqmsg->msg.lParam);
  3666. ThreadUnlock(&tlpwndT);
  3667. } else if ((pqmsg->msg.lParam & DF_TOOLTIP)
  3668. && (pqmsg->msg.message & DF_TOOLTIPSHOWING)) {
  3669. /*
  3670. * The window is gone and so must be tracking.
  3671. * Just take care of the tooltip which is still showing.
  3672. */
  3673. pwnd = pdesk->spwndTooltip;
  3674. ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwndT);
  3675. xxxResetTooltip((PTOOLTIPWND)pwnd);
  3676. ThreadUnlock(&tlpwndT);
  3677. }
  3678. }
  3679. break;
  3680. case QEVENT_RITACCESSIBILITY:
  3681. if (IsHooked(ptiCurrent, WHF_SHELL)) {
  3682. xxxCallHook((UINT)pqmsg->msg.wParam,
  3683. (WPARAM)pqmsg->msg.lParam,
  3684. (LPARAM)0,
  3685. WH_SHELL);
  3686. }
  3687. PostShellHookMessages((UINT)pqmsg->msg.wParam, pqmsg->msg.lParam);
  3688. break;
  3689. case QEVENT_RITSOUND:
  3690. /*
  3691. * This should only happen on the desktop thread.
  3692. */
  3693. #ifndef _WIN64
  3694. switch(pqmsg->msg.message) {
  3695. case RITSOUND_UPSIREN:
  3696. case RITSOUND_DOWNSIREN:
  3697. case RITSOUND_LOWBEEP:
  3698. case RITSOUND_HIGHBEEP:
  3699. case RITSOUND_KEYCLICK:
  3700. (pfnBP[pqmsg->msg.message])();
  3701. break;
  3702. case RITSOUND_DOBEEP:
  3703. switch(pqmsg->msg.wParam) {
  3704. case RITSOUND_UPSIREN:
  3705. case RITSOUND_DOWNSIREN:
  3706. case RITSOUND_LOWBEEP:
  3707. case RITSOUND_HIGHBEEP:
  3708. case RITSOUND_KEYCLICK:
  3709. DoBeep(pfnBP[pqmsg->msg.wParam], (DWORD)pqmsg->msg.lParam);
  3710. }
  3711. break;
  3712. }
  3713. #else
  3714. {
  3715. UINT uCount;
  3716. UINT uSound;
  3717. /*
  3718. * The above code uses UserBeep() - which doesn't do anything
  3719. * useful on Win64. (IA64 spec doesn't include a h/w speaker.)
  3720. *
  3721. * Instead, we post a message to WinLogon to ask it to play a sound
  3722. * for us using the PlaySound.
  3723. */
  3724. /*
  3725. * RITSOUND_DOBEEP is used to repeat a sound n times. It is currently
  3726. * only used to repeat UPSIREN 2 or 3 times, for stickykeys, when the
  3727. * right shift is held down for 12 and 16 seconds.
  3728. */
  3729. if (pqmsg->msg.message == RITSOUND_DOBEEP) {
  3730. uSound = (UINT) pqmsg->msg.wParam;
  3731. uCount = (UINT) pqmsg->msg.lParam;
  3732. if (uCount > 5) {
  3733. uCount = 5;
  3734. }
  3735. } else {
  3736. uSound = pqmsg->msg.message;
  3737. uCount = 1;
  3738. }
  3739. if (gspwndLogonNotify != NULL) {
  3740. while(uCount--) {
  3741. _PostMessage(gspwndLogonNotify, WM_LOGONNOTIFY,
  3742. LOGON_PLAYEVENTSOUND, MAKELPARAM(uSound, ACCESS_SOUND_RANGE));
  3743. }
  3744. }
  3745. /*
  3746. * Since PlaySound doesn't currently flash the window if SoundSentry
  3747. * is active, we have to do it ourselces here.
  3748. * (On 32-bit, UserBeep() takes care of doing that.)
  3749. */
  3750. _UserSoundSentryWorker();
  3751. }
  3752. #endif
  3753. break;
  3754. case QEVENT_APPCOMMAND: {
  3755. /*
  3756. * qevent app commands so we can post a wm_appcommand to the queue
  3757. */
  3758. THREADINFO *ptiWindowOwner;
  3759. int cmd;
  3760. UINT keystate;
  3761. /*
  3762. * Check the appcommand's are within reasonable ranges. If they aren't
  3763. * then we have an internal consistency error since xxxKeyEvent should
  3764. * have generated correct ones for us.
  3765. */
  3766. UserAssert(pqmsg->msg.lParam >= VK_APPCOMMAND_FIRST &&
  3767. pqmsg->msg.lParam <= VK_APPCOMMAND_LAST);
  3768. /*
  3769. * We need to work out which window to send to here. Using the same
  3770. * rules as from xxxScanSysQueue:
  3771. * Assign the input to the focus window. If there is no focus
  3772. * window, assign it to the active window as a SYS message.
  3773. */
  3774. pwnd = ptiCurrent->pq->spwndFocus;
  3775. if (!pwnd) {
  3776. pwnd = ptiCurrent->pq->spwndActive;
  3777. if (!pwnd) {
  3778. /*
  3779. * At the moment we will just eat the message since we can't find a foreground q
  3780. * This follows the method that any other app (eg hidserv) would mimic to
  3781. * find the window to send to.
  3782. */
  3783. break;
  3784. }
  3785. }
  3786. /*
  3787. * We don't want to block on another thread since the xxxSendMessage is a synchronous call
  3788. * so we post the message to the queue of the window owner thread
  3789. */
  3790. ptiWindowOwner = GETPTI(pwnd);
  3791. if (ptiCurrent != ptiWindowOwner) {
  3792. /*
  3793. * Post the event message to the window who should get it
  3794. */
  3795. PostEventMessage(ptiWindowOwner, ptiWindowOwner->pq, QEVENT_APPCOMMAND,
  3796. NULL, 0, (WPARAM)0, pqmsg->msg.lParam);
  3797. /*
  3798. * Break out of this since we've now posted the message to a
  3799. * different q - we don't want to deal with it here.
  3800. */
  3801. break;
  3802. }
  3803. cmd = APPCOMMAND_FIRST + ((UINT)pqmsg->msg.lParam - VK_APPCOMMAND_FIRST);
  3804. keystate = GetMouseKeyFlags(ptiWindowOwner->pq);
  3805. pqmsg->msg.lParam = MAKELPARAM(keystate, cmd);
  3806. /*
  3807. * Generate a WM_APPCOMMAND message from the keyboard keys.
  3808. */
  3809. ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwndT);
  3810. xxxSendMessage(pwnd, WM_APPCOMMAND, (WPARAM)HWq(pwnd), pqmsg->msg.lParam);
  3811. ThreadUnlock(&tlpwndT);
  3812. break;
  3813. }
  3814. default:
  3815. RIPMSG1(RIP_ERROR,
  3816. "xxxProcessEventMessage: Bad dwQEvent: 0x%x",
  3817. pqmsg->dwQEvent);
  3818. break;
  3819. }
  3820. ThreadUnlockPoolCleanup(ptiCurrent, &tlMsg);
  3821. }
  3822. #define QS_TEST_AND_CLEAR (QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_SENDMESSAGE)
  3823. #define QS_TEST (QS_MOUSEBUTTON | QS_KEY)
  3824. /***************************************************************************\
  3825. * _GetInputState (API)
  3826. *
  3827. * Returns the current input state for mouse buttons or keys.
  3828. *
  3829. * History:
  3830. * 11-06-90 DavidPe Created.
  3831. \***************************************************************************/
  3832. BOOL _GetInputState(
  3833. VOID)
  3834. {
  3835. if (LOWORD(_GetQueueStatus(QS_TEST_AND_CLEAR)) & QS_TEST) {
  3836. return TRUE;
  3837. } else {
  3838. return FALSE;
  3839. }
  3840. }
  3841. #undef QS_TEST_AND_CLEAR
  3842. #undef QS_TEST
  3843. /***************************************************************************\
  3844. * _GetQueueStatus (API)
  3845. *
  3846. * Returns the changebits in the lo-word and wakebits in
  3847. * the hi-word for the current queue.
  3848. *
  3849. * History:
  3850. * 12-17-90 DavidPe Created.
  3851. \***************************************************************************/
  3852. DWORD _GetQueueStatus(
  3853. UINT flags)
  3854. {
  3855. PTHREADINFO ptiCurrent;
  3856. UINT fsChangeBits;
  3857. ptiCurrent = PtiCurrentShared();
  3858. flags &= (QS_ALLINPUT | QS_ALLPOSTMESSAGE | QS_TRANSFER);
  3859. fsChangeBits = ptiCurrent->pcti->fsChangeBits;
  3860. /*
  3861. * Clear out the change bits the app is looking at
  3862. * so it'll know what changed since it's last call
  3863. * to GetQueueStatus().
  3864. */
  3865. ptiCurrent->pcti->fsChangeBits &= ~flags;
  3866. /*
  3867. * Return the current change/wake-bits.
  3868. */
  3869. return MAKELONG(fsChangeBits & flags,
  3870. (ptiCurrent->pcti->fsWakeBits | ptiCurrent->pcti->fsWakeBitsJournal) & flags);
  3871. }
  3872. /***************************************************************************\
  3873. * xxxMsgWaitForMultipleObjects (API)
  3874. *
  3875. * Blocks until an 'event' satisifying dwWakeMask occurs for the
  3876. * current thread as well as all other objects specified by the other
  3877. * parameters which are the same as the base call WaitForMultipleObjects().
  3878. *
  3879. * pfnNonMsg indicates that pHandles is big enough for nCount+1 handles
  3880. * (empty slot at end, and to call pfnNonMsg for non message events).
  3881. *
  3882. * History:
  3883. * 12-17-90 DavidPe Created.
  3884. \***************************************************************************/
  3885. #ifdef LOCK_MOUSE_CODE
  3886. #pragma alloc_text(MOUSE, xxxMsgWaitForMultipleObjects)
  3887. #endif
  3888. DWORD xxxMsgWaitForMultipleObjects(
  3889. DWORD nCount,
  3890. PVOID *apObjects,
  3891. MSGWAITCALLBACK pfnNonMsg,
  3892. PKWAIT_BLOCK WaitBlockArray)
  3893. {
  3894. PTHREADINFO ptiCurrent = PtiCurrent();
  3895. NTSTATUS Status;
  3896. ptiCurrent = PtiCurrent();
  3897. UserAssert(IsWinEventNotifyDeferredOK());
  3898. /*
  3899. * Setup the wake mask for this thread. Wait for QS_EVENT or the app won't
  3900. * get event messages like deactivate.
  3901. */
  3902. ClearQueueServerEvent(QS_ALLINPUT | QS_EVENT);
  3903. /*
  3904. * Stuff the event handle for the current queue at the end.
  3905. */
  3906. apObjects[nCount] = ptiCurrent->pEventQueueServer;
  3907. /*
  3908. * Check to see if any input came inbetween when we
  3909. * last checked and the NtClearEvent() call.
  3910. */
  3911. if (!(ptiCurrent->pcti->fsChangeBits & QS_ALLINPUT)) {
  3912. /*
  3913. * This app is going idle. Clear the spin count check to see
  3914. * if we need to make this process foreground again.
  3915. */
  3916. if (ptiCurrent->TIF_flags & TIF_SPINNING) {
  3917. if (!NT_SUCCESS(Status = CheckProcessForeground(ptiCurrent))) {
  3918. return Status;
  3919. }
  3920. }
  3921. try {
  3922. ptiCurrent->pClientInfo->cSpins = 0;
  3923. } except (W32ExceptionHandler(TRUE, RIP_WARNING)) {
  3924. return GetExceptionCode();
  3925. }
  3926. if (ptiCurrent == gptiForeground &&
  3927. IsHooked(ptiCurrent, WHF_FOREGROUNDIDLE)) {
  3928. xxxCallHook(HC_ACTION, 0, 0, WH_FOREGROUNDIDLE);
  3929. }
  3930. /*
  3931. * Set the input idle event to wake up any threads waiting
  3932. * for this thread to go into idle state.
  3933. */
  3934. zzzWakeInputIdle(ptiCurrent);
  3935. Again:
  3936. LeaveCrit();
  3937. Status = KeWaitForMultipleObjects(nCount + 1, apObjects,
  3938. WaitAny, WrUserRequest,
  3939. UserMode, FALSE,
  3940. NULL, WaitBlockArray);
  3941. EnterCrit();
  3942. UserAssert(NT_SUCCESS(Status));
  3943. if (Status == STATUS_WAIT_0 && pfnNonMsg != NULL) {
  3944. /*
  3945. * Call pfnNonMsg for the first event
  3946. */
  3947. pfnNonMsg(DEVICE_TYPE_MOUSE);
  3948. /*
  3949. * Setup again the wake mask for this thread.
  3950. * Wait for QS_EVENT or the app won't
  3951. * get event messages like deactivate.
  3952. */
  3953. ptiCurrent->pcti->fsWakeMask = QS_ALLINPUT | QS_EVENT;
  3954. goto Again;
  3955. }
  3956. if (Status == (NTSTATUS)(STATUS_WAIT_0 + nCount)) {
  3957. /*
  3958. * Reset the input idle event to block and threads waiting
  3959. * for this thread to go into idle state.
  3960. */
  3961. SleepInputIdle(ptiCurrent);
  3962. }
  3963. } else {
  3964. Status = nCount;
  3965. }
  3966. /*
  3967. * Clear fsWakeMask since we're no longer waiting on the queue.
  3968. */
  3969. ptiCurrent->pcti->fsWakeMask = 0;
  3970. return (DWORD)Status;
  3971. }
  3972. /***************************************************************************\
  3973. * xxxSleepThread
  3974. *
  3975. * Blocks until an 'event' satisifying fsWakeMask occurs for the
  3976. * current thread.
  3977. *
  3978. * History:
  3979. * 10-28-90 DavidPe Created.
  3980. \***************************************************************************/
  3981. BOOL xxxSleepThread(
  3982. UINT fsWakeMask,
  3983. DWORD Timeout,
  3984. BOOL fInputIdle)
  3985. {
  3986. PTHREADINFO ptiCurrent;
  3987. LARGE_INTEGER li, *pli;
  3988. NTSTATUS status = STATUS_SUCCESS;
  3989. BOOL fExclusive = fsWakeMask & QS_EXCLUSIVE;
  3990. WORD fsWakeMaskSaved;
  3991. BOOL fRet = FALSE;
  3992. UserAssert(IsWinEventNotifyDeferredOK());
  3993. if (fExclusive) {
  3994. /*
  3995. * The exclusive bit is a 'dummy' arg, turn it off to
  3996. * avoid any possible conflicts.
  3997. */
  3998. fsWakeMask = fsWakeMask & ~QS_EXCLUSIVE;
  3999. }
  4000. if (Timeout) {
  4001. /*
  4002. * Convert dwMilliseconds to a relative-time (i.e. negative)
  4003. * LARGE_INTEGER. NT Base calls take time values in 100 nanosecond
  4004. * units.
  4005. */
  4006. li.QuadPart = Int32x32To64(-10000, Timeout);
  4007. pli = &li;
  4008. } else {
  4009. pli = NULL;
  4010. }
  4011. CheckCritIn();
  4012. ptiCurrent = PtiCurrent();
  4013. fsWakeMaskSaved = ptiCurrent->pcti->fsWakeMask;
  4014. while (TRUE) {
  4015. /*
  4016. * First check if the input has arrived.
  4017. */
  4018. if (ptiCurrent->pcti->fsChangeBits & fsWakeMask) {
  4019. fRet = TRUE;
  4020. GetOutFromHere:
  4021. /*
  4022. * Restore the wake mask to what it was before we went to sleep
  4023. * to allow possible callbacks before KeWait... but after the mask
  4024. * has been set and also APCs from KeWait... to still be able to
  4025. * wake up. Simply clearing the mask here if we're in such a
  4026. * callback or in an APC means that the thread will never wake up.
  4027. */
  4028. ptiCurrent->pcti->fsWakeMask = fsWakeMaskSaved;
  4029. if (fRet) {
  4030. /*
  4031. * Update timeLastRead - it is used for hung app calculations.
  4032. * If the thread is waking up to process input, it isn't hung!
  4033. */
  4034. SET_TIME_LAST_READ(ptiCurrent);
  4035. }
  4036. return fRet;
  4037. }
  4038. /*
  4039. * Next check for SendMessages
  4040. */
  4041. if (!fExclusive && ptiCurrent->pcti->fsWakeBits & QS_SENDMESSAGE) {
  4042. xxxReceiveMessages(ptiCurrent);
  4043. /*
  4044. * Restore the change bits we took out in PeekMessage()
  4045. */
  4046. ptiCurrent->pcti->fsChangeBits |= (ptiCurrent->pcti->fsWakeBits & ptiCurrent->fsChangeBitsRemoved);
  4047. ptiCurrent->fsChangeBitsRemoved = 0;
  4048. }
  4049. /*
  4050. * Check to see if some resources need expunging.
  4051. * This will unload hook DLLs, including WinEvent ones.
  4052. */
  4053. if (ptiCurrent->ppi->cSysExpunge != gcSysExpunge) {
  4054. ptiCurrent->ppi->cSysExpunge = gcSysExpunge;
  4055. if (ptiCurrent->ppi->dwhmodLibLoadedMask & gdwSysExpungeMask)
  4056. xxxDoSysExpunge(ptiCurrent);
  4057. }
  4058. /*
  4059. * OR QS_SENDMESSAGE in since ReceiveMessage() will end up
  4060. * trashing pq->fsWakeMask. Do the same for QS_SYSEXPUNGE.
  4061. */
  4062. ClearQueueServerEvent((WORD)(fsWakeMask | (fExclusive ? 0 : QS_SENDMESSAGE)));
  4063. /*
  4064. * If we have timed out then return our error to the caller.
  4065. */
  4066. if (status == STATUS_TIMEOUT) {
  4067. RIPERR1(ERROR_TIMEOUT, RIP_VERBOSE, "SleepThread: The timeout has expired %lX", Timeout);
  4068. UserAssert(fRet == FALSE);
  4069. goto GetOutFromHere;
  4070. }
  4071. /*
  4072. * Because we do a non-alertable wait, we know that a status
  4073. * of STATUS_USER_APC means that the thread was terminated.
  4074. * If we have terminated, get back to user mode.
  4075. */
  4076. if (status == STATUS_USER_APC) {
  4077. #if DBG
  4078. if (ptiCurrent == gptiRit || ptiCurrent == gTermIO.ptiDesktop) {
  4079. /*
  4080. * If STATUS_USER_APC is caught in the system threads,
  4081. * that most likely means the thread was terminated by
  4082. * someone inadvertently.
  4083. */
  4084. RIPMSG1(RIP_WARNING, "xxxSleepThread: STATUS_USER_APC caught in the system thread pti=%p", ptiCurrent);
  4085. }
  4086. #endif
  4087. ClientDeliverUserApc();
  4088. UserAssert(fRet == FALSE);
  4089. goto GetOutFromHere;
  4090. }
  4091. /*
  4092. * If this is the power state callout thread, we might need to bail
  4093. * out early.
  4094. */
  4095. if (gPowerState.pEvent == ptiCurrent->pEventQueueServer) {
  4096. if (gPowerState.fCritical) {
  4097. UserAssert(fRet == FALSE);
  4098. goto GetOutFromHere;
  4099. }
  4100. }
  4101. UserAssert(status == STATUS_SUCCESS);
  4102. /*
  4103. * Check to see if any input came inbetween when we
  4104. * last checked and the NtClearEvent() call.
  4105. *
  4106. * We call NtWaitForSingleObject() rather than
  4107. * WaitForSingleObject() so we can set fAlertable
  4108. * to TRUE and thus allow timer APCs to be processed.
  4109. */
  4110. if (!(ptiCurrent->pcti->fsChangeBits & ptiCurrent->pcti->fsWakeMask)) {
  4111. /*
  4112. * This app is going idle. Clear the spin count check to see
  4113. * if we need to make this process foreground again.
  4114. */
  4115. if (fInputIdle) {
  4116. if (ptiCurrent->TIF_flags & TIF_SPINNING) {
  4117. if (!NT_SUCCESS(CheckProcessForeground(ptiCurrent))) {
  4118. goto GetOutFromHere;
  4119. }
  4120. }
  4121. try {
  4122. ptiCurrent->pClientInfo->cSpins = 0;
  4123. } except (W32ExceptionHandler(TRUE, RIP_WARNING)) {
  4124. goto GetOutFromHere;
  4125. }
  4126. }
  4127. if (!(ptiCurrent->TIF_flags & TIF_16BIT)) {
  4128. if (fInputIdle && ptiCurrent == gptiForeground &&
  4129. IsHooked(ptiCurrent, WHF_FOREGROUNDIDLE)) {
  4130. xxxCallHook(HC_ACTION, 0, 0, WH_FOREGROUNDIDLE);
  4131. }
  4132. /*
  4133. * Set the input idle event to wake up any threads waiting
  4134. * for this thread to go into idle state.
  4135. */
  4136. if (fInputIdle) {
  4137. zzzWakeInputIdle(ptiCurrent);
  4138. }
  4139. xxxSleepTask(fInputIdle, NULL);
  4140. LeaveCrit();
  4141. status = KeWaitForSingleObject(ptiCurrent->pEventQueueServer,
  4142. WrUserRequest, UserMode, FALSE, pli);
  4143. EnterCrit();
  4144. /*
  4145. * Reset the input idle event to block and threads waiting
  4146. * for this thread to go into idle state.
  4147. */
  4148. SleepInputIdle(ptiCurrent);
  4149. /*
  4150. * ptiCurrent is 16bit!
  4151. */
  4152. } else {
  4153. if (fInputIdle) {
  4154. zzzWakeInputIdle(ptiCurrent);
  4155. }
  4156. xxxSleepTask(fInputIdle, NULL);
  4157. }
  4158. }
  4159. }
  4160. }
  4161. /***************************************************************************\
  4162. * SetWakeBit
  4163. *
  4164. * Adds the specified wake bit to specified THREADINFO and wakes its thread
  4165. * up if the bit is in its fsWakeMask.
  4166. *
  4167. * Nothing will happen in the system unless we come to this function, so be
  4168. * fast and small.
  4169. *
  4170. * History:
  4171. * 10-28-90 DavidPe Created.
  4172. \***************************************************************************/
  4173. VOID SetWakeBit(
  4174. PTHREADINFO pti,
  4175. UINT wWakeBit)
  4176. {
  4177. CheckCritIn();
  4178. UserAssert(pti);
  4179. /*
  4180. * Win3.1 changes ptiKeyboard and ptiMouse accordingly if we're setting
  4181. * those bits.
  4182. */
  4183. if (wWakeBit & QS_MOUSE) {
  4184. pti->pq->ptiMouse = pti;
  4185. }
  4186. #ifdef GENERIC_INPUT
  4187. if (wWakeBit & (QS_KEY | QS_RAWINPUT)) {
  4188. pti->pq->ptiKeyboard = pti;
  4189. }
  4190. #else
  4191. if (wWakeBit & QS_KEY) {
  4192. pti->pq->ptiKeyboard = pti;
  4193. }
  4194. #endif
  4195. /*
  4196. * OR in these bits - these bits represent what input this app has
  4197. * (fsWakeBits), or what input has arrived since that last look
  4198. * (fsChangeBits).
  4199. */
  4200. pti->pcti->fsWakeBits |= wWakeBit;
  4201. pti->pcti->fsChangeBits |= wWakeBit;
  4202. /*
  4203. * Before waking, do screen saver check to see if it should
  4204. * go away.
  4205. */
  4206. if ((wWakeBit & QS_INPUT)
  4207. && (pti->ppi->W32PF_Flags & W32PF_IDLESCREENSAVER)) {
  4208. if ((wWakeBit & QS_MOUSEMOVE)
  4209. && (gpsi->ptCursor.x == gptSSCursor.x)
  4210. && (gpsi->ptCursor.y == gptSSCursor.y)) {
  4211. goto SkipScreenSaverStuff;
  4212. }
  4213. /*
  4214. * Our idle screen saver needs to be given a priority boost so that it
  4215. * can process input.
  4216. */
  4217. pti->ppi->W32PF_Flags &= ~W32PF_IDLESCREENSAVER;
  4218. SetForegroundPriority(pti, TRUE);
  4219. }
  4220. SkipScreenSaverStuff:
  4221. if (wWakeBit & pti->pcti->fsWakeMask) {
  4222. /*
  4223. * Wake the Thread
  4224. */
  4225. if (pti->TIF_flags & TIF_16BIT) {
  4226. pti->ptdb->nEvents++;
  4227. gpsi->nEvents++;
  4228. WakeWowTask(pti);
  4229. } else {
  4230. KeSetEvent(pti->pEventQueueServer, 2, FALSE);
  4231. }
  4232. }
  4233. }
  4234. /***************************************************************************\
  4235. * TransferWakeBit
  4236. *
  4237. * We have a mesasge from the system queue. If out input bit for this
  4238. * message isn't set, set ours and clear the guy whose bit was set
  4239. * because of this message.
  4240. *
  4241. * 10-22-92 ScottLu Created.
  4242. \***************************************************************************/
  4243. VOID TransferWakeBit(
  4244. PTHREADINFO pti,
  4245. UINT message)
  4246. {
  4247. PTHREADINFO ptiT;
  4248. UINT fsMask;
  4249. /*
  4250. * Calculate the mask from the message range. Only interested
  4251. * in hardware input here: mouse and keys.
  4252. */
  4253. #ifdef GENERIC_INPUT
  4254. fsMask = CalcWakeMask(message, message, 0) & QS_INPUT;
  4255. #else
  4256. fsMask = CalcWakeMask(message, message, 0) & (QS_MOUSE | QS_KEY);
  4257. #endif
  4258. /*
  4259. * If it is set in this thread's wakebits, nothing to do.
  4260. * Otherwise transfer them from the owner to this thread.
  4261. */
  4262. if (!(pti->pcti->fsWakeBits & fsMask)) {
  4263. /*
  4264. * Either mouse or key is set (not both). Remove this bit
  4265. * from the thread that currently owns it, and change mouse /
  4266. * key ownership to this thread.
  4267. */
  4268. if (fsMask & QS_KEY) {
  4269. ptiT = pti->pq->ptiKeyboard;
  4270. pti->pq->ptiKeyboard = pti;
  4271. } else {
  4272. ptiT = pti->pq->ptiMouse;
  4273. pti->pq->ptiMouse = pti;
  4274. }
  4275. ptiT->pcti->fsWakeBits &= ~fsMask;
  4276. /*
  4277. * Transfer them to this thread (certainly this may be the
  4278. * same thread for win32 threads not sharing queues).
  4279. */
  4280. pti->pcti->fsWakeBits |= fsMask;
  4281. pti->pcti->fsChangeBits |= fsMask;
  4282. }
  4283. }
  4284. /***************************************************************************\
  4285. * ClearWakeBit
  4286. *
  4287. * Clears wake bits. If fSysCheck is TRUE, this clears the input bits only
  4288. * if no messages are in the input queue. Otherwise, it clears input bits
  4289. * unconditionally.
  4290. *
  4291. * 11-05-92 ScottLu Created.
  4292. \***************************************************************************/
  4293. VOID ClearWakeBit(
  4294. PTHREADINFO pti,
  4295. UINT wWakeBit,
  4296. BOOL fSysCheck)
  4297. {
  4298. /*
  4299. * If fSysCheck is TRUE, clear bits only if we are not doing journal
  4300. * playback and there are no more messages in the queue. fSysCheck
  4301. * is TRUE if clearing because of no more input. FALSE if just
  4302. * transfering input ownership from one thread to another.
  4303. */
  4304. if (fSysCheck) {
  4305. if (pti->pq->mlInput.cMsgs != 0 || FJOURNALPLAYBACK()) {
  4306. return;
  4307. }
  4308. if (pti->pq->QF_flags & QF_MOUSEMOVED) {
  4309. wWakeBit &= ~QS_MOUSEMOVE;
  4310. }
  4311. }
  4312. /*
  4313. * Only clear the wake bits, not the change bits as well!
  4314. */
  4315. pti->pcti->fsWakeBits &= ~wWakeBit;
  4316. }
  4317. /***************************************************************************\
  4318. * PtiFromThreadId
  4319. *
  4320. * Returns the THREADINFO for the specified thread or NULL if thread
  4321. * doesn't exist or doesn't have a THREADINFO.
  4322. *
  4323. * History:
  4324. * 01-30-91 DavidPe Created.
  4325. \***************************************************************************/
  4326. PTHREADINFO PtiFromThreadId(
  4327. DWORD dwThreadId)
  4328. {
  4329. PETHREAD pEThread;
  4330. PTHREADINFO pti;
  4331. if (!NT_SUCCESS(LockThreadByClientId(LongToHandle(dwThreadId), &pEThread))) {
  4332. return NULL;
  4333. }
  4334. /*
  4335. * If the thread is not terminating, look up the pti. This is
  4336. * needed because the value returned by PtiFromThread() is
  4337. * undefined if the thread is terminating. See PspExitThread in
  4338. * ntos\ps\psdelete.c.
  4339. */
  4340. if (!PsIsThreadTerminating(pEThread)) {
  4341. pti = PtiFromThread(pEThread);
  4342. } else {
  4343. pti = NULL;
  4344. }
  4345. /*
  4346. * Do a sanity check on the pti to make sure it's really valid.
  4347. */
  4348. if (pti != NULL) {
  4349. try {
  4350. if (GETPTIID(pti) != LongToHandle(dwThreadId)) {
  4351. pti = NULL;
  4352. } else if (!(pti->TIF_flags & TIF_GUITHREADINITIALIZED)) {
  4353. RIPMSG1(RIP_WARNING, "PtiFromThreadId: pti %#p not initialized", pti);
  4354. pti = NULL;
  4355. } else if (pti->TIF_flags & TIF_INCLEANUP) {
  4356. RIPMSG1(RIP_WARNING, "PtiFromThreadId: pti %#p in cleanup", pti);
  4357. pti = NULL;
  4358. }
  4359. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  4360. pti = NULL;
  4361. }
  4362. }
  4363. UnlockThread(pEThread);
  4364. return pti;
  4365. }
  4366. /***************************************************************************\
  4367. * StoreMessage
  4368. *
  4369. *
  4370. *
  4371. * History:
  4372. * 10-31-90 DarrinM Ported from Win 3.0 sources.
  4373. \***************************************************************************/
  4374. VOID StoreMessage(
  4375. LPMSG pmsg,
  4376. PWND pwnd,
  4377. UINT message,
  4378. WPARAM wParam,
  4379. LPARAM lParam,
  4380. DWORD time)
  4381. {
  4382. CheckCritIn();
  4383. pmsg->hwnd = HW(pwnd);
  4384. pmsg->message = message;
  4385. pmsg->wParam = wParam;
  4386. pmsg->lParam = lParam;
  4387. pmsg->time = (time != 0 ? time : NtGetTickCount());
  4388. pmsg->pt = gpsi->ptCursor;
  4389. }
  4390. /***************************************************************************\
  4391. * StoreQMessage
  4392. *
  4393. * If 'time' is 0 grab the current time, if not, it means that this message
  4394. * is for an input event and eventually the mouse/keyboard hooks want to see
  4395. * the right time stamps.
  4396. *
  4397. * History:
  4398. * 02-27-91 DavidPe Created.
  4399. * 06-15-96 CLupu Add 'time' parameter
  4400. \***************************************************************************/
  4401. VOID StoreQMessage(
  4402. PQMSG pqmsg,
  4403. PWND pwnd,
  4404. UINT message,
  4405. WPARAM wParam,
  4406. LPARAM lParam,
  4407. DWORD time,
  4408. DWORD dwQEvent,
  4409. ULONG_PTR dwExtraInfo)
  4410. {
  4411. CheckCritIn();
  4412. pqmsg->msg.hwnd = HW(pwnd);
  4413. pqmsg->msg.message = message;
  4414. pqmsg->msg.wParam = wParam;
  4415. pqmsg->msg.lParam = lParam;
  4416. pqmsg->msg.time = (time == 0) ? NtGetTickCount() : time;
  4417. #ifdef REDIRECTION
  4418. if (message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) {
  4419. pqmsg->msg.pt.x = LOWORD(lParam);
  4420. pqmsg->msg.pt.y = HIWORD(lParam);
  4421. } else {
  4422. pqmsg->msg.pt = gpsi->ptCursor;
  4423. }
  4424. #else
  4425. pqmsg->msg.pt = gpsi->ptCursor;
  4426. #endif
  4427. pqmsg->dwQEvent = dwQEvent;
  4428. pqmsg->ExtraInfo = dwExtraInfo;
  4429. }
  4430. /***************************************************************************\
  4431. * xxxInitProcessInfo
  4432. *
  4433. * This initializes the process info. Usually gets created before the
  4434. * CreateProcess() call returns (so we can synchronize with the starting
  4435. * process in several different ways).
  4436. *
  4437. * 09-18-91 ScottLu Created.
  4438. \***************************************************************************/
  4439. NTSTATUS xxxInitProcessInfo(
  4440. PW32PROCESS pwp)
  4441. {
  4442. PPROCESSINFO ppi = (PPROCESSINFO)pwp;
  4443. NTSTATUS Status;
  4444. CheckCritIn();
  4445. /*
  4446. * Check if we need to initialize the process.
  4447. */
  4448. if (pwp->W32PF_Flags & W32PF_PROCESSCONNECTED) {
  4449. return STATUS_ALREADY_WIN32;
  4450. }
  4451. pwp->W32PF_Flags |= W32PF_PROCESSCONNECTED;
  4452. #if defined(_WIN64)
  4453. /* Tag as emulated 32bit. Flag is copied to be consistent with
  4454. * the way WOW16 apps are tagged for win32k.
  4455. */
  4456. if (PsGetProcessWow64Process(pwp->Process)) {
  4457. pwp->W32PF_Flags |= W32PF_WOW64;
  4458. }
  4459. #endif
  4460. #ifdef GENERIC_INPUT
  4461. UserAssert(ppi->pHidTable == NULL);
  4462. #endif
  4463. /*
  4464. * Mark this app as "starting" - it will be starting until its first
  4465. * window activates.
  4466. */
  4467. UserVerify(xxxSetProcessInitState(pwp->Process, STARTF_FORCEOFFFEEDBACK));
  4468. /*
  4469. * link it into the starting processes list
  4470. */
  4471. SetAppStarting(ppi);
  4472. /*
  4473. * link it into the global processes list
  4474. */
  4475. ppi->ppiNextRunning = gppiList;
  4476. gppiList = ppi;
  4477. /*
  4478. * If foreground activation has not been canceled and the parent process
  4479. * (or an ancestor) can force a foreground change, then allow this process
  4480. * to come to the foreground when it does its first activation.
  4481. *
  4482. * Bug 273518 - joejo
  4483. *
  4484. * This will allow console windows to set foreground correctly on new
  4485. * process' it launches, as opposed it just forcing foreground.
  4486. */
  4487. if (TEST_PUDF(PUDF_ALLOWFOREGROUNDACTIVATE) && CheckAllowForeground(pwp->Process)) {
  4488. ppi->W32PF_Flags |= W32PF_ALLOWFOREGROUNDACTIVATE;
  4489. }
  4490. TAGMSG2(DBGTAG_FOREGROUND, "xxxInitProcessInfo %s W32PF %#p",
  4491. ((ppi->W32PF_Flags & W32PF_ALLOWFOREGROUNDACTIVATE) ? "set" : "NOT"),
  4492. ppi);
  4493. /*
  4494. * Get the logon session id. This is used to determine which
  4495. * windowstation to connect to and to identify attempts to
  4496. * call hooks across security contexts.
  4497. */
  4498. Status = GetProcessLuid(NULL, &ppi->luidSession);
  4499. UserAssert(NT_SUCCESS(Status));
  4500. /*
  4501. * Ensure that we're in sync with the expunge count
  4502. */
  4503. ppi->cSysExpunge = gcSysExpunge;
  4504. /*
  4505. * Don't perform any LPK callbacks until GDI notifies
  4506. * us that the LPK(s) are loaded and initialized.
  4507. */
  4508. ppi->dwLpkEntryPoints = 0;
  4509. return STATUS_SUCCESS;
  4510. }
  4511. /***************************************************************************\
  4512. * DestroyProcessInfo
  4513. *
  4514. * This function is executed when the last thread of a process goes
  4515. * away.
  4516. *
  4517. * SO VERY IMPORTANT:
  4518. * Note that the last thread of the process might not be a w32 thread. So do
  4519. * not make any calls here that assume a w32 pti. Do avoid any function calling
  4520. * PtiCurrent() as it probably assumes it is on a nice w32 thread.
  4521. *
  4522. * Also note that if the process is locked, the ppi is not going away; this
  4523. * simply means that execution on this process has ended. So make sure to clean
  4524. * up in a way that the ppi data is still valid (for example, if you free a
  4525. * pointer, set it to NULL).
  4526. *
  4527. * zzz Note: Not a zzz routine although it calls zzzCalcStartCursorHide() -
  4528. * Since we can't make callbacks on a non-GUI thread, we use
  4529. * DeferWinEventNotify() & EndDeferWinEventNotifyWithoutProcessing()
  4530. * to prevent callbacks.
  4531. *
  4532. * 04/08/96 GerardoB Added header
  4533. \***************************************************************************/
  4534. BOOL DestroyProcessInfo(
  4535. PW32PROCESS pwp)
  4536. {
  4537. PPROCESSINFO ppi = (PPROCESSINFO)pwp;
  4538. PDESKTOPVIEW pdv, pdvNext;
  4539. BOOL fHadThreads;
  4540. PPUBOBJ ppo;
  4541. CheckCritIn();
  4542. /*
  4543. * Free up input idle event if it exists - wake everyone waiting on it
  4544. * first. This object will get created sometimes even for non-windows
  4545. * processes (usually for WinExec(), which calls WaitForInputIdle()).
  4546. */
  4547. CLOSE_PSEUDO_EVENT(&pwp->InputIdleEvent);
  4548. /*
  4549. * Check to see if the startglass is on, and if so turn it off and update.
  4550. * DeferWinEventNotify to because we cannot process notifications for this
  4551. * thread now (we may have no PtiCurrent, see comment above)
  4552. */
  4553. BEGINATOMICCHECK();
  4554. DeferWinEventNotify();
  4555. if (pwp->W32PF_Flags & W32PF_STARTGLASS) {
  4556. pwp->W32PF_Flags &= ~W32PF_STARTGLASS;
  4557. zzzCalcStartCursorHide(NULL, 0);
  4558. }
  4559. /*
  4560. * This is bookkeeping - restore original notification deferral but without
  4561. * attempting to process any deferred notifications because we have no pti.
  4562. */
  4563. EndDeferWinEventNotifyWithoutProcessing();
  4564. ENDATOMICCHECK();
  4565. /*
  4566. * If the process never called Win32k, we're done.
  4567. */
  4568. if (!(pwp->W32PF_Flags & W32PF_PROCESSCONNECTED)) {
  4569. return FALSE;
  4570. }
  4571. #ifdef GENERIC_INPUT
  4572. /*
  4573. * Cleanup the HID devices this process has requested.
  4574. */
  4575. if (ppi->pHidTable) {
  4576. DestroyProcessHidRequests(ppi);
  4577. }
  4578. #endif
  4579. /*
  4580. * Play the Process Close sound for non-console processes
  4581. * running on the I/O windowstation.
  4582. */
  4583. if ((ppi->W32PF_Flags & W32PF_IOWINSTA) &&
  4584. !(ppi->W32PF_Flags & W32PF_CONSOLEAPPLICATION) &&
  4585. (gspwndLogonNotify != NULL) &&
  4586. !(ppi->rpwinsta->dwWSF_Flags & WSF_OPENLOCK)) {
  4587. PTHREADINFO pti = GETPTI(gspwndLogonNotify);
  4588. PQMSG pqmsg;
  4589. if ((pqmsg = AllocQEntry(&pti->mlPost)) != NULL) {
  4590. StoreQMessage(pqmsg, gspwndLogonNotify, WM_LOGONNOTIFY,
  4591. LOGON_PLAYEVENTSOUND, USER_SOUND_CLOSE, 0, 0, 0);
  4592. SetWakeBit(pti, QS_POSTMESSAGE | QS_ALLPOSTMESSAGE);
  4593. }
  4594. }
  4595. /*
  4596. * Be like WIN95.
  4597. * If this is the shell process, then send a LOGON_RESTARTSHELL
  4598. * notification to the winlogon process (only if not logging off)
  4599. */
  4600. if (IsShellProcess(ppi)) {
  4601. /*
  4602. * The shell process will get killed and it's better to set this
  4603. * in the desktop info.
  4604. */
  4605. ppi->rpdeskStartup->pDeskInfo->ppiShellProcess = NULL;
  4606. /*
  4607. * If we're not logging off, notify winlogon
  4608. */
  4609. if ((gspwndLogonNotify != NULL) &&
  4610. !(ppi->rpwinsta->dwWSF_Flags & WSF_OPENLOCK)) {
  4611. PTHREADINFO pti = GETPTI(gspwndLogonNotify);
  4612. PQMSG pqmsg;
  4613. if ((pqmsg = AllocQEntry(&pti->mlPost)) != NULL) {
  4614. StoreQMessage(pqmsg, gspwndLogonNotify, WM_LOGONNOTIFY,
  4615. LOGON_RESTARTSHELL, PsGetProcessExitStatus(ppi->Process), 0, 0, 0);
  4616. SetWakeBit(pti, QS_POSTMESSAGE | QS_ALLPOSTMESSAGE);
  4617. }
  4618. }
  4619. }
  4620. if (ppi->cThreads) {
  4621. RIPMSG1(RIP_ERROR,
  4622. "Disconnect with 0x%x threads remaining",
  4623. ppi->cThreads);
  4624. }
  4625. /*
  4626. * If the app is still starting, remove it from the startup list
  4627. */
  4628. if (ppi->W32PF_Flags & W32PF_APPSTARTING) {
  4629. /*
  4630. * Bug 294193 - joejo
  4631. *
  4632. * Handle case when creator process exits before the child
  4633. * process makes it to CheckAllowForeground code. This is typical with
  4634. * stub EXEs that do nothing but create other processes.
  4635. */
  4636. GiveForegroundActivateRight(PsGetProcessId(ppi->Process));
  4637. ClearAppStarting(ppi);
  4638. }
  4639. /*
  4640. * remove it from the global list
  4641. */
  4642. REMOVE_FROM_LIST(PROCESSINFO, gppiList, ppi, ppiNextRunning);
  4643. /*
  4644. * If any threads ever connected, there may be DCs, classes,
  4645. * cursors, etc. still lying around. If no threads connected
  4646. * (which is the case for console apps), skip all of this cleanup.
  4647. */
  4648. fHadThreads = ppi->W32PF_Flags & W32PF_THREADCONNECTED;
  4649. if (fHadThreads) {
  4650. /*
  4651. * When a process dies we need to make sure any DCE's it owns
  4652. * and have not been deleted are cleanup up. The clean up
  4653. * earlier may have failed if the DC was busy in GDI.
  4654. */
  4655. if (ppi->W32PF_Flags & W32PF_OWNDCCLEANUP) {
  4656. DelayedDestroyCacheDC();
  4657. }
  4658. #if DBG
  4659. {
  4660. PHE pheT, pheMax;
  4661. /*
  4662. * Loop through the table destroying all objects created by the current
  4663. * process. All objects will get destroyed in their proper order simply
  4664. * because of the object locking.
  4665. */
  4666. pheMax = &gSharedInfo.aheList[giheLast];
  4667. for (pheT = gSharedInfo.aheList; pheT <= pheMax; pheT++) {
  4668. /*
  4669. * We should have no process objects left for this process.
  4670. */
  4671. UserAssertMsg0(
  4672. pheT->bFlags & HANDLEF_DESTROY ||
  4673. !(gahti[pheT->bType].bObjectCreateFlags & OCF_PROCESSOWNED) ||
  4674. (PPROCESSINFO)pheT->pOwner != ppi,
  4675. "We should have no process objects left for this process!");
  4676. }
  4677. }
  4678. #endif
  4679. }
  4680. if (pwp->UserHandleCount) {
  4681. RIPMSG1(RIP_ERROR,
  4682. "Disconnect with 0x%x User handle objects remaining",
  4683. pwp->UserHandleCount);
  4684. }
  4685. /*
  4686. * check if we need to zap PID's for DDE objects
  4687. */
  4688. for (ppo = gpPublicObjectList;
  4689. ppo != NULL;
  4690. ppo = ppo->next) {
  4691. if (ppo->pid == pwp->W32Pid) {
  4692. ppo->pid = OBJECT_OWNER_PUBLIC;
  4693. }
  4694. }
  4695. if (gppiScreenSaver == ppi) {
  4696. UserAssert(ppi->W32PF_Flags & W32PF_SCREENSAVER);
  4697. gppiScreenSaver = NULL;
  4698. }
  4699. if (gppiForegroundOld == ppi) {
  4700. gppiForegroundOld = NULL;
  4701. }
  4702. if (gppiUserApiHook == ppi) {
  4703. _UnregisterUserApiHook();
  4704. }
  4705. UnlockWinSta(&ppi->rpwinsta);
  4706. UnlockDesktop(&ppi->rpdeskStartup, LDU_PPI_DESKSTARTUP3, (ULONG_PTR)ppi);
  4707. /*
  4708. * Close the startup desktop handle now if it's still around. If we wait
  4709. * until handle table cleanup time we could potentially deadlock.
  4710. */
  4711. if (ppi->hdeskStartup) {
  4712. UserVerify(NT_SUCCESS(CloseProtectedHandle(ppi->hdeskStartup)));
  4713. ppi->hdeskStartup = NULL;
  4714. }
  4715. /*
  4716. * Mark the process as terminated so access checks will work.
  4717. */
  4718. ppi->W32PF_Flags |= W32PF_TERMINATED;
  4719. /*
  4720. * Cleanup wow process info struct, if any
  4721. */
  4722. if (ppi->pwpi) {
  4723. PWOWPROCESSINFO pwpi = ppi->pwpi;
  4724. ObDereferenceObject(pwpi->pEventWowExec);
  4725. REMOVE_FROM_LIST(WOWPROCESSINFO, gpwpiFirstWow, pwpi, pwpiNext);
  4726. UserFreePool(pwpi);
  4727. ppi->pwpi = NULL;
  4728. }
  4729. /*
  4730. * Delete desktop views. System will do unmapping.
  4731. */
  4732. pdv = ppi->pdvList;
  4733. while (pdv) {
  4734. pdvNext = pdv->pdvNext;
  4735. UserFreePool(pdv);
  4736. pdv = pdvNext;
  4737. }
  4738. ppi->pdvList = NULL;
  4739. /*
  4740. * Clear the SendInput/Journalling hook caller ppi
  4741. */
  4742. if (ppi == gppiInputProvider) {
  4743. gppiInputProvider = NULL;
  4744. }
  4745. /*
  4746. * If this ppi locked SetForegroundWindow, clean up
  4747. */
  4748. if (ppi == gppiLockSFW) {
  4749. gppiLockSFW = NULL;
  4750. }
  4751. return fHadThreads;
  4752. }
  4753. /***************************************************************************\
  4754. * ClearWakeMask
  4755. *
  4756. \***************************************************************************/
  4757. VOID ClearWakeMask(
  4758. VOID)
  4759. {
  4760. PtiCurrent()->pcti->fsWakeMask = 0;
  4761. }
  4762. /***************************************************************************\
  4763. * xxxGetInputEvent
  4764. *
  4765. * Returns a duplicated event-handle that the client process can use to
  4766. * wait on input events.
  4767. *
  4768. * History:
  4769. * 05-02-91 DavidPe Created.
  4770. \***************************************************************************/
  4771. HANDLE xxxGetInputEvent(
  4772. DWORD dwWakeMask)
  4773. {
  4774. PTHREADINFO ptiCurrent;
  4775. WORD wFlags = HIWORD(dwWakeMask);
  4776. UserAssert(IsWinEventNotifyDeferredOK());
  4777. ptiCurrent = PtiCurrent();
  4778. /*
  4779. * If our wait condition is satisfied, signal the event and return.
  4780. * (Since the wake mask could have been anything at the time the input
  4781. * arrived, the event might not be signaled)
  4782. */
  4783. if (GetInputBits(ptiCurrent->pcti, LOWORD(dwWakeMask), (wFlags & MWMO_INPUTAVAILABLE))) {
  4784. KeSetEvent(ptiCurrent->pEventQueueServer, 2, FALSE);
  4785. return ptiCurrent->hEventQueueClient;
  4786. }
  4787. /*
  4788. * If an idle hook is set, call it.
  4789. */
  4790. if (ptiCurrent == gptiForeground &&
  4791. IsHooked(ptiCurrent, WHF_FOREGROUNDIDLE)) {
  4792. xxxCallHook(HC_ACTION, 0, 0, WH_FOREGROUNDIDLE);
  4793. }
  4794. /*
  4795. * What is the criteria for an "idle process"?
  4796. * Answer: The first thread that calls zzzWakeInputIdle, or SleepInputIdle or...
  4797. * Any thread that calls xxxGetInputEvent with any of the following
  4798. * bits set in its wakemask: (sanfords)
  4799. */
  4800. if (dwWakeMask & (QS_POSTMESSAGE | QS_INPUT)) {
  4801. ptiCurrent->ppi->ptiMainThread = ptiCurrent;
  4802. }
  4803. /*
  4804. * When we return, this app is going to sleep. Since it is in its
  4805. * idle mode when it goes to sleep, wake any apps waiting for this
  4806. * app to go idle.
  4807. */
  4808. zzzWakeInputIdle(ptiCurrent);
  4809. /*
  4810. * Setup the wake mask for this thread. Wait for QS_EVENT or the app won't
  4811. * get event messages like deactivate.
  4812. */
  4813. ClearQueueServerEvent((WORD)(dwWakeMask | QS_EVENT));
  4814. /*
  4815. * This app is going idle. Clear the spin count check to see
  4816. * if we need to make this process foreground again.
  4817. */
  4818. try {
  4819. ptiCurrent->pClientInfo->cSpins = 0;
  4820. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  4821. return NULL;
  4822. }
  4823. if (ptiCurrent->TIF_flags & TIF_SPINNING) {
  4824. if (!NT_SUCCESS(CheckProcessForeground(ptiCurrent))) {
  4825. return NULL;
  4826. }
  4827. }
  4828. UserAssert(ptiCurrent->pcti->fsWakeMask != 0);
  4829. return ptiCurrent->hEventQueueClient;
  4830. }
  4831. /***************************************************************************\
  4832. * xxxWaitForInputIdle
  4833. *
  4834. * This routine waits on a particular input queue for "input idle", meaning
  4835. * it waits till that queue has no input to process.
  4836. *
  4837. * 09-13-91 ScottLu Created.
  4838. \***************************************************************************/
  4839. DWORD xxxWaitForInputIdle(
  4840. ULONG_PTR idProcess,
  4841. DWORD dwMilliseconds,
  4842. BOOL fSharedWow)
  4843. {
  4844. PTHREADINFO ptiCurrent;
  4845. PTHREADINFO pti;
  4846. PEPROCESS Process;
  4847. PW32PROCESS W32Process;
  4848. PPROCESSINFO ppi;
  4849. DWORD dwResult;
  4850. NTSTATUS Status;
  4851. TL tlProcess;
  4852. ptiCurrent = PtiCurrent();
  4853. /*
  4854. * If fSharedWow is set, the client passed in a fake process
  4855. * handle which CreateProcess returns for Win16 apps started
  4856. * in the shared WOW VDM.
  4857. *
  4858. * CreateProcess returns a real process handle when you start
  4859. * a Win16 app in a separate WOW VDM.
  4860. */
  4861. if (fSharedWow) { // Waiting for a WOW task to go idle.
  4862. PWOWTHREADINFO pwti;
  4863. /*
  4864. * Look for a matching thread in the WOW thread info list.
  4865. */
  4866. for (pwti = gpwtiFirst; pwti != NULL; pwti = pwti->pwtiNext) {
  4867. if (pwti->idParentProcess == HandleToUlong(PsGetThreadProcessId(ptiCurrent->pEThread)) &&
  4868. pwti->idWaitObject == idProcess) {
  4869. break;
  4870. }
  4871. }
  4872. /*
  4873. * If we couldn't find the right thread, bail out.
  4874. */
  4875. if (pwti == NULL) {
  4876. RIPMSG0(RIP_WARNING, "WaitForInputIdle couldn't find 16-bit task");
  4877. return (DWORD)-1;
  4878. }
  4879. /*
  4880. * Now wait for it to go idle and return.
  4881. */
  4882. dwResult = WaitOnPseudoEvent(&pwti->pIdleEvent, dwMilliseconds);
  4883. if (dwResult == STATUS_ABANDONED) {
  4884. dwResult = xxxPollAndWaitForSingleObject(pwti->pIdleEvent,
  4885. NULL,
  4886. dwMilliseconds);
  4887. }
  4888. return dwResult;
  4889. }
  4890. /*
  4891. * We shouldn't get here for system threads.
  4892. */
  4893. UserAssert(!(ptiCurrent->TIF_flags & TIF_SYSTEMTHREAD));
  4894. /*
  4895. * If the app is waiting for itself to go idle, error.
  4896. */
  4897. if (PsGetThreadProcessId(ptiCurrent->pEThread) == (HANDLE)idProcess &&
  4898. ptiCurrent == ptiCurrent->ppi->ptiMainThread) {
  4899. RIPMSG0(RIP_WARNING, "WaitForInputIdle waiting on self");
  4900. return (DWORD)-1;
  4901. }
  4902. /*
  4903. * Now find the ppi structure for this process.
  4904. */
  4905. Status = LockProcessByClientId((HANDLE)idProcess, &Process);
  4906. if (!NT_SUCCESS(Status))
  4907. return (DWORD)-1;
  4908. if (PsGetProcessExitProcessCalled(Process)) {
  4909. UnlockProcess(Process);
  4910. return (DWORD)-1;
  4911. }
  4912. W32Process = (PW32PROCESS)PsGetProcessWin32Process(Process);
  4913. /*
  4914. * If we can't find that process info structure, return error.
  4915. * Or, if this is a console application, don't wait on it.
  4916. */
  4917. if (W32Process == NULL || W32Process->W32PF_Flags & W32PF_CONSOLEAPPLICATION) {
  4918. UnlockProcess(Process);
  4919. return (DWORD)-1;
  4920. }
  4921. /*
  4922. * We have to wait mark the Process as one which others are waiting on
  4923. */
  4924. ppi = (PPROCESSINFO)W32Process;
  4925. ppi->W32PF_Flags |= W32PF_WAITFORINPUTIDLE;
  4926. for (pti = ppi->ptiList; pti != NULL; pti = pti->ptiSibling) {
  4927. pti->TIF_flags |= TIF_WAITFORINPUTIDLE;
  4928. }
  4929. /*
  4930. * Thread lock the process to ensure that it will be dereferenced
  4931. * if the thread exits.
  4932. */
  4933. LockW32Process(W32Process, &tlProcess);
  4934. UnlockProcess(Process);
  4935. dwResult = WaitOnPseudoEvent(&W32Process->InputIdleEvent, dwMilliseconds);
  4936. if (dwResult == STATUS_ABANDONED) {
  4937. dwResult = xxxPollAndWaitForSingleObject(W32Process->InputIdleEvent,
  4938. Process,
  4939. dwMilliseconds);
  4940. }
  4941. /*
  4942. * Clear all thread TIF_WAIT bits from the process.
  4943. */
  4944. ppi->W32PF_Flags &= ~W32PF_WAITFORINPUTIDLE;
  4945. for (pti = ppi->ptiList; pti != NULL; pti = pti->ptiSibling) {
  4946. pti->TIF_flags &= ~TIF_WAITFORINPUTIDLE;
  4947. }
  4948. UnlockW32Process(&tlProcess);
  4949. return dwResult;
  4950. }
  4951. #define INTERMEDIATE_TIMEOUT (500) // 1/2 second
  4952. /***************************************************************************\
  4953. * xxxPollAndWaitForSingleObject
  4954. *
  4955. * Sometimes we have to wait on an event but still want to periodically
  4956. * wake up and see if the client process has been terminated.
  4957. *
  4958. * dwMilliseconds is initially the total amount of time to wait and after
  4959. * each intermediate wait reflects the amount of time left to wait.
  4960. * -1 means wait indefinitely.
  4961. *
  4962. * 02-Jul-1993 johnc Created.
  4963. \***************************************************************************/
  4964. // LATER!!! can we get rid of the Polling idea and wait additionally on
  4965. // LATER!!! the hEventServer and set that when a thread dies
  4966. DWORD xxxPollAndWaitForSingleObject(
  4967. PKEVENT pEvent,
  4968. PVOID pExecObject,
  4969. DWORD dwMilliseconds)
  4970. {
  4971. DWORD dwIntermediateMilliseconds, dwStartTickCount;
  4972. PTHREADINFO ptiCurrent;
  4973. UINT cEvent = 2;
  4974. NTSTATUS Status = -1;
  4975. LARGE_INTEGER li;
  4976. TL tlEvent;
  4977. ptiCurrent = PtiCurrent();
  4978. if (ptiCurrent->apEvent == NULL) {
  4979. ptiCurrent->apEvent = UserAllocPoolNonPaged(POLL_EVENT_CNT * sizeof(PKEVENT), TAG_EVENT);
  4980. if (ptiCurrent->apEvent == NULL)
  4981. return (DWORD)-1;
  4982. }
  4983. /*
  4984. * Refcount the event to ensure that it won't go
  4985. * away during the wait. By using a thread lock, the
  4986. * event will be dereferenced if the thread exits
  4987. * during a callback. The process pointer has already been
  4988. * locked.
  4989. */
  4990. ThreadLockObject(pEvent, &tlEvent);
  4991. /*
  4992. * If a process was passed in, wait on it too. No need
  4993. * to reference this because the caller has it referenced.
  4994. */
  4995. if (pExecObject) {
  4996. cEvent++;
  4997. }
  4998. /*
  4999. * We want to wake if there're sent messages pending
  5000. */
  5001. ClearQueueServerEvent(QS_SENDMESSAGE);
  5002. /*
  5003. * Wow Tasks MUST be descheduled while in the wait to allow
  5004. * other tasks in the same wow scheduler to run.
  5005. *
  5006. * For example, 16 bit app A calls WaitForInputIdle on 32 bit app B.
  5007. * App B starts up and tries to send a message to 16 bit app C. App C
  5008. * will never be able to process the message unless app A yields
  5009. * control to it, so app B will never go idle.
  5010. */
  5011. if (ptiCurrent->TIF_flags & TIF_16BIT) {
  5012. xxxSleepTask(FALSE, HEVENT_REMOVEME);
  5013. // caution: the wow task is no longer scheduled.
  5014. }
  5015. dwStartTickCount = NtGetTickCount();
  5016. while (TRUE) {
  5017. if (dwMilliseconds > INTERMEDIATE_TIMEOUT) {
  5018. dwIntermediateMilliseconds = INTERMEDIATE_TIMEOUT;
  5019. /*
  5020. * If we are not waiting an infinite amount of time then subtract
  5021. * the last loop duration from time left to wait.
  5022. */
  5023. if (dwMilliseconds != INFINITE) {
  5024. DWORD dwNewTickCount = NtGetTickCount();
  5025. DWORD dwDelta = ComputePastTickDelta(dwNewTickCount, dwStartTickCount);
  5026. dwStartTickCount = dwNewTickCount;
  5027. if (dwDelta < dwMilliseconds) {
  5028. dwMilliseconds -= dwDelta;
  5029. } else {
  5030. dwMilliseconds = 0;
  5031. }
  5032. }
  5033. } else {
  5034. dwIntermediateMilliseconds = dwMilliseconds;
  5035. dwMilliseconds = 0;
  5036. }
  5037. /*
  5038. * Convert dwMilliseconds to a relative-time(i.e. negative) LARGE_INTEGER.
  5039. * NT Base calls take time values in 100 nanosecond units.
  5040. */
  5041. if (dwIntermediateMilliseconds != INFINITE)
  5042. li.QuadPart = Int32x32To64(-10000, dwIntermediateMilliseconds);
  5043. /*
  5044. * Load events into the wait array. Do this every time
  5045. * through the loop in case of recursion.
  5046. */
  5047. ptiCurrent->apEvent[IEV_IDLE] = pEvent;
  5048. ptiCurrent->apEvent[IEV_INPUT] = ptiCurrent->pEventQueueServer;
  5049. ptiCurrent->apEvent[IEV_EXEC] = pExecObject;
  5050. LeaveCrit();
  5051. Status = KeWaitForMultipleObjects(cEvent,
  5052. &ptiCurrent->apEvent[IEV_IDLE],
  5053. WaitAny,
  5054. WrUserRequest,
  5055. UserMode,
  5056. FALSE,
  5057. (dwIntermediateMilliseconds == INFINITE ?
  5058. NULL : &li),
  5059. NULL);
  5060. EnterCrit();
  5061. if (!NT_SUCCESS(Status)) {
  5062. Status = -1;
  5063. } else {
  5064. /*
  5065. * Because we do a non-alertable wait, we know that a status
  5066. * of STATUS_USER_APC means that the thread was terminated.
  5067. * If we have terminated, get back to user mode
  5068. */
  5069. if (Status == STATUS_USER_APC) {
  5070. ClientDeliverUserApc();
  5071. Status = -1;
  5072. }
  5073. }
  5074. if (ptiCurrent->pcti->fsChangeBits & QS_SENDMESSAGE) {
  5075. /*
  5076. * Wow Tasks MUST wait to be rescheduled in the wow non-premptive
  5077. * scheduler before doing anything which might invoke client code.
  5078. */
  5079. if (ptiCurrent->TIF_flags & TIF_16BIT) {
  5080. xxxDirectedYield(DY_OLDYIELD);
  5081. }
  5082. xxxReceiveMessages(ptiCurrent);
  5083. if (ptiCurrent->TIF_flags & TIF_16BIT) {
  5084. xxxSleepTask(FALSE, HEVENT_REMOVEME);
  5085. // caution: the wow task is no longer scheduled.
  5086. }
  5087. }
  5088. /*
  5089. * If we returned from the wait for some other reason than a timeout
  5090. * or to receive messages we are done. If it is a timeout we are
  5091. * only done waiting if the overall time is zero.
  5092. */
  5093. if (Status != STATUS_TIMEOUT && Status != 1)
  5094. break;
  5095. if (dwMilliseconds == 0) {
  5096. /*
  5097. * Fix up the return if the last poll was interupted by a message
  5098. */
  5099. if (Status == 1)
  5100. Status = WAIT_TIMEOUT;
  5101. break;
  5102. }
  5103. }
  5104. /*
  5105. * reschedule the 16 bit app
  5106. */
  5107. if (ptiCurrent->TIF_flags & TIF_16BIT) {
  5108. xxxDirectedYield(DY_OLDYIELD);
  5109. }
  5110. /*
  5111. * Unlock the events.
  5112. */
  5113. ThreadUnlockObject(&tlEvent);
  5114. return Status;
  5115. }
  5116. /***************************************************************************\
  5117. * WaitOnPseudoEvent
  5118. *
  5119. * Similar semantics to WaitForSingleObject() but works with pseudo events.
  5120. * Could fail if creation on the fly fails.
  5121. * Returns STATUS_ABANDONED_WAIT if caller needs to wait on the event and event is
  5122. * created and ready to be waited on.
  5123. *
  5124. * This assumes the event was created with fManualReset=TRUE, fInitState=FALSE
  5125. *
  5126. * 10/28/93 SanfordS Created
  5127. \***************************************************************************/
  5128. DWORD WaitOnPseudoEvent(
  5129. HANDLE *phE,
  5130. DWORD dwMilliseconds)
  5131. {
  5132. HANDLE hEvent;
  5133. NTSTATUS Status;
  5134. CheckCritIn();
  5135. if (*phE == PSEUDO_EVENT_OFF) {
  5136. if (!NT_SUCCESS(ZwCreateEvent(&hEvent, EVENT_ALL_ACCESS, NULL,
  5137. NotificationEvent, FALSE))) {
  5138. UserAssert(!"Could not create event on the fly.");
  5139. if (dwMilliseconds != INFINITE) {
  5140. return STATUS_TIMEOUT;
  5141. } else {
  5142. return (DWORD)-1;
  5143. }
  5144. }
  5145. Status = ObReferenceObjectByHandle(hEvent, EVENT_ALL_ACCESS, *ExEventObjectType,
  5146. KernelMode, phE, NULL);
  5147. ZwClose(hEvent);
  5148. if (!NT_SUCCESS(Status))
  5149. return (DWORD)-1;
  5150. } else if (*phE == PSEUDO_EVENT_ON) {
  5151. return STATUS_WAIT_0;
  5152. }
  5153. return(STATUS_ABANDONED);
  5154. }
  5155. /***************************************************************************\
  5156. * xxxSetCsrssThreadDesktop
  5157. *
  5158. * Set/clear and lock/unlock a desktop for a csrss thread
  5159. * When setting a desktop, ppdeskRestore must be valid and will receive
  5160. * the old (previous) desktop, if any; the caller is expected to restore
  5161. * this pdesk when done.
  5162. *
  5163. * When restoring a desktop, ppdeskRestore must be NULL. pdesk must have been
  5164. * previously returned by this same function (in *ppdeskRestore).
  5165. *
  5166. * History:
  5167. * 02-18-97 GerardoB Extracted from SetInformationThread
  5168. \***************************************************************************/
  5169. NTSTATUS xxxSetCsrssThreadDesktop(PDESKTOP pdesk, PDESKRESTOREDATA pdrdRestore)
  5170. {
  5171. PTHREADINFO ptiCurrent = PtiCurrent();
  5172. NTSTATUS Status = STATUS_SUCCESS;
  5173. MSG msg;
  5174. /*
  5175. * Only csr should come here
  5176. */
  5177. UserAssert(ISCSRSS());
  5178. UserAssert(pdrdRestore);
  5179. UserAssert(pdrdRestore->pdeskRestore == NULL);
  5180. #if 0
  5181. /*
  5182. * If we're in clean up, csrss worker threads should not be messing around
  5183. */
  5184. if (gdwHydraHint & HH_INITIATEWIN32KCLEANUP) {
  5185. FRE_RIPMSG0(RIP_ERROR, "xxxSetCsrssThreadDesktop: HH_INITIATEWIN32KCLEANUP is set");
  5186. }
  5187. #endif
  5188. if (pdesk->dwDTFlags & DF_DESTROYED) {
  5189. RIPMSG1(RIP_WARNING, "xxxSetCsrssThreadDesktop: pdesk %#p destroyed",
  5190. pdesk);
  5191. return STATUS_UNSUCCESSFUL;
  5192. }
  5193. /*
  5194. * Lock the current desktop (set operation). Also, create and save a
  5195. * handle to the new desktop.
  5196. */
  5197. pdrdRestore->pdeskRestore = ptiCurrent->rpdesk;
  5198. if (pdrdRestore->pdeskRestore != NULL) {
  5199. Status = ObReferenceObjectByPointer(pdrdRestore->pdeskRestore,
  5200. MAXIMUM_ALLOWED,
  5201. *ExDesktopObjectType,
  5202. KernelMode);
  5203. if (!NT_SUCCESS(Status)) {
  5204. pdrdRestore->pdeskRestore = NULL;
  5205. pdrdRestore->hdeskNew = NULL;
  5206. return Status;
  5207. }
  5208. LogDesktop(pdrdRestore->pdeskRestore, LD_REF_FN_SETCSRSSTHREADDESKTOP, TRUE, (ULONG_PTR)PtiCurrent());
  5209. }
  5210. Status = ObOpenObjectByPointer(
  5211. pdesk,
  5212. 0,
  5213. NULL,
  5214. EVENT_ALL_ACCESS,
  5215. NULL,
  5216. KernelMode,
  5217. &(pdrdRestore->hdeskNew));
  5218. if (!NT_SUCCESS(Status)) {
  5219. RIPNTERR2(Status, RIP_WARNING, "SetCsrssThreadDesktop, can't open handle, pdesk %#p. Status: %#x", pdesk, Status);
  5220. if (pdrdRestore->pdeskRestore) {
  5221. LogDesktop(pdrdRestore->pdeskRestore, LD_DEREF_FN_SETCSRSSTHREADDESKTOP1, FALSE, (ULONG_PTR)PtiCurrent());
  5222. ObDereferenceObject(pdrdRestore->pdeskRestore);
  5223. pdrdRestore->pdeskRestore = NULL;
  5224. }
  5225. pdrdRestore->hdeskNew = NULL;
  5226. return Status;
  5227. }
  5228. /*
  5229. * Set the new desktop, if switching
  5230. */
  5231. if (pdesk != ptiCurrent->rpdesk) {
  5232. /*
  5233. * Process any remaining messages before we leave the desktop
  5234. */
  5235. if (ptiCurrent->rpdesk) {
  5236. while (xxxPeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD))
  5237. xxxDispatchMessage(&msg);
  5238. }
  5239. if (!xxxSetThreadDesktop(NULL, pdesk)) {
  5240. RIPMSG1(RIP_WARNING, "xxxSetCsrssThreadDesktop: xxxSetThreadDesktop(%#p) failed", pdesk);
  5241. Status = STATUS_INVALID_HANDLE;
  5242. /*
  5243. * We're failing so deref if needed.
  5244. */
  5245. if (pdrdRestore->pdeskRestore != NULL) {
  5246. LogDesktop(pdrdRestore->pdeskRestore, LD_DEREF_FN_SETCSRSSTHREADDESKTOP1, FALSE, (ULONG_PTR)PtiCurrent());
  5247. ObDereferenceObject(pdrdRestore->pdeskRestore);
  5248. pdrdRestore->pdeskRestore = NULL;
  5249. }
  5250. CloseProtectedHandle(pdrdRestore->hdeskNew);
  5251. pdrdRestore->hdeskNew = NULL;
  5252. }
  5253. }
  5254. UserAssert(NT_SUCCESS(Status));
  5255. return Status;
  5256. }
  5257. NTSTATUS xxxRestoreCsrssThreadDesktop(PDESKRESTOREDATA pdrdRestore)
  5258. {
  5259. PTHREADINFO ptiCurrent = PtiCurrent();
  5260. NTSTATUS Status = STATUS_SUCCESS;
  5261. MSG msg;
  5262. /*
  5263. * Only csr should come here
  5264. */
  5265. UserAssert(ISCSRSS());
  5266. UserAssert(pdrdRestore);
  5267. /*
  5268. * Set the new desktop, if switching
  5269. */
  5270. if (pdrdRestore->pdeskRestore != ptiCurrent->rpdesk) {
  5271. /*
  5272. * Process any remaining messages before we leave the desktop
  5273. */
  5274. if (ptiCurrent->rpdesk) {
  5275. while (xxxPeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD))
  5276. xxxDispatchMessage(&msg);
  5277. }
  5278. if (!xxxSetThreadDesktop(NULL, pdrdRestore->pdeskRestore)) {
  5279. FRE_RIPMSG1(RIP_ERROR, "xxxRestoreCsrssThreadDesktop: xxxRestoreThreadDesktop(%#p) failed", pdrdRestore->pdeskRestore);
  5280. Status = STATUS_INVALID_HANDLE;
  5281. }
  5282. }
  5283. /*
  5284. * Dereference the desktop, even if failing the call.
  5285. */
  5286. if (pdrdRestore->pdeskRestore != NULL) {
  5287. LogDesktop(pdrdRestore->pdeskRestore, LD_DEREF_FN_SETCSRSSTHREADDESKTOP2, FALSE, 0);
  5288. ObDereferenceObject(pdrdRestore->pdeskRestore);
  5289. pdrdRestore->pdeskRestore = NULL;
  5290. }
  5291. if (pdrdRestore->hdeskNew) {
  5292. CloseProtectedHandle(pdrdRestore->hdeskNew);
  5293. UserAssert(NT_SUCCESS(Status));
  5294. pdrdRestore->hdeskNew = NULL;
  5295. }
  5296. return Status;
  5297. }
  5298. /***************************************************************************\
  5299. * GetTaskName
  5300. *
  5301. * Gets the application name from a thread.
  5302. \***************************************************************************/
  5303. ULONG GetTaskName(
  5304. PTHREADINFO pti,
  5305. PWSTR Buffer,
  5306. ULONG BufferLength)
  5307. {
  5308. ANSI_STRING strAppName;
  5309. UNICODE_STRING strAppNameU;
  5310. NTSTATUS Status;
  5311. ULONG NameLength = 0;
  5312. if (pti == NULL) {
  5313. *Buffer = 0;
  5314. return 0;
  5315. }
  5316. if (pti->pstrAppName != NULL) {
  5317. NameLength = min(pti->pstrAppName->Length + sizeof(WCHAR), BufferLength);
  5318. RtlCopyMemory(Buffer, pti->pstrAppName->Buffer, NameLength);
  5319. } else {
  5320. RtlInitAnsiString(&strAppName, PsGetProcessImageFileName(PsGetThreadProcess(pti->pEThread)));
  5321. if (BufferLength < sizeof(WCHAR)) {
  5322. NameLength = (strAppName.Length + 1) * sizeof(WCHAR);
  5323. } else {
  5324. strAppNameU.Buffer = Buffer;
  5325. strAppNameU.MaximumLength = (SHORT)BufferLength - sizeof(WCHAR);
  5326. Status = RtlAnsiStringToUnicodeString(&strAppNameU, &strAppName,
  5327. FALSE);
  5328. if (NT_SUCCESS(Status))
  5329. NameLength = strAppNameU.Length + sizeof(WCHAR);
  5330. }
  5331. }
  5332. Buffer[(NameLength / sizeof(WCHAR)) - 1] = 0;
  5333. return NameLength;
  5334. }
  5335. /***************************************************************************\
  5336. * QueryInformationThread
  5337. *
  5338. * Returns information about a thread.
  5339. *
  5340. * History:
  5341. * 03-01-95 JimA Created.
  5342. \***************************************************************************/
  5343. NTSTATUS xxxQueryInformationThread(
  5344. IN HANDLE hThread,
  5345. IN USERTHREADINFOCLASS ThreadInfoClass,
  5346. OUT PVOID ThreadInformation,
  5347. IN ULONG ThreadInformationLength,
  5348. OUT PULONG ReturnLength OPTIONAL)
  5349. {
  5350. PUSERTHREAD_SHUTDOWN_INFORMATION pShutdown;
  5351. PUSERTHREAD_WOW_INFORMATION pWow;
  5352. PETHREAD Thread;
  5353. PTHREADINFO pti;
  5354. NTSTATUS Status = STATUS_SUCCESS;
  5355. ULONG LocalReturnLength = 0;
  5356. DWORD dwClientFlags;
  5357. /*
  5358. * Only allow CSRSS to make this call
  5359. */
  5360. UserAssert(ISCSRSS());
  5361. Status = ObReferenceObjectByHandle(hThread,
  5362. THREAD_QUERY_INFORMATION,
  5363. *PsThreadType,
  5364. UserMode,
  5365. &Thread,
  5366. NULL);
  5367. if (!NT_SUCCESS(Status)) {
  5368. return Status;
  5369. }
  5370. pti = PtiFromThread(Thread);
  5371. switch (ThreadInfoClass) {
  5372. case UserThreadShutdownInformation:
  5373. LocalReturnLength = sizeof(USERTHREAD_SHUTDOWN_INFORMATION);
  5374. UserAssert(ThreadInformationLength == sizeof(USERTHREAD_SHUTDOWN_INFORMATION));
  5375. pShutdown = ThreadInformation;
  5376. /*
  5377. * Read the client flags and zero out the structure,
  5378. * except for pdeskRestore (which is supposed
  5379. * to be the last field)
  5380. */
  5381. dwClientFlags = pShutdown->dwFlags;
  5382. UserAssert(FIELD_OFFSET(USERTHREAD_SHUTDOWN_INFORMATION, drdRestore)
  5383. == (sizeof(USERTHREAD_SHUTDOWN_INFORMATION) - sizeof(DESKRESTOREDATA)));
  5384. RtlZeroMemory(pShutdown,
  5385. sizeof(USERTHREAD_SHUTDOWN_INFORMATION) - sizeof(DESKRESTOREDATA));
  5386. /*
  5387. * Return the desktop window handle if the thread
  5388. * has a desktop and the desktop is on a visible
  5389. * windowstation.
  5390. */
  5391. if (pti != NULL && pti->rpdesk != NULL &&
  5392. !(pti->rpdesk->rpwinstaParent->dwWSF_Flags & WSF_NOIO))
  5393. pShutdown->hwndDesktop = HW(pti->rpdesk->pDeskInfo->spwnd);
  5394. /*
  5395. * Return shutdown status. Zero indicates that the thread
  5396. * has windows and can be shut down in the normal manner.
  5397. */
  5398. if (PsGetThreadProcessId(Thread) == gpidLogon) {
  5399. /*
  5400. * Do not shutdown the logon process.
  5401. */
  5402. pShutdown->StatusShutdown = SHUTDOWN_KNOWN_PROCESS;
  5403. } else if (pti == NULL || pti->rpdesk == NULL) {
  5404. /*
  5405. * The thread either is not a gui thread or it doesn't
  5406. * have a desktop. Make console do the shutdown.
  5407. */
  5408. pShutdown->StatusShutdown = SHUTDOWN_UNKNOWN_PROCESS;
  5409. }
  5410. /*
  5411. * Return flags
  5412. */
  5413. if (pti != NULL && pti->cWindows != 0)
  5414. pShutdown->dwFlags |= USER_THREAD_GUI;
  5415. /*
  5416. * If we return the desktop window handle and the
  5417. * app should be shut down, switch to the desktop
  5418. * and assign it to the shutdown worker thread.
  5419. */
  5420. if ((pShutdown->dwFlags & USER_THREAD_GUI) &&
  5421. pShutdown->StatusShutdown == 0) {
  5422. /*
  5423. * The current csrss thread is going to
  5424. * make activation calls, send messages,
  5425. * switch video modes, etc so we need to
  5426. * assign it to a dekstop
  5427. */
  5428. PTHREADINFO ptiCurrent = PtiCurrent();
  5429. UserAssert(pti->rpdesk != NULL);
  5430. if (ptiCurrent->rpdesk != pti->rpdesk) {
  5431. /*
  5432. * If this thread already has a desktop,
  5433. * restore the old one first.
  5434. * This might happen when threads of the same
  5435. * process are attached to different desktops.
  5436. */
  5437. if (ptiCurrent->rpdesk != NULL) {
  5438. Status = xxxRestoreCsrssThreadDesktop(&pShutdown->drdRestore);
  5439. UserAssert(pti == PtiFromThread(Thread));
  5440. }
  5441. if (NT_SUCCESS(Status)) {
  5442. Status = xxxSetCsrssThreadDesktop(pti->rpdesk, &pShutdown->drdRestore);
  5443. UserAssert(pti == PtiFromThread(Thread));
  5444. }
  5445. }
  5446. /*
  5447. * If we're forcing a shutdown, then there is no need to switch
  5448. * since we won't send any messages or bring up the EndTask dialog
  5449. * (We still want to have a proper rpdesk since BoostHardError might
  5450. * call PostThreadMessage)
  5451. */
  5452. if (!(dwClientFlags & WMCS_NORETRY)) {
  5453. if (NT_SUCCESS(Status)) {
  5454. xxxSwitchDesktop(pti->rpdesk->rpwinstaParent, pti->rpdesk, SDF_SLOVERRIDE);
  5455. UserAssert(pti == PtiFromThread(Thread));
  5456. }
  5457. }
  5458. }
  5459. break;
  5460. case UserThreadFlags:
  5461. LocalReturnLength = sizeof(DWORD);
  5462. if (pti == NULL) {
  5463. Status = STATUS_INVALID_HANDLE;
  5464. } else {
  5465. UserAssert(ThreadInformationLength == sizeof(DWORD));
  5466. *(LPDWORD)ThreadInformation = pti->TIF_flags;
  5467. }
  5468. break;
  5469. case UserThreadTaskName:
  5470. LocalReturnLength = GetTaskName(pti, ThreadInformation, ThreadInformationLength);
  5471. break;
  5472. case UserThreadWOWInformation:
  5473. LocalReturnLength = sizeof(USERTHREAD_WOW_INFORMATION);
  5474. UserAssert(ThreadInformationLength == sizeof(USERTHREAD_WOW_INFORMATION));
  5475. pWow = ThreadInformation;
  5476. RtlZeroMemory(pWow, sizeof(USERTHREAD_WOW_INFORMATION));
  5477. /*
  5478. * If the thread is 16-bit, Status = the exit task function
  5479. * and task id.
  5480. */
  5481. if (pti && pti->TIF_flags & TIF_16BIT) {
  5482. pWow->lpfnWowExitTask = pti->ppi->pwpi->lpfnWowExitTask;
  5483. if (pti->ptdb) {
  5484. pWow->hTaskWow = pti->ptdb->hTaskWow;
  5485. } else {
  5486. pWow->hTaskWow = 0;
  5487. }
  5488. }
  5489. break;
  5490. case UserThreadHungStatus:
  5491. LocalReturnLength = sizeof(DWORD);
  5492. UserAssert(ThreadInformationLength >= sizeof(DWORD));
  5493. /*
  5494. * Return hung status.
  5495. */
  5496. if (pti) {
  5497. *(PDWORD)ThreadInformation =
  5498. (DWORD)FHungApp(pti, (DWORD)*(PDWORD)ThreadInformation);
  5499. } else {
  5500. *(PDWORD)ThreadInformation = FALSE;
  5501. }
  5502. break;
  5503. default:
  5504. Status = STATUS_INVALID_INFO_CLASS;
  5505. RIPMSG1(RIP_ERROR, "Invalid ThreadInfoClass 0x%x", ThreadInfoClass);
  5506. break;
  5507. }
  5508. if (ARGUMENT_PRESENT(ReturnLength)) {
  5509. *ReturnLength = LocalReturnLength;
  5510. }
  5511. UnlockThread(Thread);
  5512. return Status;
  5513. }
  5514. /***************************************************************************\
  5515. * xxxSetInformationThread
  5516. *
  5517. * Sets information about a thread.
  5518. *
  5519. * History:
  5520. * 03-01-95 JimA Created.
  5521. \***************************************************************************/
  5522. NTSTATUS xxxSetInformationThread(
  5523. IN HANDLE hThread,
  5524. IN USERTHREADINFOCLASS ThreadInfoClass,
  5525. IN PVOID ThreadInformation,
  5526. IN ULONG ThreadInformationLength)
  5527. {
  5528. PUSERTHREAD_FLAGS pFlags;
  5529. HANDLE hClientThread;
  5530. DWORD dwOldFlags;
  5531. PTHREADINFO ptiT;
  5532. NTSTATUS Status = STATUS_SUCCESS;
  5533. PETHREAD Thread;
  5534. PETHREAD ThreadClient;
  5535. PTHREADINFO pti;
  5536. HANDLE CsrPortHandle;
  5537. UNREFERENCED_PARAMETER(ThreadInformationLength);
  5538. /*
  5539. * Only allow CSRSS to make this call
  5540. */
  5541. UserAssert(ISCSRSS());
  5542. Status = ObReferenceObjectByHandle(hThread,
  5543. THREAD_SET_INFORMATION,
  5544. *PsThreadType,
  5545. UserMode,
  5546. &Thread,
  5547. NULL);
  5548. if (!NT_SUCCESS(Status))
  5549. return Status;
  5550. pti = PtiFromThread(Thread);
  5551. switch (ThreadInfoClass) {
  5552. case UserThreadFlags:
  5553. if (pti == NULL) {
  5554. Status = STATUS_INVALID_HANDLE;
  5555. } else {
  5556. UserAssert(ThreadInformationLength == sizeof(USERTHREAD_FLAGS));
  5557. pFlags = ThreadInformation;
  5558. dwOldFlags = pti->TIF_flags;
  5559. pti->TIF_flags ^= ((dwOldFlags ^ pFlags->dwFlags) & pFlags->dwMask);
  5560. }
  5561. break;
  5562. case UserThreadHungStatus:
  5563. if (pti == NULL) {
  5564. Status = STATUS_INVALID_HANDLE;
  5565. } else {
  5566. /*
  5567. * No arguments, simple set the last time read.
  5568. */
  5569. SET_TIME_LAST_READ(pti);
  5570. }
  5571. break;
  5572. case UserThreadInitiateShutdown:
  5573. UserAssert(ThreadInformationLength == sizeof(ULONG));
  5574. Status = InitiateShutdown(Thread, (PULONG)ThreadInformation);
  5575. break;
  5576. case UserThreadEndShutdown:
  5577. UserAssert(ThreadInformationLength == sizeof(NTSTATUS));
  5578. Status = EndShutdown(Thread, *(NTSTATUS *)ThreadInformation);
  5579. break;
  5580. case UserThreadUseDesktop:
  5581. UserAssert(ThreadInformationLength == sizeof(USERTHREAD_USEDESKTOPINFO));
  5582. if (pti == NULL) {
  5583. Status = STATUS_INVALID_HANDLE;
  5584. break;
  5585. }
  5586. /*
  5587. * If the caller provides a thread handle, then we use that
  5588. * thread's pdesk and return the pdesk currently used
  5589. * by the caller (set operation). Otherwise,
  5590. * we use the pdesk provided by the caller (restore operation).
  5591. */
  5592. hClientThread = ((PUSERTHREAD_USEDESKTOPINFO)ThreadInformation)->hThread;
  5593. if (hClientThread != NULL) {
  5594. Status = ObReferenceObjectByHandle(hClientThread,
  5595. THREAD_QUERY_INFORMATION,
  5596. *PsThreadType,
  5597. UserMode,
  5598. &ThreadClient,
  5599. NULL);
  5600. if (!NT_SUCCESS(Status))
  5601. break;
  5602. ptiT = PtiFromThread(ThreadClient);
  5603. if ((ptiT == NULL) || (ptiT->rpdesk == NULL)) {
  5604. Status = STATUS_INVALID_HANDLE;
  5605. goto DerefClientThread;
  5606. }
  5607. Status = xxxSetCsrssThreadDesktop(ptiT->rpdesk, &((PUSERTHREAD_USEDESKTOPINFO)ThreadInformation)->drdRestore);
  5608. } else {
  5609. Status = xxxRestoreCsrssThreadDesktop(&((PUSERTHREAD_USEDESKTOPINFO)ThreadInformation)->drdRestore);
  5610. }
  5611. if (hClientThread != NULL) {
  5612. DerefClientThread:
  5613. ObDereferenceObject(ThreadClient);
  5614. }
  5615. break;
  5616. case UserThreadUseActiveDesktop:
  5617. UserAssert(ThreadInformationLength == sizeof(USERTHREAD_USEDESKTOPINFO));
  5618. if (pti == NULL || grpdeskRitInput == NULL) {
  5619. Status = STATUS_INVALID_HANDLE;
  5620. break;
  5621. }
  5622. Status = xxxSetCsrssThreadDesktop(grpdeskRitInput,
  5623. &((PUSERTHREAD_USEDESKTOPINFO)ThreadInformation)->drdRestore);
  5624. break;
  5625. case UserThreadCsrApiPort:
  5626. /*
  5627. * Only CSR can call this
  5628. */
  5629. if (PsGetThreadProcess(Thread) != gpepCSRSS) {
  5630. Status = STATUS_ACCESS_DENIED;
  5631. break;
  5632. }
  5633. UserAssert(ThreadInformationLength == sizeof(HANDLE));
  5634. /*
  5635. * Only set it once.
  5636. */
  5637. if (CsrApiPort != NULL)
  5638. break;
  5639. CsrPortHandle = *(PHANDLE)ThreadInformation;
  5640. Status = ObReferenceObjectByHandle(
  5641. CsrPortHandle,
  5642. 0,
  5643. NULL, //*LpcPortObjectType,
  5644. UserMode,
  5645. &CsrApiPort,
  5646. NULL);
  5647. if (!NT_SUCCESS(Status)) {
  5648. CsrApiPort = NULL;
  5649. RIPMSG1(RIP_WARNING,
  5650. "CSR port reference failed, Status=%#lx",
  5651. Status);
  5652. }
  5653. break;
  5654. default:
  5655. Status = STATUS_INVALID_INFO_CLASS;
  5656. RIPMSG1(RIP_ERROR, "Invalid ThreadInfoClass 0x%x", ThreadInfoClass);
  5657. break;
  5658. }
  5659. UnlockThread(Thread);
  5660. return Status;
  5661. }
  5662. /***************************************************************************\
  5663. * _GetProcessDefaultLayout (API)
  5664. *
  5665. * Retreives the default layout information about a process.
  5666. *
  5667. * History:
  5668. * 23-01-98 SamerA Created.
  5669. \***************************************************************************/
  5670. BOOL _GetProcessDefaultLayout(
  5671. OUT DWORD *pdwDefaultLayout)
  5672. {
  5673. BOOL fSuccess = FALSE;
  5674. /*
  5675. * Do not allow CSRSS to make this call. This call might happen due to
  5676. * the inheritence code. See xxxCreateWindowEx(...)
  5677. */
  5678. if (ISCSRSS()) {
  5679. UserSetLastError(ERROR_INVALID_ACCESS);
  5680. goto api_error;
  5681. }
  5682. try {
  5683. ProbeForWriteUlong(pdwDefaultLayout);
  5684. *pdwDefaultLayout = PpiCurrent()->dwLayout;
  5685. } except (W32ExceptionHandler(TRUE, RIP_WARNING)) {
  5686. goto api_error;
  5687. }
  5688. fSuccess = TRUE;
  5689. api_error:
  5690. return fSuccess;
  5691. }
  5692. /***************************************************************************\
  5693. * _SetProcessDefaultLayout (API)
  5694. *
  5695. * Sets the default layout information about a process.
  5696. *
  5697. * History:
  5698. * 23-01-98 SamerA Created.
  5699. \***************************************************************************/
  5700. BOOL _SetProcessDefaultLayout(
  5701. IN DWORD dwDefaultLayout)
  5702. {
  5703. /*
  5704. * Do not allow CSRSS to make this call
  5705. */
  5706. UserAssert(PsGetCurrentProcess() != gpepCSRSS);
  5707. /*
  5708. * Validate dwDefaultLayout
  5709. */
  5710. if (dwDefaultLayout & ~LAYOUT_ORIENTATIONMASK)
  5711. {
  5712. RIPERR1(ERROR_INVALID_PARAMETER,
  5713. RIP_WARNING,
  5714. "Calling SetProcessDefaultLayout with invalid layout = %lX",
  5715. dwDefaultLayout);
  5716. return FALSE;
  5717. }
  5718. /*
  5719. * Update the process default layout param
  5720. */
  5721. PpiCurrent()->dwLayout = dwDefaultLayout;
  5722. return TRUE;
  5723. }
  5724. /***************************************************************************\
  5725. * SetInformationProcess
  5726. *
  5727. * Sets information about a process.
  5728. *
  5729. * History:
  5730. * 09-27-96 GerardoB Created.
  5731. \***************************************************************************/
  5732. NTSTATUS SetInformationProcess(
  5733. IN HANDLE hProcess,
  5734. IN USERPROCESSINFOCLASS ProcessInfoClass,
  5735. IN PVOID ProcessInformation,
  5736. IN ULONG ProcessInformationLength)
  5737. {
  5738. PUSERPROCESS_FLAGS pFlags;
  5739. DWORD dwOldFlags;
  5740. NTSTATUS Status = STATUS_SUCCESS;
  5741. PEPROCESS Process;
  5742. PPROCESSINFO ppi;
  5743. UNREFERENCED_PARAMETER(ProcessInformationLength);
  5744. UserAssert(ISCSRSS());
  5745. Status = ObReferenceObjectByHandle(hProcess,
  5746. PROCESS_SET_INFORMATION,
  5747. *PsProcessType,
  5748. UserMode,
  5749. &Process,
  5750. NULL);
  5751. if (!NT_SUCCESS(Status)) {
  5752. return Status;
  5753. }
  5754. ppi = PpiFromProcess(Process);
  5755. switch (ProcessInfoClass) {
  5756. case UserProcessFlags:
  5757. if (ppi == NULL) {
  5758. Status = STATUS_INVALID_HANDLE;
  5759. } else {
  5760. UserAssert(ProcessInformationLength == sizeof(USERPROCESS_FLAGS));
  5761. pFlags = ProcessInformation;
  5762. dwOldFlags = ppi->W32PF_Flags;
  5763. ppi->W32PF_Flags ^= ((dwOldFlags ^ pFlags->dwFlags) & pFlags->dwMask);
  5764. }
  5765. break;
  5766. default:
  5767. Status = STATUS_INVALID_INFO_CLASS;
  5768. UserAssert(FALSE);
  5769. break;
  5770. }
  5771. UnlockProcess(Process);
  5772. return Status;
  5773. }
  5774. /***************************************************************************\
  5775. * xxxSetConsoleCaretInfo
  5776. *
  5777. * Store information about the console's homegrown caret and notify any
  5778. * interested apps that it changed. We need this for accessibility.
  5779. *
  5780. * History:
  5781. * 26-May-1999 JerrySh Created.
  5782. \***************************************************************************/
  5783. VOID xxxSetConsoleCaretInfo(
  5784. PCONSOLE_CARET_INFO pcci)
  5785. {
  5786. PWND pwnd;
  5787. TL tlpwnd;
  5788. pwnd = ValidateHwnd(pcci->hwnd);
  5789. if (pwnd && pwnd->head.rpdesk) {
  5790. pwnd->head.rpdesk->cciConsole = *pcci;
  5791. ThreadLock(pwnd, &tlpwnd);
  5792. xxxWindowEvent(EVENT_OBJECT_LOCATIONCHANGE, pwnd, OBJID_CARET, INDEXID_CONTAINER, WEF_ASYNC);
  5793. ThreadUnlock(&tlpwnd);
  5794. }
  5795. }
  5796. /***************************************************************************\
  5797. * xxxConsoleControl
  5798. *
  5799. * Performs special control operations for console.
  5800. *
  5801. * History:
  5802. * 03-01-95 JimA Created.
  5803. \***************************************************************************/
  5804. NTSTATUS xxxConsoleControl(
  5805. IN CONSOLECONTROL ConsoleControl,
  5806. IN PVOID ConsoleInformation,
  5807. IN ULONG ConsoleInformationLength)
  5808. {
  5809. PCONSOLEDESKTOPCONSOLETHREAD pDesktopConsole;
  5810. PCONSOLEWINDOWSTATIONPROCESS pConsoleWindowStationInfo;
  5811. PDESKTOP pdesk;
  5812. DWORD dwThreadIdOld;
  5813. NTSTATUS Status = STATUS_SUCCESS;
  5814. UNREFERENCED_PARAMETER(ConsoleInformationLength);
  5815. UserAssert(ISCSRSS());
  5816. switch (ConsoleControl) {
  5817. case ConsoleDesktopConsoleThread:
  5818. UserAssert(ConsoleInformationLength == sizeof(CONSOLEDESKTOPCONSOLETHREAD));
  5819. pDesktopConsole = (PCONSOLEDESKTOPCONSOLETHREAD)ConsoleInformation;
  5820. Status = ObReferenceObjectByHandle(
  5821. pDesktopConsole->hdesk,
  5822. 0,
  5823. *ExDesktopObjectType,
  5824. UserMode,
  5825. &pdesk,
  5826. NULL);
  5827. if (!NT_SUCCESS(Status))
  5828. return Status;
  5829. LogDesktop(pdesk, LD_REF_FN_CONSOLECONTROL1, TRUE, (ULONG_PTR)PtiCurrent());
  5830. dwThreadIdOld = pdesk->dwConsoleThreadId;
  5831. if (pDesktopConsole->dwThreadId != (DWORD)-1) {
  5832. pdesk->dwConsoleThreadId =
  5833. pDesktopConsole->dwThreadId;
  5834. /*
  5835. * Make sure that the console input thread is not starting while
  5836. * shutting down.
  5837. */
  5838. if ((pDesktopConsole->dwThreadId != 0) && (gdwHydraHint & HH_INITIATEWIN32KCLEANUP)) {
  5839. FRE_RIPMSG1(RIP_ERROR, "xxxConsoleControl: Console input thread starting during shutdown. dwThreadId: %lx",
  5840. pDesktopConsole->dwThreadId);
  5841. }
  5842. }
  5843. pDesktopConsole->dwThreadId = dwThreadIdOld;
  5844. LogDesktop(pdesk, LD_DEREF_FN_CONSOLECONTROL1, FALSE, (ULONG_PTR)PtiCurrent());
  5845. ObDereferenceObject(pdesk);
  5846. break;
  5847. case ConsoleClassAtom:
  5848. UserAssert(ConsoleInformationLength == sizeof(ATOM));
  5849. gatomConsoleClass = *(ATOM *)ConsoleInformation;
  5850. break;
  5851. case ConsoleNotifyConsoleApplication:
  5852. /*
  5853. * Bug 273518 - joejo
  5854. *
  5855. * Adding optimization to bug fix
  5856. */
  5857. UserAssert(ConsoleInformationLength == sizeof(CONSOLE_PROCESS_INFO));
  5858. xxxUserNotifyConsoleApplication((PCONSOLE_PROCESS_INFO)ConsoleInformation);
  5859. break;
  5860. case ConsoleSetVDMCursorBounds:
  5861. UserAssert((ConsoleInformation == NULL) ||
  5862. (ConsoleInformationLength == sizeof(RECT)));
  5863. SetVDMCursorBounds(ConsoleInformation);
  5864. break;
  5865. case ConsolePublicPalette:
  5866. UserAssert(ConsoleInformationLength == sizeof(HPALETTE));
  5867. GreSetPaletteOwner(*(HPALETTE *)ConsoleInformation, OBJECT_OWNER_PUBLIC);
  5868. break;
  5869. case ConsoleWindowStationProcess:
  5870. UserAssert(ConsoleInformationLength == sizeof(CONSOLEWINDOWSTATIONPROCESS));
  5871. pConsoleWindowStationInfo = (PCONSOLEWINDOWSTATIONPROCESS)ConsoleInformation;
  5872. UserSetConsoleProcessWindowStation(pConsoleWindowStationInfo->dwProcessId,
  5873. pConsoleWindowStationInfo->hwinsta);
  5874. break;
  5875. #if defined(FE_IME)
  5876. /*
  5877. * For console IME issue
  5878. *
  5879. * Console IME do register thread ID in DESKTOP info.
  5880. * So should be per desktop.
  5881. */
  5882. case ConsoleRegisterConsoleIME:
  5883. {
  5884. PCONSOLE_REGISTER_CONSOLEIME RegConIMEInfo;
  5885. DWORD dwConsoleIMEThreadIdOld;
  5886. UserAssert(ConsoleInformationLength == sizeof(CONSOLE_REGISTER_CONSOLEIME));
  5887. RegConIMEInfo = (PCONSOLE_REGISTER_CONSOLEIME)ConsoleInformation;
  5888. RegConIMEInfo->dwConsoleInputThreadId = 0;
  5889. Status = ObReferenceObjectByHandle(
  5890. RegConIMEInfo->hdesk,
  5891. 0,
  5892. *ExDesktopObjectType,
  5893. UserMode,
  5894. &pdesk,
  5895. NULL);
  5896. if (!NT_SUCCESS(Status))
  5897. return Status;
  5898. LogDesktop(pdesk, LD_REF_FN_CONSOLECONTROL2, TRUE, (ULONG_PTR)PtiCurrent());
  5899. Status = STATUS_SUCCESS;
  5900. if (pdesk->dwConsoleThreadId)
  5901. {
  5902. /*
  5903. * Exists console input thread
  5904. */
  5905. RegConIMEInfo->dwConsoleInputThreadId = pdesk->dwConsoleThreadId;
  5906. dwConsoleIMEThreadIdOld = pdesk->dwConsoleIMEThreadId;
  5907. if (RegConIMEInfo->dwAction != REGCONIME_QUERY) {
  5908. PTHREADINFO ptiConsoleIME;
  5909. if ((ptiConsoleIME = PtiFromThreadId(RegConIMEInfo->dwThreadId)) != NULL) {
  5910. if ((RegConIMEInfo->dwAction == REGCONIME_REGISTER) &&
  5911. !(ptiConsoleIME->TIF_flags & TIF_DONTATTACHQUEUE)) {
  5912. /*
  5913. * Register
  5914. */
  5915. ptiConsoleIME->TIF_flags |= TIF_DONTATTACHQUEUE;
  5916. pdesk->dwConsoleIMEThreadId = RegConIMEInfo->dwThreadId;
  5917. } else if ((RegConIMEInfo->dwAction == REGCONIME_UNREGISTER) &&
  5918. (ptiConsoleIME->TIF_flags & TIF_DONTATTACHQUEUE)) {
  5919. /*
  5920. * Unregister
  5921. */
  5922. ptiConsoleIME->TIF_flags &= ~TIF_DONTATTACHQUEUE;
  5923. pdesk->dwConsoleIMEThreadId = 0;
  5924. } else if (RegConIMEInfo->dwAction == REGCONIME_TERMINATE) {
  5925. /*
  5926. * Terminate console IME (Logoff/Shutdown)
  5927. */
  5928. pdesk->dwConsoleIMEThreadId = 0;
  5929. }
  5930. } else if (RegConIMEInfo->dwAction == REGCONIME_TERMINATE) {
  5931. /*
  5932. * Abnormal end console IME
  5933. */
  5934. pdesk->dwConsoleIMEThreadId = 0;
  5935. } else {
  5936. Status = STATUS_ACCESS_DENIED;
  5937. }
  5938. }
  5939. RegConIMEInfo->dwThreadId = dwConsoleIMEThreadIdOld;
  5940. }
  5941. LogDesktop(pdesk, LD_DEREF_FN_CONSOLECONTROL2, FALSE, (ULONG_PTR)PtiCurrent());
  5942. ObDereferenceObject(pdesk);
  5943. return Status;
  5944. }
  5945. break;
  5946. #endif
  5947. case ConsoleFullscreenSwitch:
  5948. UserAssert(ConsoleInformationLength == sizeof(CONSOLE_FULLSCREEN_SWITCH));
  5949. xxxbFullscreenSwitch(((PCONSOLE_FULLSCREEN_SWITCH)ConsoleInformation)->bFullscreenSwitch,
  5950. ((PCONSOLE_FULLSCREEN_SWITCH)ConsoleInformation)->hwnd);
  5951. break;
  5952. case ConsoleSetCaretInfo:
  5953. UserAssert(ConsoleInformationLength == sizeof(CONSOLE_CARET_INFO));
  5954. xxxSetConsoleCaretInfo((PCONSOLE_CARET_INFO)ConsoleInformation);
  5955. break;
  5956. default:
  5957. RIPMSGF1(RIP_ERROR, "Invalid control class: 0x%x", ConsoleControl);
  5958. return STATUS_INVALID_INFO_CLASS;
  5959. }
  5960. return STATUS_SUCCESS;
  5961. }