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.

1231 lines
36 KiB

  1. /***************************** Module Header ******************************\
  2. * Module Name: ghost.c
  3. *
  4. * Copyright (c) 1985-1999, Microsoft Corporation
  5. *
  6. * Ghost support for unresponsive windows.
  7. *
  8. * History:
  9. * 23-Apr-1999 vadimg created
  10. \***************************************************************************/
  11. #include "precomp.h"
  12. #pragma hdrstop
  13. #ifdef HUNGAPP_GHOSTING
  14. typedef struct tagGHOST *PGHOST;
  15. typedef struct tagGHOST {
  16. PGHOST pghostNext; // next structure in the linked list
  17. PWND pwnd; // hung window we're trying to ghost
  18. PWND pwndGhost; // ghost window created for this pwnd
  19. HBITMAP hbm; // saved visual bits for the ghosted window
  20. HRGN hrgn; // what visual bits are available to us
  21. RECT rcClient; // client rect in window's coordinates
  22. UINT fWarningText : 1; // whether the warning text has been added
  23. UINT fSizedOrMoved : 1;
  24. } GHOST, *PGHOST;
  25. PGHOST gpghostFirst; // pointer to the start of the ghost list
  26. PTHREADINFO gptiGhost; // pointer to ghost threadinfo
  27. ULONG guGhostLinked;
  28. ULONG guGhostUnlinked;
  29. ULONG guGhostBmpCreated;
  30. ULONG guGhostBmpFreed;
  31. #define XY_MARGIN 10
  32. #define MAXSTRING 256
  33. #define GHOST_MAX 50
  34. /***************************************************************************\
  35. * _DisableProcessWindowsGhosting
  36. *
  37. * Diables ghosting windows for the calling process.
  38. * History:
  39. * 31-May-01 MSadek Created.
  40. \***************************************************************************/
  41. VOID _DisableProcessWindowsGhosting(
  42. VOID)
  43. {
  44. PpiCurrent()->W32PF_Flags |= W32PF_DISABLEWINDOWSGHOSTING;
  45. }
  46. /***************************************************************************\
  47. * GhostFromGhostPwnd
  48. *
  49. * Find the ghost structure for this ghost window.
  50. \***************************************************************************/
  51. PGHOST GhostFromGhostPwnd(
  52. PWND pwndGhost)
  53. {
  54. PGHOST pghost;
  55. for (pghost = gpghostFirst; pghost != NULL; pghost = pghost->pghostNext) {
  56. if (pghost->pwndGhost == pwndGhost) {
  57. return pghost;
  58. }
  59. }
  60. return NULL;
  61. }
  62. /***************************************************************************\
  63. * GhostFromPwnd
  64. *
  65. \***************************************************************************/
  66. PGHOST GhostFromPwnd(
  67. PWND pwnd)
  68. {
  69. PGHOST pghost;
  70. for (pghost = gpghostFirst; pghost != NULL; pghost = pghost->pghostNext) {
  71. if (pghost->pwnd == pwnd) {
  72. return pghost;
  73. }
  74. }
  75. return NULL;
  76. }
  77. /***************************************************************************\
  78. * FindGhost
  79. *
  80. * Find a ghost that corresponds to this hung window.
  81. \***************************************************************************/
  82. PWND FindGhost(
  83. PWND pwnd)
  84. {
  85. PGHOST pghost = GhostFromPwnd(pwnd);
  86. if (pghost != NULL) {
  87. return pghost->pwndGhost;
  88. } else {
  89. return NULL;
  90. }
  91. }
  92. /***************************************************************************\
  93. * GhostSizedOrMoved
  94. *
  95. * Returns true if the ghost window corresponding to a window was sized or moved
  96. * through its life time.
  97. \***************************************************************************/
  98. BOOL GhostSizedOrMoved(
  99. PWND pwnd)
  100. {
  101. PGHOST pghost = GhostFromPwnd(pwnd);
  102. if (pghost != NULL) {
  103. return pghost->fSizedOrMoved;
  104. } else {
  105. return FALSE;
  106. }
  107. }
  108. /***************************************************************************\
  109. * UnlinkAndFreeGhost
  110. *
  111. * This function unlinks a ghost element from the list and free its allocated
  112. * memory.
  113. \***************************************************************************/
  114. _inline VOID UnlinkAndFreeGhost(
  115. PGHOST* ppghost,
  116. PGHOST pghost)
  117. {
  118. UserAssert(pghost->hbm == NULL);
  119. *ppghost = pghost->pghostNext;
  120. UserFreePool(pghost);
  121. guGhostUnlinked++;
  122. }
  123. /***************************************************************************\
  124. * GetWindowIcon
  125. *
  126. * Get a window icon. If asked try the large icon first, then the small icon,
  127. * then the windows logo icon.
  128. \***************************************************************************/
  129. PICON GetWindowIcon(
  130. PWND pwnd,
  131. BOOL fBigIcon)
  132. {
  133. HICON hicon;
  134. PICON picon = NULL;
  135. if (fBigIcon) {
  136. hicon = (HICON)_GetProp(pwnd, MAKEINTATOM(gpsi->atomIconProp), TRUE);
  137. if (hicon) {
  138. picon = (PICON)HMValidateHandleNoRip(hicon, TYPE_CURSOR);
  139. }
  140. if (picon == NULL) {
  141. picon = pwnd->pcls->spicn;
  142. }
  143. }
  144. if (picon == NULL) {
  145. hicon = (HICON)_GetProp(pwnd, MAKEINTATOM(gpsi->atomIconSmProp), TRUE);
  146. if (hicon != NULL) {
  147. picon = (PICON)HMValidateHandleNoRip(hicon, TYPE_CURSOR);
  148. }
  149. if (picon == NULL) {
  150. picon = pwnd->pcls->spicnSm;
  151. }
  152. }
  153. return picon;
  154. }
  155. /***************************************************************************\
  156. * AddGhost
  157. *
  158. * Add a new ghost structure for a hung window.
  159. \***************************************************************************/
  160. BOOL AddGhost(
  161. PWND pwnd)
  162. {
  163. PGHOST pghost;
  164. CheckCritIn();
  165. /*
  166. * Need to limit the maximum number of ghost windows created as not to
  167. * result into thread starvation.
  168. */
  169. if (guGhostLinked - guGhostUnlinked == GHOST_MAX) {
  170. return FALSE;
  171. }
  172. if ((pghost = (PGHOST)UserAllocPoolZInit(sizeof(GHOST), TAG_GHOST)) == NULL) {
  173. return FALSE;
  174. }
  175. pghost->pghostNext = gpghostFirst;
  176. gpghostFirst = pghost;
  177. pghost->pwnd = pwnd;
  178. /*
  179. * When pwndGhost is NULL, the ghost thread will try to create a ghost
  180. * window for this hung window.
  181. */
  182. KeSetEvent(gpEventScanGhosts, EVENT_INCREMENT, FALSE);
  183. guGhostLinked++;
  184. return TRUE;
  185. }
  186. BOOL AddOwnedWindowToGhostList(
  187. PWND pwndRoot,
  188. PWND pwndOrg)
  189. {
  190. PWND pwnd = NULL;
  191. while (pwnd = NextOwnedWindow(pwnd, pwndRoot, pwndRoot->spwndParent)) {
  192. if (!AddOwnedWindowToGhostList(pwnd, pwndOrg)) {
  193. return FALSE;
  194. }
  195. /*
  196. * We need to add the bottom window on the chain first to the ghost
  197. * list because we scan the list from the head thus, ensure that the
  198. * owned window is already created at the time we create the ownee
  199. * ghost.
  200. */
  201. if (GhostFromPwnd(pwnd) == NULL) {
  202. if (!AddGhost(pwnd)) {
  203. return FALSE;
  204. }
  205. if (GETPTI(pwndOrg) != GETPTI(pwndRoot)) {
  206. RIPMSGF4(RIP_WARNING,
  207. "Cross thread ghosting pwnd: 0x%p pti 0x%p, pwndRoot: 0x%p pti 0x%p",
  208. pwndOrg,
  209. GETPTI(pwndOrg),
  210. pwndRoot,
  211. GETPTI(pwndRoot));
  212. }
  213. }
  214. }
  215. return TRUE;
  216. }
  217. BOOL AddGhostOwnersAndOwnees(
  218. PWND pwnd)
  219. {
  220. PWND pwndRoot = pwnd;
  221. /*
  222. * Get the topmost owner window in the chain.
  223. */
  224. while(pwndRoot->spwndOwner != NULL) {
  225. pwndRoot = pwndRoot->spwndOwner;
  226. }
  227. /*
  228. * Now starting form that window, walk the whole ownee tree.
  229. */
  230. if (!AddOwnedWindowToGhostList(pwndRoot, pwnd)) {
  231. return FALSE;
  232. }
  233. /*
  234. * For the topmost window (or the only single window if there is no Owner / Ownee
  235. * relationship at all, add the window to the ghost list
  236. */
  237. if (GhostFromPwnd(pwndRoot) == NULL) {
  238. if (!AddGhost(pwndRoot)) {
  239. return FALSE;
  240. }
  241. if (GETPTI(pwnd) != GETPTI(pwndRoot)) {
  242. RIPMSGF4(RIP_WARNING,
  243. "Cross thread ghosting pwnd: 0x%p pti 0x%p, pwndRoot: 0x%p pti 0x%p",
  244. pwnd,
  245. GETPTI(pwnd),
  246. pwndRoot,
  247. GETPTI(pwndRoot));
  248. }
  249. }
  250. return TRUE;
  251. }
  252. #if GHOST_AGGRESSIVE
  253. /***************************************************************************\
  254. * DimSavedBits
  255. *
  256. \***************************************************************************/
  257. VOID DimSavedBits(
  258. PGHOST pghost)
  259. {
  260. HBITMAP hbm, hbmOld, hbmOld2;
  261. LONG cx, cy;
  262. RECT rc;
  263. BLENDFUNCTION blend;
  264. if (pghost->hbm == NULL) {
  265. return;
  266. }
  267. if (gpDispInfo->fAnyPalette) {
  268. return;
  269. }
  270. cx = pghost->rcClient.right - pghost->rcClient.left;
  271. cy = pghost->rcClient.bottom - pghost->rcClient.top;
  272. hbm = GreCreateCompatibleBitmap(gpDispInfo->hdcScreen, cx, cy);
  273. if (hbm == NULL) {
  274. return;
  275. }
  276. hbmOld = GreSelectBitmap(ghdcMem, hbm);
  277. hbmOld2 = GreSelectBitmap(ghdcMem2, pghost->hbm);
  278. rc.left = rc.top = 0;
  279. rc.right = cx;
  280. rc.bottom = cy;
  281. FillRect(ghdcMem, &rc, SYSHBR(MENU));
  282. blend.BlendOp = AC_SRC_OVER;
  283. blend.BlendFlags = AC_MIRRORBITMAP;
  284. blend.AlphaFormat = 0;
  285. blend.SourceConstantAlpha = 150;
  286. GreAlphaBlend(ghdcMem, 0, 0, cx, cy, ghdcMem2, 0, 0, cx, cy, blend, NULL);
  287. GreSelectBitmap(ghdcMem, hbmOld);
  288. GreSelectBitmap(ghdcMem2, hbmOld2);
  289. GreDeleteObject(pghost->hbm);
  290. pghost->hbm = hbm;
  291. }
  292. #endif
  293. /***************************************************************************\
  294. * SaveVisualBits
  295. *
  296. \***************************************************************************/
  297. VOID SaveVisualBits(
  298. PGHOST pghost)
  299. {
  300. BOOL fSaveBits;
  301. PWND pwnd;
  302. HBITMAP hbmOld;
  303. int cx, cy;
  304. RECT rcT;
  305. HDC hdc;
  306. fSaveBits = FALSE;
  307. pwnd = pghost->pwnd;
  308. /*
  309. * Nothing to save if the window is completely invalid.
  310. */
  311. if (pwnd->hrgnUpdate != HRGN_FULL) {
  312. CalcVisRgn(&pghost->hrgn, pwnd, pwnd, DCX_CLIPSIBLINGS);
  313. /*
  314. * Only can save bits if the window is not completely obscured and
  315. * either there is no invalid bits or if there are bits left over
  316. * after we subtract the invalid bits from the visible area.
  317. */
  318. if (pghost->hrgn != NULL &&
  319. GreGetRgnBox(pghost->hrgn, &rcT) != NULLREGION) {
  320. if (pwnd->hrgnUpdate == NULL) {
  321. fSaveBits = TRUE;
  322. } else {
  323. /*
  324. * We'll use the bounding box of the invalid region of the
  325. * ghost window as an approximation of the total invalid
  326. * region, this way we won't have to go through all of the
  327. * children.
  328. */
  329. GreGetRgnBox(pwnd->hrgnUpdate, &rcT);
  330. SetRectRgnIndirect(ghrgnGDC, &rcT);
  331. if (SubtractRgn(pghost->hrgn, pghost->hrgn, ghrgnGDC) != NULLREGION) {
  332. fSaveBits = TRUE;
  333. }
  334. }
  335. }
  336. }
  337. /*
  338. * Now try to save the bits.
  339. */
  340. if (fSaveBits) {
  341. UserAssert(pghost->hrgn != NULL);
  342. cx = pwnd->rcClient.right - pwnd->rcClient.left;
  343. cy = pwnd->rcClient.bottom - pwnd->rcClient.top;
  344. if (pghost->hbm != NULL) {
  345. FRE_RIPMSG0(RIP_ERROR, "SaveVisaulBits: overriding pghost->hbm");
  346. }
  347. /*
  348. * Use NOVIDEOMEMORY here, because for the blend we'll have to be
  349. * reading from this bitmap and reading from video memory is slow
  350. * when the alpha isn't done by the graphics card but by GDI.
  351. */
  352. pghost->hbm = GreCreateCompatibleBitmap(gpDispInfo->hdcScreen, cx, cy | CCB_NOVIDEOMEMORY);
  353. guGhostBmpCreated++;
  354. if (pghost->hbm != NULL) {
  355. int dx, dy;
  356. dx = pghost->pwnd->rcClient.left - pghost->pwndGhost->rcClient.left;
  357. dy = pghost->pwnd->rcClient.top - pghost->pwndGhost->rcClient.top;
  358. /*
  359. * Get the visual bits rectangle in ghost client rect origin.
  360. */
  361. pghost->rcClient.left = dx;
  362. pghost->rcClient.top = dy;
  363. pghost->rcClient.right = dx + cx;
  364. pghost->rcClient.bottom = dy + cy;
  365. /*
  366. * Make the region originate in the ghost client rect origin.
  367. */
  368. GreOffsetRgn(pghost->hrgn,
  369. -pwnd->rcClient.left + dx,
  370. -pwnd->rcClient.top + dy);
  371. hbmOld = GreSelectBitmap(ghdcMem, pghost->hbm);
  372. hdc = _GetDC(pghost->pwnd);
  373. GreBitBlt(ghdcMem, 0, 0, cx, cy, hdc, 0, 0, SRCCOPY, 0);
  374. _ReleaseDC(hdc);
  375. GreSelectBitmap(ghdcMem, hbmOld);
  376. }
  377. }
  378. /*
  379. * Clean up the region if couldn't save the visual bits successfully.
  380. */
  381. if (pghost->hbm == NULL && pghost->hrgn != NULL) {
  382. GreDeleteObject(pghost->hrgn);
  383. pghost->hrgn = NULL;
  384. }
  385. }
  386. /***************************************************************************\
  387. * xxxAddWarningText
  388. *
  389. \***************************************************************************/
  390. VOID xxxAddWarningText(
  391. PWND pwnd)
  392. {
  393. WCHAR szText[CCHTITLEMAX];
  394. UINT cch, cchNR;
  395. LARGE_STRING strName;
  396. WCHAR szNR[MAXSTRING];
  397. ServerLoadString(hModuleWin, STR_NOT_RESPONDING, szNR, ARRAY_SIZE(szNR));
  398. /*
  399. * Add "Not responding" to the end of the title text.
  400. */
  401. cch = TextCopy(&pwnd->strName, szText, CCHTITLEMAX);
  402. cchNR = wcslen(szNR);
  403. cch = min(CCHTITLEMAX - cchNR - 1, cch);
  404. wcscpy(szText + cch, szNR);
  405. strName.bAnsi = FALSE;
  406. strName.Buffer = szText;
  407. strName.Length = (USHORT)((cch + cchNR) * sizeof(WCHAR));
  408. strName.MaximumLength = strName.Length + sizeof(UNICODE_NULL);
  409. xxxDefWindowProc(pwnd, WM_SETTEXT, 0, (LPARAM)&strName);
  410. }
  411. /***************************************************************************\
  412. * xxxCreateGhostWindow
  413. *
  414. \***************************************************************************/
  415. BOOL xxxCreateGhostWindow(
  416. PGHOST pghost)
  417. {
  418. PWND pwnd;
  419. PWND pwndGhost;
  420. PWND pwndOwner = NULL;
  421. PGHOST pghostOwner = NULL;
  422. PTHREADINFO pti;
  423. HWND hwnd, hwndGhost;
  424. TL tlpwndT1, tlpwndT2, tlpwndT3, tlpwndT4, tlpwndT5;
  425. PWND pwndPrev;
  426. DWORD dwFlags, style, ExStyle;
  427. PICON picon;
  428. LARGE_UNICODE_STRING str;
  429. UINT cbAlloc;
  430. BOOL fHasOwner = FALSE;
  431. if (gbCleanupInitiated) {
  432. FRE_RIPMSG0(RIP_ERROR, "Trying to create a ghost window while shutdown is in progress");
  433. return FALSE;
  434. }
  435. pwnd = pghost->pwnd;
  436. cbAlloc = pwnd->strName.Length + sizeof(WCHAR);
  437. str.Buffer = UserAllocPoolWithQuota(cbAlloc, TAG_GHOST);
  438. if (str.Buffer == NULL) {
  439. return FALSE;
  440. }
  441. str.MaximumLength = cbAlloc;
  442. str.Length = pwnd->strName.Length;
  443. str.bAnsi = FALSE;
  444. RtlCopyMemory(str.Buffer, pwnd->strName.Buffer, str.Length);
  445. str.Buffer[str.Length / sizeof(WCHAR)] = 0;
  446. ThreadLock(pwnd, &tlpwndT1);
  447. ThreadLockPool(ptiCurrent, str.Buffer, &tlpwndT2);
  448. if (pwnd->spwndOwner && ((pghostOwner = GhostFromPwnd(pwnd->spwndOwner)) != NULL) &&
  449. ((pwndOwner = pghostOwner->pwndGhost)) != NULL) {
  450. fHasOwner = TRUE;
  451. ThreadLock(pwndOwner, &tlpwndT3);
  452. }
  453. /*
  454. * Create the ghost window invisible and disallow it to be
  455. * maximized since it would be kind of pointless...
  456. * We don't remove the WS_MAXIMIZEBOX box here as
  457. * GetMonitorMaxArea() checks on WFMAXBOX to judge
  458. * if the window should be maximized to the full screen
  459. * area or to the working area (and it is being called during window creation).
  460. * See bug# 320325
  461. */
  462. ExStyle = (pwnd->ExStyle & ~(WS_EX_LAYERED | WS_EX_COMPOSITED)) & WS_EX_ALLVALID;
  463. style = pwnd->style & ~(WS_VISIBLE | WS_DISABLED);
  464. pwndGhost = xxxNVCreateWindowEx(ExStyle, (PLARGE_STRING)gatomGhost,
  465. (PLARGE_STRING)&str, style,
  466. pwnd->rcWindow.left, pwnd->rcWindow.top,
  467. pwnd->rcWindow.right - pwnd->rcWindow.left,
  468. pwnd->rcWindow.bottom - pwnd->rcWindow.top,
  469. pwndOwner, NULL, hModuleWin, NULL, WINVER);
  470. if (pwndGhost == NULL || (pghost = GhostFromPwnd(pwnd)) == NULL) {
  471. if (fHasOwner) {
  472. ThreadUnlock(&tlpwndT3);
  473. }
  474. ThreadUnlockAndFreePool(ptiCurrent, &tlpwndT2);
  475. ThreadUnlock(&tlpwndT1);
  476. return FALSE;
  477. }
  478. pghost->pwndGhost = pwndGhost;
  479. /*
  480. * Try to get large and small icons for the hung window. Since
  481. * we store the handles, it should be OK if these icons
  482. * somehow go away while the ghost window still exists.
  483. */
  484. if ((picon = GetWindowIcon(pwnd, TRUE)) != NULL) {
  485. InternalSetProp(pwndGhost, MAKEINTATOM(gpsi->atomIconProp),
  486. (HANDLE)PtoHq(picon), PROPF_INTERNAL | PROPF_NOPOOL);
  487. }
  488. if ((picon = GetWindowIcon(pwnd, FALSE)) != NULL) {
  489. InternalSetProp(pwndGhost, MAKEINTATOM(gpsi->atomIconSmProp),
  490. (HANDLE)PtoHq(picon), PROPF_INTERNAL | PROPF_NOPOOL);
  491. }
  492. /*
  493. * Now remove WFMAXBOX before painting the window.
  494. */
  495. ClrWF(pwndGhost, WFMAXBOX);
  496. SaveVisualBits(pghost);
  497. #if GHOST_AGGRESSIVE
  498. DimSavedBits(pghost);
  499. #endif
  500. /*
  501. * If the hung window is the active foreground window, allow
  502. * the activation to bring the ghost window to the foreground.
  503. */
  504. dwFlags = SWP_NOSIZE | SWP_NOMOVE;
  505. if (TestWF(pwnd, WFVISIBLE)) {
  506. dwFlags |= SWP_SHOWWINDOW;
  507. SetWF(pwnd, WEFGHOSTMAKEVISIBLE);
  508. }
  509. pti = GETPTI(pwnd);
  510. if (pti->pq == gpqForeground && pti->pq->spwndActive == pwnd) {
  511. PtiCurrent()->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE;
  512. } else {
  513. dwFlags |= SWP_NOACTIVATE;
  514. }
  515. /*
  516. * We will zorder the ghost window right above the hung window.
  517. */
  518. pwndPrev = _GetWindow(pwnd, GW_HWNDPREV);
  519. if (pwndPrev == pwndGhost) {
  520. dwFlags |= SWP_NOZORDER;
  521. pwndPrev = NULL;
  522. }
  523. ThreadLock(pwndGhost, &tlpwndT4);
  524. ThreadLock(pwndPrev, &tlpwndT5);
  525. /*
  526. * Make the shell remove the hung window from the taskbar. From
  527. * now on users will be dealing with the system menu on the
  528. * ghost window.
  529. */
  530. hwnd = HWq(pwnd);
  531. hwndGhost = HWq(pwndGhost);
  532. PostShellHookMessages(HSHELL_WINDOWREPLACING, (LPARAM)hwndGhost);
  533. PostShellHookMessages(HSHELL_WINDOWREPLACED, (LPARAM)hwnd);
  534. xxxCallHook(HSHELL_WINDOWREPLACED, (WPARAM)hwnd, (LPARAM)hwndGhost, WH_SHELL);
  535. xxxSetWindowPos(pwndGhost, pwndPrev, 0, 0, 0, 0, dwFlags);
  536. /*
  537. * Clear the visible bit on the hung window now and post our
  538. * queue message which will figure out when it wakes up.
  539. */
  540. if (TestWF(pwnd, WEFGHOSTMAKEVISIBLE)) {
  541. SetVisible(pwnd, SV_UNSET);
  542. }
  543. pti = GETPTI(pwnd);
  544. PostEventMessage(pti, pti->pq, QEVENT_HUNGTHREAD, pwnd, 0, 0, 0);
  545. zzzWindowEvent(EVENT_OBJECT_HIDE, pwnd, OBJID_WINDOW, INDEXID_CONTAINER, WEF_USEPWNDTHREAD);
  546. /*
  547. * If the end user clicked and held on the hung window, fake
  548. * this mouse click to the ghost window. This also ensures that
  549. * the attempted dragging operation will not be interrupted.
  550. */
  551. if (gspwndMouseOwner == pwnd) {
  552. Lock(&gspwndMouseOwner, pwndGhost);
  553. PostInputMessage(GETPTI(pwndGhost)->pq, pwndGhost, WM_LBUTTONDOWN,
  554. 0, MAKELONG((SHORT)gptCursorAsync.x, (SHORT)gptCursorAsync.y),
  555. 0, 0);
  556. }
  557. ThreadUnlock(&tlpwndT5);
  558. ThreadUnlock(&tlpwndT4);
  559. if (fHasOwner) {
  560. ThreadUnlock(&tlpwndT3);
  561. }
  562. ThreadUnlockAndFreePool(ptiCurrent, &tlpwndT2);
  563. ThreadUnlock(&tlpwndT1);
  564. return TRUE;
  565. }
  566. /***************************************************************************\
  567. * CleanupGhost
  568. *
  569. * Cleans up an ghost structure entry
  570. * Handles the case when the ghost thread got destroyed during callback
  571. * History:
  572. * 29-Nov-00 MSadek Created.
  573. \***************************************************************************/
  574. PWND CleanupGhost(
  575. PGHOST *ppghost,
  576. PGHOST pghost)
  577. {
  578. PWND pwndGhost;
  579. if (pghost->hrgn != NULL) {
  580. GreDeleteObject(pghost->hrgn);
  581. }
  582. if (pghost->hbm != NULL) {
  583. GreDeleteObject(pghost->hbm);
  584. guGhostBmpFreed++;
  585. pghost->hbm = NULL;
  586. }
  587. /*
  588. * We used the icon handles owned by the ghosted window, so
  589. * we will only remove the properties without actually
  590. * destroying the icons, as it would happen in DestroyWindow.
  591. */
  592. pwndGhost = pghost->pwndGhost;
  593. if (pwndGhost != NULL) {
  594. InternalRemoveProp(pwndGhost,
  595. MAKEINTATOM(gpsi->atomIconProp), PROPF_INTERNAL);
  596. InternalRemoveProp(pwndGhost,
  597. MAKEINTATOM(gpsi->atomIconSmProp), PROPF_INTERNAL);
  598. }
  599. UnlinkAndFreeGhost(ppghost, pghost);
  600. return pwndGhost;
  601. }
  602. /***************************************************************************\
  603. * ResetGhostThreadInfo
  604. *
  605. * Does a celanup for the ghost windows global linked list.
  606. * Add a comment reading that we need to clean up the list, if we die unexpectedly
  607. * because we don't know if a ghost thread will got created again.
  608. * History:
  609. * 12-Oct-00 MSadek Created.
  610. \***************************************************************************/
  611. VOID ResetGhostThreadInfo(
  612. PTHREADINFO pti)
  613. {
  614. PGHOST* ppghost;
  615. PGHOST pghost;
  616. UNREFERENCED_PARAMETER(pti);
  617. ppghost = &gpghostFirst;
  618. if (gpghostFirst != NULL) {
  619. RIPMSGF0(RIP_WARNING,
  620. "Ghost thread died while the ghost list is not empty");
  621. }
  622. while (*ppghost != NULL) {
  623. pghost = *ppghost;
  624. CleanupGhost(ppghost, pghost);
  625. }
  626. UserAssert(pti == gptiGhost);
  627. gptiGhost = NULL;
  628. }
  629. /***************************************************************************\
  630. * ScanGhosts
  631. *
  632. * This is our core function that will scan through the ghost list. It must
  633. * always be called in the context of the ghost thread which assures that all
  634. * creation and destruction of ghost windows happen in the context of that
  635. * thread. When in the ghost structure
  636. *
  637. * pwnd is NULL - the hung window has been destroyed or the thread it's on
  638. * woke up and so we need to destroy the ghost window.
  639. *
  640. * pwndGhost is NULL - the thread that pwnd is on is hung and so create the
  641. * ghost window for it.
  642. *
  643. * 6-2-1999 vadimg created
  644. \***************************************************************************/
  645. BOOL xxxScanGhosts(
  646. VOID)
  647. {
  648. PGHOST* ppghost;
  649. PGHOST pghost;
  650. PWND pwndTemp;
  651. ULONG uGhostUnlinked;
  652. CheckCritIn();
  653. ppghost = &gpghostFirst;
  654. while (*ppghost != NULL) {
  655. pghost = *ppghost;
  656. /*
  657. * pwnd is NULL means we need to destroy the ghost window. Note, we
  658. * need to remove the ghost from the list first to make sure that
  659. * xxxFreeWindow can't find the ghost in the list and try to destroy
  660. * the ghost window again causing an infinite loop.
  661. */
  662. if (pghost->pwnd == NULL) {
  663. pwndTemp = CleanupGhost(ppghost, pghost);
  664. if (pwndTemp != NULL) {
  665. uGhostUnlinked = guGhostUnlinked;
  666. xxxDestroyWindow(pwndTemp);
  667. /*
  668. * If we have called back, the pointers might be invalid.
  669. * Let's start the search again.
  670. */
  671. if (uGhostUnlinked != guGhostUnlinked) {
  672. ppghost = &gpghostFirst;
  673. continue;
  674. }
  675. }
  676. } else if (pghost->pwndGhost == NULL) {
  677. HWND hwnd;
  678. PGHOST pghostTemp = pghost;
  679. pwndTemp = pghost->pwnd;
  680. hwnd = PtoHq(pwndTemp);
  681. uGhostUnlinked = guGhostUnlinked;
  682. if (!xxxCreateGhostWindow(pghost)) {
  683. /*
  684. * If window creation failed, clean up by removing the struct
  685. * from the list altogether.
  686. */
  687. if (RevalidateCatHwnd(hwnd) && (pghost = GhostFromPwnd(pwndTemp))) {
  688. UserAssert(pghost->pwndGhost == NULL);
  689. RemoveGhost(pwndTemp);
  690. }
  691. } else {
  692. #if DBG
  693. if (RevalidateCatHwnd(hwnd) && (pghost = GhostFromPwnd(pwndTemp)) && (pghost == pghostTemp)) {
  694. UserAssert(pghost->pwndGhost != NULL);
  695. }
  696. #endif
  697. }
  698. /*
  699. * If we have called back, the pointers might be invalid. Let's
  700. * start the search again.
  701. */
  702. if (uGhostUnlinked != guGhostUnlinked) {
  703. ppghost = &gpghostFirst;
  704. continue;
  705. }
  706. } else {
  707. ppghost = &pghost->pghostNext;
  708. }
  709. }
  710. /*
  711. * If there are no more ghosts left, cleanup and terminate this
  712. * thread. by returning FALSE.
  713. */
  714. if (gpghostFirst == NULL) {
  715. return FALSE;
  716. }
  717. return TRUE;
  718. }
  719. /***************************************************************************\
  720. * GhostThread
  721. *
  722. * The thread that will service hung windows. It's created on demand and is
  723. * terminated when the last ghost window is destroyed.
  724. \***************************************************************************/
  725. VOID GhostThread(
  726. PDESKTOP pdesk)
  727. {
  728. NTSTATUS status;
  729. DWORD dwResult;
  730. MSG msg;
  731. PKEVENT rgEvents[2];
  732. BOOL fLoop = TRUE;
  733. BOOL fCSRSSThread = ISCSRSS();
  734. TL tlGhost;
  735. if (fCSRSSThread) {
  736. /*
  737. * Make this a GUI thread.
  738. */
  739. status = InitSystemThread(NULL);
  740. }
  741. EnterCrit();
  742. /*
  743. * Don't allow multiple ghost threads to be created.
  744. */
  745. if (NULL != gptiGhost) {
  746. LeaveCrit();
  747. return;
  748. }
  749. gptiGhost = PtiCurrent();
  750. ThreadLockPoolCleanup(gptiGhost, gptiGhost, &tlGhost, ResetGhostThreadInfo);
  751. /*
  752. * Try to assign this thread to the desktop. Any ghost windows can be
  753. * created only on that desktop.
  754. */
  755. if (fCSRSSThread) {
  756. if (!NT_SUCCESS(status) || !xxxSetThreadDesktop(NULL, pdesk)) {
  757. goto Cleanup;
  758. }
  759. }
  760. gptiGhost->pwinsta = pdesk->rpwinstaParent;
  761. rgEvents[0] = gpEventScanGhosts;
  762. /*
  763. * Scan the list, since gptiGhost was NULL up to now and thus no posted
  764. * messages could reach us.
  765. */
  766. while (fLoop) {
  767. /*
  768. * Wait for any message sent or posted to this queue, while calling
  769. * ProcessDeviceChanges whenever the mouse change event (pkeHidChange)
  770. * is set.
  771. */
  772. dwResult = xxxMsgWaitForMultipleObjects(1, rgEvents, NULL, NULL);
  773. /*
  774. * result tells us the type of event we have:
  775. * a message or a signalled handle
  776. *
  777. * if there are one or more messages in the queue ...
  778. */
  779. if (dwResult == WAIT_OBJECT_0) {
  780. fLoop = xxxScanGhosts();
  781. } else if (dwResult == STATUS_USER_APC){
  782. goto Cleanup;
  783. } else {
  784. UserAssert(dwResult == WAIT_OBJECT_0 + 1);
  785. while (xxxPeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
  786. xxxDispatchMessage(&msg);
  787. }
  788. }
  789. }
  790. Cleanup:
  791. ThreadUnlockPoolCleanup(gptiGhost, &tlGhost);
  792. ResetGhostThreadInfo(PtiCurrent());
  793. LeaveCrit();
  794. }
  795. /***************************************************************************\
  796. * xxxCreateGhost
  797. *
  798. * This function will create a ghost thread when needed and add a request
  799. * to create a ghost to the ghost list.
  800. \***************************************************************************/
  801. BOOL xxxCreateGhost(
  802. PWND pwnd)
  803. {
  804. USER_API_MSG m;
  805. NTSTATUS Status;
  806. PDESKTOP pdesk;
  807. BOOL bRemoteThread = FALSE;
  808. HANDLE UniqueProcessId = 0;
  809. CheckLock(pwnd);
  810. /*
  811. * Bail out early for winlogon windows.
  812. */
  813. pdesk = pwnd->head.rpdesk;
  814. if (pdesk == grpdeskLogon) {
  815. return FALSE;
  816. }
  817. /*
  818. * We can only service windows on the same desktop.
  819. */
  820. if (gptiGhost != NULL && gptiGhost->rpdesk != pdesk) {
  821. return FALSE;
  822. }
  823. /*
  824. * Don't try to ghost windows from the ghost thread itself.
  825. */
  826. if (GETPTI(pwnd) == gptiGhost) {
  827. return FALSE;
  828. }
  829. /*
  830. * Not much we can do if this hung window doesn't have a caption.
  831. */
  832. if (TestWF(pwnd, WFCAPTION) != LOBYTE(WFCAPTION)) {
  833. return FALSE;
  834. }
  835. /*
  836. * Try to create a ghost thread. Note that the event can have a value
  837. * though the thread is NULL. This could happen if the thread died
  838. * before making it to the kernel.
  839. */
  840. if (gptiGhost == NULL) {
  841. PPROCESSINFO ppi, ppiShellProcess = NULL;
  842. if (gpEventScanGhosts == NULL) {
  843. gpEventScanGhosts = CreateKernelEvent(SynchronizationEvent, FALSE);
  844. if (gpEventScanGhosts == NULL) {
  845. return FALSE;
  846. }
  847. }
  848. UserAssert (ISCSRSS());
  849. ppi = GETPTI(pwnd)->ppi;
  850. if (ppi->rpdeskStartup && ppi->rpdeskStartup->pDeskInfo) {
  851. ppiShellProcess = ppi->rpdeskStartup->pDeskInfo->ppiShellProcess;
  852. }
  853. if (ppiShellProcess && ppiShellProcess->Process != gpepCSRSS) {
  854. bRemoteThread = TRUE;
  855. UniqueProcessId = PsGetProcessId(ppiShellProcess->Process);
  856. }
  857. if (!InitCreateSystemThreadsMsg(&m, CST_GHOST, pdesk, UniqueProcessId, bRemoteThread)) {
  858. return FALSE;
  859. }
  860. /*
  861. * Since we are in CSRSS context use LpcRequestPort to send
  862. * LPC_DATAGRAM message type. Do not use LpcRequestWaitReplyPort
  863. * because it will send LPC_REQUEST which will fail (in server side).
  864. */
  865. LeaveCrit();
  866. Status = LpcRequestPort(CsrApiPort, (PPORT_MESSAGE)&m);
  867. EnterCrit();
  868. if (gpEventScanGhosts == NULL) {
  869. return FALSE;
  870. }
  871. if (!NT_SUCCESS(Status)) {
  872. return FALSE;
  873. }
  874. }
  875. if (!(TestWF(pwnd, WFINDESTROY) || TestWF(pwnd, WFDESTROYED))) {
  876. return AddGhostOwnersAndOwnees(pwnd);
  877. }
  878. return FALSE;
  879. }
  880. /***************************************************************************\
  881. * RemoveGhost
  882. *
  883. * This function is called from xxxFreeWindow to check and takes care
  884. * of business when pwnd is either a ghost or a hung window.
  885. \***************************************************************************/
  886. VOID RemoveGhost(
  887. PWND pwnd)
  888. {
  889. PGHOST* ppghost;
  890. PGHOST pghost;
  891. CheckCritIn();
  892. for (ppghost = &gpghostFirst; *ppghost != NULL;
  893. ppghost = &(*ppghost)->pghostNext) {
  894. pghost = *ppghost;
  895. /*
  896. * If this window matches the hung window, then set an event to
  897. * destroy the corresponding ghost window. If the ghost window hasn't
  898. * been created yet, we can nuke the structure in context.
  899. */
  900. if (pghost->pwnd == pwnd) {
  901. if (pghost->pwndGhost == NULL) {
  902. UnlinkAndFreeGhost(ppghost, pghost);
  903. } else {
  904. pghost->pwnd = NULL;
  905. KeSetEvent(gpEventScanGhosts, EVENT_INCREMENT, FALSE);
  906. }
  907. break;
  908. }
  909. /*
  910. * If this window matches the ghost window, just remove the
  911. * structure from the list.
  912. */
  913. if (pghost->pwndGhost == pwnd) {
  914. UnlinkAndFreeGhost(ppghost, pghost);
  915. break;
  916. }
  917. }
  918. }
  919. /***************************************************************************\
  920. * PaintGhost
  921. *
  922. * Draw the ghost window look.
  923. \***************************************************************************/
  924. VOID PaintGhost(
  925. PWND pwnd,
  926. HDC hdc)
  927. {
  928. PGHOST pghost;
  929. HBITMAP hbmOld;
  930. RECT rc;
  931. LONG cx, cy;
  932. #if GHOST_AGGRESSIVE
  933. HFONT hfont, hfontOld;
  934. WCHAR szHung[MAXSTRING];
  935. ULONG cch;
  936. SIZE size;
  937. LONG xText;
  938. LOGFONTW lf;
  939. #endif
  940. pghost = GhostFromGhostPwnd(pwnd);
  941. if (pghost == NULL) {
  942. return;
  943. }
  944. rc.left = rc.top = 0;
  945. rc.right = pwnd->rcClient.right - pwnd->rcClient.left;
  946. rc.bottom = pwnd->rcClient.bottom - pwnd->rcClient.top;
  947. if (pghost->hbm != NULL) {
  948. cx = pghost->rcClient.right - pghost->rcClient.left;
  949. cy = pghost->rcClient.bottom - pghost->rcClient.top;
  950. hbmOld = GreSelectBitmap(ghdcMem, pghost->hbm);
  951. GreExtSelectClipRgn(hdc, pghost->hrgn, RGN_COPY);
  952. GreBitBlt(hdc, pghost->rcClient.left, pghost->rcClient.top,
  953. cx, cy, ghdcMem, 0, 0, SRCCOPY, 0);
  954. GreSelectBitmap(ghdcMem, hbmOld);
  955. SetRectRgnIndirect(ghrgnGDC, &rc);
  956. SubtractRgn(ghrgnGDC, ghrgnGDC, pghost->hrgn);
  957. GreExtSelectClipRgn(hdc, ghrgnGDC, RGN_COPY);
  958. }
  959. FillRect(hdc, &rc, SYSHBR(WINDOW));
  960. GreExtSelectClipRgn(hdc, NULL, RGN_COPY);
  961. #if GHOST_AGGRESSIVE
  962. ServerLoadString(hModuleWin, STR_HUNG, szHung, ARRAY_SIZE(szHung));
  963. cch = wcslen(szHung);
  964. GreSetTextColor(hdc, RGB(0, 0, 255));
  965. GreSetBkColor(hdc, RGB(255, 255, 0));
  966. GreExtGetObjectW(gpsi->hCaptionFont, sizeof(LOGFONTW), &lf);
  967. lf.lfHeight = (lf.lfHeight * 3) / 2;
  968. lf.lfWeight = FW_BOLD;
  969. hfont = GreCreateFontIndirectW(&lf);
  970. hfontOld = GreSelectFont(hdc, hfont);
  971. GreGetTextExtentW(hdc, szHung, cch, &size, GGTE_WIN3_EXTENT);
  972. xText = max(0, ((rc.right - rc.left) - size.cx) / 2);
  973. GreExtTextOutW(hdc, xText, 0, 0, NULL, szHung, cch, NULL);
  974. GreSelectFont(hdc, hfontOld);
  975. GreDeleteObject(hfont);
  976. #endif
  977. }
  978. /***************************************************************************\
  979. * xxxGhostWndProc
  980. *
  981. * Processes messages for ghost windows.
  982. \***************************************************************************/
  983. LRESULT xxxGhostWndProc(
  984. PWND pwnd,
  985. UINT uMsg,
  986. WPARAM wParam,
  987. LPARAM lParam)
  988. {
  989. PGHOST pghost;
  990. VALIDATECLASSANDSIZE(pwnd, uMsg, wParam, lParam, FNID_GHOST, WM_NCCREATE);
  991. switch (uMsg) {
  992. case WM_CLOSE:
  993. pghost = GhostFromGhostPwnd(pwnd);
  994. /*
  995. * Do the end task on the hung thread when the user tries to close
  996. * the ghost window.
  997. */
  998. if (pghost != NULL && pghost->pwnd != NULL) {
  999. PostShellHookMessages(HSHELL_ENDTASK, (LPARAM)HWq(pghost->pwnd));
  1000. }
  1001. return 0;
  1002. case WM_LBUTTONDOWN:
  1003. pghost = GhostFromGhostPwnd(pwnd);
  1004. if (pghost != NULL) {
  1005. if (pghost->fWarningText) {
  1006. return 0;
  1007. } else {
  1008. pghost->fWarningText = TRUE;
  1009. }
  1010. }
  1011. xxxAddWarningText(pwnd);
  1012. return 0;
  1013. case WM_SIZE:
  1014. /*
  1015. * Since we have wrapped, flowing text, repaint it when sizing.
  1016. */
  1017. xxxInvalidateRect(pwnd, NULL, TRUE);
  1018. return 0;
  1019. case WM_ERASEBKGND:
  1020. PaintGhost(pwnd, (HDC)wParam);
  1021. return 1;
  1022. case WM_SETCURSOR:
  1023. /*
  1024. * Show the hung app cursor over the client.
  1025. */
  1026. if (LOWORD(lParam) == HTCLIENT) {
  1027. zzzSetCursor(SYSCUR(WAIT));
  1028. return 1;
  1029. }
  1030. case WM_EXITSIZEMOVE:
  1031. pghost = GhostFromGhostPwnd(pwnd);
  1032. if (pghost != NULL) {
  1033. pghost->fSizedOrMoved = TRUE;
  1034. }
  1035. /*
  1036. * FALL THROUGH to DWP.
  1037. */
  1038. default:
  1039. return xxxDefWindowProc(pwnd, uMsg, wParam, lParam);
  1040. }
  1041. }
  1042. #endif