/***************************** Module Header ******************************\ * Module Name: ghost.c * * Copyright (c) 1985-1999, Microsoft Corporation * * Ghost support for unresponsive windows. * * History: * 23-Apr-1999 vadimg created \***************************************************************************/ #include "precomp.h" #pragma hdrstop #ifdef HUNGAPP_GHOSTING typedef struct tagGHOST *PGHOST; typedef struct tagGHOST { PGHOST pghostNext; // next structure in the linked list PWND pwnd; // hung window we're trying to ghost PWND pwndGhost; // ghost window created for this pwnd HBITMAP hbm; // saved visual bits for the ghosted window HRGN hrgn; // what visual bits are available to us RECT rcClient; // client rect in window's coordinates UINT fWarningText : 1; // whether the warning text has been added UINT fSizedOrMoved : 1; } GHOST, *PGHOST; PGHOST gpghostFirst; // pointer to the start of the ghost list PTHREADINFO gptiGhost; // pointer to ghost threadinfo ULONG guGhostLinked; ULONG guGhostUnlinked; ULONG guGhostBmpCreated; ULONG guGhostBmpFreed; #define XY_MARGIN 10 #define MAXSTRING 256 #define GHOST_MAX 50 /***************************************************************************\ * _DisableProcessWindowsGhosting * * Diables ghosting windows for the calling process. * History: * 31-May-01 MSadek Created. \***************************************************************************/ VOID _DisableProcessWindowsGhosting( VOID) { PpiCurrent()->W32PF_Flags |= W32PF_DISABLEWINDOWSGHOSTING; } /***************************************************************************\ * GhostFromGhostPwnd * * Find the ghost structure for this ghost window. \***************************************************************************/ PGHOST GhostFromGhostPwnd( PWND pwndGhost) { PGHOST pghost; for (pghost = gpghostFirst; pghost != NULL; pghost = pghost->pghostNext) { if (pghost->pwndGhost == pwndGhost) { return pghost; } } return NULL; } /***************************************************************************\ * GhostFromPwnd * \***************************************************************************/ PGHOST GhostFromPwnd( PWND pwnd) { PGHOST pghost; for (pghost = gpghostFirst; pghost != NULL; pghost = pghost->pghostNext) { if (pghost->pwnd == pwnd) { return pghost; } } return NULL; } /***************************************************************************\ * FindGhost * * Find a ghost that corresponds to this hung window. \***************************************************************************/ PWND FindGhost( PWND pwnd) { PGHOST pghost = GhostFromPwnd(pwnd); if (pghost != NULL) { return pghost->pwndGhost; } else { return NULL; } } /***************************************************************************\ * GhostSizedOrMoved * * Returns true if the ghost window corresponding to a window was sized or moved * through its life time. \***************************************************************************/ BOOL GhostSizedOrMoved( PWND pwnd) { PGHOST pghost = GhostFromPwnd(pwnd); if (pghost != NULL) { return pghost->fSizedOrMoved; } else { return FALSE; } } /***************************************************************************\ * UnlinkAndFreeGhost * * This function unlinks a ghost element from the list and free its allocated * memory. \***************************************************************************/ _inline VOID UnlinkAndFreeGhost( PGHOST* ppghost, PGHOST pghost) { UserAssert(pghost->hbm == NULL); *ppghost = pghost->pghostNext; UserFreePool(pghost); guGhostUnlinked++; } /***************************************************************************\ * GetWindowIcon * * Get a window icon. If asked try the large icon first, then the small icon, * then the windows logo icon. \***************************************************************************/ PICON GetWindowIcon( PWND pwnd, BOOL fBigIcon) { HICON hicon; PICON picon = NULL; if (fBigIcon) { hicon = (HICON)_GetProp(pwnd, MAKEINTATOM(gpsi->atomIconProp), TRUE); if (hicon) { picon = (PICON)HMValidateHandleNoRip(hicon, TYPE_CURSOR); } if (picon == NULL) { picon = pwnd->pcls->spicn; } } if (picon == NULL) { hicon = (HICON)_GetProp(pwnd, MAKEINTATOM(gpsi->atomIconSmProp), TRUE); if (hicon != NULL) { picon = (PICON)HMValidateHandleNoRip(hicon, TYPE_CURSOR); } if (picon == NULL) { picon = pwnd->pcls->spicnSm; } } return picon; } /***************************************************************************\ * AddGhost * * Add a new ghost structure for a hung window. \***************************************************************************/ BOOL AddGhost( PWND pwnd) { PGHOST pghost; CheckCritIn(); /* * Need to limit the maximum number of ghost windows created as not to * result into thread starvation. */ if (guGhostLinked - guGhostUnlinked == GHOST_MAX) { return FALSE; } if ((pghost = (PGHOST)UserAllocPoolZInit(sizeof(GHOST), TAG_GHOST)) == NULL) { return FALSE; } pghost->pghostNext = gpghostFirst; gpghostFirst = pghost; pghost->pwnd = pwnd; /* * When pwndGhost is NULL, the ghost thread will try to create a ghost * window for this hung window. */ KeSetEvent(gpEventScanGhosts, EVENT_INCREMENT, FALSE); guGhostLinked++; return TRUE; } BOOL AddOwnedWindowToGhostList( PWND pwndRoot, PWND pwndOrg) { PWND pwnd = NULL; while (pwnd = NextOwnedWindow(pwnd, pwndRoot, pwndRoot->spwndParent)) { if (!AddOwnedWindowToGhostList(pwnd, pwndOrg)) { return FALSE; } /* * We need to add the bottom window on the chain first to the ghost * list because we scan the list from the head thus, ensure that the * owned window is already created at the time we create the ownee * ghost. */ if (GhostFromPwnd(pwnd) == NULL) { if (!AddGhost(pwnd)) { return FALSE; } if (GETPTI(pwndOrg) != GETPTI(pwndRoot)) { RIPMSGF4(RIP_WARNING, "Cross thread ghosting pwnd: 0x%p pti 0x%p, pwndRoot: 0x%p pti 0x%p", pwndOrg, GETPTI(pwndOrg), pwndRoot, GETPTI(pwndRoot)); } } } return TRUE; } BOOL AddGhostOwnersAndOwnees( PWND pwnd) { PWND pwndRoot = pwnd; /* * Get the topmost owner window in the chain. */ while(pwndRoot->spwndOwner != NULL) { pwndRoot = pwndRoot->spwndOwner; } /* * Now starting form that window, walk the whole ownee tree. */ if (!AddOwnedWindowToGhostList(pwndRoot, pwnd)) { return FALSE; } /* * For the topmost window (or the only single window if there is no Owner / Ownee * relationship at all, add the window to the ghost list */ if (GhostFromPwnd(pwndRoot) == NULL) { if (!AddGhost(pwndRoot)) { return FALSE; } if (GETPTI(pwnd) != GETPTI(pwndRoot)) { RIPMSGF4(RIP_WARNING, "Cross thread ghosting pwnd: 0x%p pti 0x%p, pwndRoot: 0x%p pti 0x%p", pwnd, GETPTI(pwnd), pwndRoot, GETPTI(pwndRoot)); } } return TRUE; } #if GHOST_AGGRESSIVE /***************************************************************************\ * DimSavedBits * \***************************************************************************/ VOID DimSavedBits( PGHOST pghost) { HBITMAP hbm, hbmOld, hbmOld2; LONG cx, cy; RECT rc; BLENDFUNCTION blend; if (pghost->hbm == NULL) { return; } if (gpDispInfo->fAnyPalette) { return; } cx = pghost->rcClient.right - pghost->rcClient.left; cy = pghost->rcClient.bottom - pghost->rcClient.top; hbm = GreCreateCompatibleBitmap(gpDispInfo->hdcScreen, cx, cy); if (hbm == NULL) { return; } hbmOld = GreSelectBitmap(ghdcMem, hbm); hbmOld2 = GreSelectBitmap(ghdcMem2, pghost->hbm); rc.left = rc.top = 0; rc.right = cx; rc.bottom = cy; FillRect(ghdcMem, &rc, SYSHBR(MENU)); blend.BlendOp = AC_SRC_OVER; blend.BlendFlags = AC_MIRRORBITMAP; blend.AlphaFormat = 0; blend.SourceConstantAlpha = 150; GreAlphaBlend(ghdcMem, 0, 0, cx, cy, ghdcMem2, 0, 0, cx, cy, blend, NULL); GreSelectBitmap(ghdcMem, hbmOld); GreSelectBitmap(ghdcMem2, hbmOld2); GreDeleteObject(pghost->hbm); pghost->hbm = hbm; } #endif /***************************************************************************\ * SaveVisualBits * \***************************************************************************/ VOID SaveVisualBits( PGHOST pghost) { BOOL fSaveBits; PWND pwnd; HBITMAP hbmOld; int cx, cy; RECT rcT; HDC hdc; fSaveBits = FALSE; pwnd = pghost->pwnd; /* * Nothing to save if the window is completely invalid. */ if (pwnd->hrgnUpdate != HRGN_FULL) { CalcVisRgn(&pghost->hrgn, pwnd, pwnd, DCX_CLIPSIBLINGS); /* * Only can save bits if the window is not completely obscured and * either there is no invalid bits or if there are bits left over * after we subtract the invalid bits from the visible area. */ if (pghost->hrgn != NULL && GreGetRgnBox(pghost->hrgn, &rcT) != NULLREGION) { if (pwnd->hrgnUpdate == NULL) { fSaveBits = TRUE; } else { /* * We'll use the bounding box of the invalid region of the * ghost window as an approximation of the total invalid * region, this way we won't have to go through all of the * children. */ GreGetRgnBox(pwnd->hrgnUpdate, &rcT); SetRectRgnIndirect(ghrgnGDC, &rcT); if (SubtractRgn(pghost->hrgn, pghost->hrgn, ghrgnGDC) != NULLREGION) { fSaveBits = TRUE; } } } } /* * Now try to save the bits. */ if (fSaveBits) { UserAssert(pghost->hrgn != NULL); cx = pwnd->rcClient.right - pwnd->rcClient.left; cy = pwnd->rcClient.bottom - pwnd->rcClient.top; if (pghost->hbm != NULL) { FRE_RIPMSG0(RIP_ERROR, "SaveVisaulBits: overriding pghost->hbm"); } /* * Use NOVIDEOMEMORY here, because for the blend we'll have to be * reading from this bitmap and reading from video memory is slow * when the alpha isn't done by the graphics card but by GDI. */ pghost->hbm = GreCreateCompatibleBitmap(gpDispInfo->hdcScreen, cx, cy | CCB_NOVIDEOMEMORY); guGhostBmpCreated++; if (pghost->hbm != NULL) { int dx, dy; dx = pghost->pwnd->rcClient.left - pghost->pwndGhost->rcClient.left; dy = pghost->pwnd->rcClient.top - pghost->pwndGhost->rcClient.top; /* * Get the visual bits rectangle in ghost client rect origin. */ pghost->rcClient.left = dx; pghost->rcClient.top = dy; pghost->rcClient.right = dx + cx; pghost->rcClient.bottom = dy + cy; /* * Make the region originate in the ghost client rect origin. */ GreOffsetRgn(pghost->hrgn, -pwnd->rcClient.left + dx, -pwnd->rcClient.top + dy); hbmOld = GreSelectBitmap(ghdcMem, pghost->hbm); hdc = _GetDC(pghost->pwnd); GreBitBlt(ghdcMem, 0, 0, cx, cy, hdc, 0, 0, SRCCOPY, 0); _ReleaseDC(hdc); GreSelectBitmap(ghdcMem, hbmOld); } } /* * Clean up the region if couldn't save the visual bits successfully. */ if (pghost->hbm == NULL && pghost->hrgn != NULL) { GreDeleteObject(pghost->hrgn); pghost->hrgn = NULL; } } /***************************************************************************\ * xxxAddWarningText * \***************************************************************************/ VOID xxxAddWarningText( PWND pwnd) { WCHAR szText[CCHTITLEMAX]; UINT cch, cchNR; LARGE_STRING strName; WCHAR szNR[MAXSTRING]; ServerLoadString(hModuleWin, STR_NOT_RESPONDING, szNR, ARRAY_SIZE(szNR)); /* * Add "Not responding" to the end of the title text. */ cch = TextCopy(&pwnd->strName, szText, CCHTITLEMAX); cchNR = wcslen(szNR); cch = min(CCHTITLEMAX - cchNR - 1, cch); wcscpy(szText + cch, szNR); strName.bAnsi = FALSE; strName.Buffer = szText; strName.Length = (USHORT)((cch + cchNR) * sizeof(WCHAR)); strName.MaximumLength = strName.Length + sizeof(UNICODE_NULL); xxxDefWindowProc(pwnd, WM_SETTEXT, 0, (LPARAM)&strName); } /***************************************************************************\ * xxxCreateGhostWindow * \***************************************************************************/ BOOL xxxCreateGhostWindow( PGHOST pghost) { PWND pwnd; PWND pwndGhost; PWND pwndOwner = NULL; PGHOST pghostOwner = NULL; PTHREADINFO pti; HWND hwnd, hwndGhost; TL tlpwndT1, tlpwndT2, tlpwndT3, tlpwndT4, tlpwndT5; PWND pwndPrev; DWORD dwFlags, style, ExStyle; PICON picon; LARGE_UNICODE_STRING str; UINT cbAlloc; BOOL fHasOwner = FALSE; if (gbCleanupInitiated) { FRE_RIPMSG0(RIP_ERROR, "Trying to create a ghost window while shutdown is in progress"); return FALSE; } pwnd = pghost->pwnd; cbAlloc = pwnd->strName.Length + sizeof(WCHAR); str.Buffer = UserAllocPoolWithQuota(cbAlloc, TAG_GHOST); if (str.Buffer == NULL) { return FALSE; } str.MaximumLength = cbAlloc; str.Length = pwnd->strName.Length; str.bAnsi = FALSE; RtlCopyMemory(str.Buffer, pwnd->strName.Buffer, str.Length); str.Buffer[str.Length / sizeof(WCHAR)] = 0; ThreadLock(pwnd, &tlpwndT1); ThreadLockPool(ptiCurrent, str.Buffer, &tlpwndT2); if (pwnd->spwndOwner && ((pghostOwner = GhostFromPwnd(pwnd->spwndOwner)) != NULL) && ((pwndOwner = pghostOwner->pwndGhost)) != NULL) { fHasOwner = TRUE; ThreadLock(pwndOwner, &tlpwndT3); } /* * Create the ghost window invisible and disallow it to be * maximized since it would be kind of pointless... * We don't remove the WS_MAXIMIZEBOX box here as * GetMonitorMaxArea() checks on WFMAXBOX to judge * if the window should be maximized to the full screen * area or to the working area (and it is being called during window creation). * See bug# 320325 */ ExStyle = (pwnd->ExStyle & ~(WS_EX_LAYERED | WS_EX_COMPOSITED)) & WS_EX_ALLVALID; style = pwnd->style & ~(WS_VISIBLE | WS_DISABLED); pwndGhost = xxxNVCreateWindowEx(ExStyle, (PLARGE_STRING)gatomGhost, (PLARGE_STRING)&str, style, pwnd->rcWindow.left, pwnd->rcWindow.top, pwnd->rcWindow.right - pwnd->rcWindow.left, pwnd->rcWindow.bottom - pwnd->rcWindow.top, pwndOwner, NULL, hModuleWin, NULL, WINVER); if (pwndGhost == NULL || (pghost = GhostFromPwnd(pwnd)) == NULL) { if (fHasOwner) { ThreadUnlock(&tlpwndT3); } ThreadUnlockAndFreePool(ptiCurrent, &tlpwndT2); ThreadUnlock(&tlpwndT1); return FALSE; } pghost->pwndGhost = pwndGhost; /* * Try to get large and small icons for the hung window. Since * we store the handles, it should be OK if these icons * somehow go away while the ghost window still exists. */ if ((picon = GetWindowIcon(pwnd, TRUE)) != NULL) { InternalSetProp(pwndGhost, MAKEINTATOM(gpsi->atomIconProp), (HANDLE)PtoHq(picon), PROPF_INTERNAL | PROPF_NOPOOL); } if ((picon = GetWindowIcon(pwnd, FALSE)) != NULL) { InternalSetProp(pwndGhost, MAKEINTATOM(gpsi->atomIconSmProp), (HANDLE)PtoHq(picon), PROPF_INTERNAL | PROPF_NOPOOL); } /* * Now remove WFMAXBOX before painting the window. */ ClrWF(pwndGhost, WFMAXBOX); SaveVisualBits(pghost); #if GHOST_AGGRESSIVE DimSavedBits(pghost); #endif /* * If the hung window is the active foreground window, allow * the activation to bring the ghost window to the foreground. */ dwFlags = SWP_NOSIZE | SWP_NOMOVE; if (TestWF(pwnd, WFVISIBLE)) { dwFlags |= SWP_SHOWWINDOW; SetWF(pwnd, WEFGHOSTMAKEVISIBLE); } pti = GETPTI(pwnd); if (pti->pq == gpqForeground && pti->pq->spwndActive == pwnd) { PtiCurrent()->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE; } else { dwFlags |= SWP_NOACTIVATE; } /* * We will zorder the ghost window right above the hung window. */ pwndPrev = _GetWindow(pwnd, GW_HWNDPREV); if (pwndPrev == pwndGhost) { dwFlags |= SWP_NOZORDER; pwndPrev = NULL; } ThreadLock(pwndGhost, &tlpwndT4); ThreadLock(pwndPrev, &tlpwndT5); /* * Make the shell remove the hung window from the taskbar. From * now on users will be dealing with the system menu on the * ghost window. */ hwnd = HWq(pwnd); hwndGhost = HWq(pwndGhost); PostShellHookMessages(HSHELL_WINDOWREPLACING, (LPARAM)hwndGhost); PostShellHookMessages(HSHELL_WINDOWREPLACED, (LPARAM)hwnd); xxxCallHook(HSHELL_WINDOWREPLACED, (WPARAM)hwnd, (LPARAM)hwndGhost, WH_SHELL); xxxSetWindowPos(pwndGhost, pwndPrev, 0, 0, 0, 0, dwFlags); /* * Clear the visible bit on the hung window now and post our * queue message which will figure out when it wakes up. */ if (TestWF(pwnd, WEFGHOSTMAKEVISIBLE)) { SetVisible(pwnd, SV_UNSET); } pti = GETPTI(pwnd); PostEventMessage(pti, pti->pq, QEVENT_HUNGTHREAD, pwnd, 0, 0, 0); zzzWindowEvent(EVENT_OBJECT_HIDE, pwnd, OBJID_WINDOW, INDEXID_CONTAINER, WEF_USEPWNDTHREAD); /* * If the end user clicked and held on the hung window, fake * this mouse click to the ghost window. This also ensures that * the attempted dragging operation will not be interrupted. */ if (gspwndMouseOwner == pwnd) { Lock(&gspwndMouseOwner, pwndGhost); PostInputMessage(GETPTI(pwndGhost)->pq, pwndGhost, WM_LBUTTONDOWN, 0, MAKELONG((SHORT)gptCursorAsync.x, (SHORT)gptCursorAsync.y), 0, 0); } ThreadUnlock(&tlpwndT5); ThreadUnlock(&tlpwndT4); if (fHasOwner) { ThreadUnlock(&tlpwndT3); } ThreadUnlockAndFreePool(ptiCurrent, &tlpwndT2); ThreadUnlock(&tlpwndT1); return TRUE; } /***************************************************************************\ * CleanupGhost * * Cleans up an ghost structure entry * Handles the case when the ghost thread got destroyed during callback * History: * 29-Nov-00 MSadek Created. \***************************************************************************/ PWND CleanupGhost( PGHOST *ppghost, PGHOST pghost) { PWND pwndGhost; if (pghost->hrgn != NULL) { GreDeleteObject(pghost->hrgn); } if (pghost->hbm != NULL) { GreDeleteObject(pghost->hbm); guGhostBmpFreed++; pghost->hbm = NULL; } /* * We used the icon handles owned by the ghosted window, so * we will only remove the properties without actually * destroying the icons, as it would happen in DestroyWindow. */ pwndGhost = pghost->pwndGhost; if (pwndGhost != NULL) { InternalRemoveProp(pwndGhost, MAKEINTATOM(gpsi->atomIconProp), PROPF_INTERNAL); InternalRemoveProp(pwndGhost, MAKEINTATOM(gpsi->atomIconSmProp), PROPF_INTERNAL); } UnlinkAndFreeGhost(ppghost, pghost); return pwndGhost; } /***************************************************************************\ * ResetGhostThreadInfo * * Does a celanup for the ghost windows global linked list. * Add a comment reading that we need to clean up the list, if we die unexpectedly * because we don't know if a ghost thread will got created again. * History: * 12-Oct-00 MSadek Created. \***************************************************************************/ VOID ResetGhostThreadInfo( PTHREADINFO pti) { PGHOST* ppghost; PGHOST pghost; UNREFERENCED_PARAMETER(pti); ppghost = &gpghostFirst; if (gpghostFirst != NULL) { RIPMSGF0(RIP_WARNING, "Ghost thread died while the ghost list is not empty"); } while (*ppghost != NULL) { pghost = *ppghost; CleanupGhost(ppghost, pghost); } UserAssert(pti == gptiGhost); gptiGhost = NULL; } /***************************************************************************\ * ScanGhosts * * This is our core function that will scan through the ghost list. It must * always be called in the context of the ghost thread which assures that all * creation and destruction of ghost windows happen in the context of that * thread. When in the ghost structure * * pwnd is NULL - the hung window has been destroyed or the thread it's on * woke up and so we need to destroy the ghost window. * * pwndGhost is NULL - the thread that pwnd is on is hung and so create the * ghost window for it. * * 6-2-1999 vadimg created \***************************************************************************/ BOOL xxxScanGhosts( VOID) { PGHOST* ppghost; PGHOST pghost; PWND pwndTemp; ULONG uGhostUnlinked; CheckCritIn(); ppghost = &gpghostFirst; while (*ppghost != NULL) { pghost = *ppghost; /* * pwnd is NULL means we need to destroy the ghost window. Note, we * need to remove the ghost from the list first to make sure that * xxxFreeWindow can't find the ghost in the list and try to destroy * the ghost window again causing an infinite loop. */ if (pghost->pwnd == NULL) { pwndTemp = CleanupGhost(ppghost, pghost); if (pwndTemp != NULL) { uGhostUnlinked = guGhostUnlinked; xxxDestroyWindow(pwndTemp); /* * If we have called back, the pointers might be invalid. * Let's start the search again. */ if (uGhostUnlinked != guGhostUnlinked) { ppghost = &gpghostFirst; continue; } } } else if (pghost->pwndGhost == NULL) { HWND hwnd; PGHOST pghostTemp = pghost; pwndTemp = pghost->pwnd; hwnd = PtoHq(pwndTemp); uGhostUnlinked = guGhostUnlinked; if (!xxxCreateGhostWindow(pghost)) { /* * If window creation failed, clean up by removing the struct * from the list altogether. */ if (RevalidateCatHwnd(hwnd) && (pghost = GhostFromPwnd(pwndTemp))) { UserAssert(pghost->pwndGhost == NULL); RemoveGhost(pwndTemp); } } else { #if DBG if (RevalidateCatHwnd(hwnd) && (pghost = GhostFromPwnd(pwndTemp)) && (pghost == pghostTemp)) { UserAssert(pghost->pwndGhost != NULL); } #endif } /* * If we have called back, the pointers might be invalid. Let's * start the search again. */ if (uGhostUnlinked != guGhostUnlinked) { ppghost = &gpghostFirst; continue; } } else { ppghost = &pghost->pghostNext; } } /* * If there are no more ghosts left, cleanup and terminate this * thread. by returning FALSE. */ if (gpghostFirst == NULL) { return FALSE; } return TRUE; } /***************************************************************************\ * GhostThread * * The thread that will service hung windows. It's created on demand and is * terminated when the last ghost window is destroyed. \***************************************************************************/ VOID GhostThread( PDESKTOP pdesk) { NTSTATUS status; DWORD dwResult; MSG msg; PKEVENT rgEvents[2]; BOOL fLoop = TRUE; BOOL fCSRSSThread = ISCSRSS(); TL tlGhost; if (fCSRSSThread) { /* * Make this a GUI thread. */ status = InitSystemThread(NULL); } EnterCrit(); /* * Don't allow multiple ghost threads to be created. */ if (NULL != gptiGhost) { LeaveCrit(); return; } gptiGhost = PtiCurrent(); ThreadLockPoolCleanup(gptiGhost, gptiGhost, &tlGhost, ResetGhostThreadInfo); /* * Try to assign this thread to the desktop. Any ghost windows can be * created only on that desktop. */ if (fCSRSSThread) { if (!NT_SUCCESS(status) || !xxxSetThreadDesktop(NULL, pdesk)) { goto Cleanup; } } gptiGhost->pwinsta = pdesk->rpwinstaParent; rgEvents[0] = gpEventScanGhosts; /* * Scan the list, since gptiGhost was NULL up to now and thus no posted * messages could reach us. */ while (fLoop) { /* * Wait for any message sent or posted to this queue, while calling * ProcessDeviceChanges whenever the mouse change event (pkeHidChange) * is set. */ dwResult = xxxMsgWaitForMultipleObjects(1, rgEvents, NULL, NULL); /* * result tells us the type of event we have: * a message or a signalled handle * * if there are one or more messages in the queue ... */ if (dwResult == WAIT_OBJECT_0) { fLoop = xxxScanGhosts(); } else if (dwResult == STATUS_USER_APC){ goto Cleanup; } else { UserAssert(dwResult == WAIT_OBJECT_0 + 1); while (xxxPeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { xxxDispatchMessage(&msg); } } } Cleanup: ThreadUnlockPoolCleanup(gptiGhost, &tlGhost); ResetGhostThreadInfo(PtiCurrent()); LeaveCrit(); } /***************************************************************************\ * xxxCreateGhost * * This function will create a ghost thread when needed and add a request * to create a ghost to the ghost list. \***************************************************************************/ BOOL xxxCreateGhost( PWND pwnd) { USER_API_MSG m; NTSTATUS Status; PDESKTOP pdesk; BOOL bRemoteThread = FALSE; HANDLE UniqueProcessId = 0; CheckLock(pwnd); /* * Bail out early for winlogon windows. */ pdesk = pwnd->head.rpdesk; if (pdesk == grpdeskLogon) { return FALSE; } /* * We can only service windows on the same desktop. */ if (gptiGhost != NULL && gptiGhost->rpdesk != pdesk) { return FALSE; } /* * Don't try to ghost windows from the ghost thread itself. */ if (GETPTI(pwnd) == gptiGhost) { return FALSE; } /* * Not much we can do if this hung window doesn't have a caption. */ if (TestWF(pwnd, WFCAPTION) != LOBYTE(WFCAPTION)) { return FALSE; } /* * Try to create a ghost thread. Note that the event can have a value * though the thread is NULL. This could happen if the thread died * before making it to the kernel. */ if (gptiGhost == NULL) { PPROCESSINFO ppi, ppiShellProcess = NULL; if (gpEventScanGhosts == NULL) { gpEventScanGhosts = CreateKernelEvent(SynchronizationEvent, FALSE); if (gpEventScanGhosts == NULL) { return FALSE; } } UserAssert (ISCSRSS()); ppi = GETPTI(pwnd)->ppi; if (ppi->rpdeskStartup && ppi->rpdeskStartup->pDeskInfo) { ppiShellProcess = ppi->rpdeskStartup->pDeskInfo->ppiShellProcess; } if (ppiShellProcess && ppiShellProcess->Process != gpepCSRSS) { bRemoteThread = TRUE; UniqueProcessId = PsGetProcessId(ppiShellProcess->Process); } if (!InitCreateSystemThreadsMsg(&m, CST_GHOST, pdesk, UniqueProcessId, bRemoteThread)) { return FALSE; } /* * Since we are in CSRSS context use LpcRequestPort to send * LPC_DATAGRAM message type. Do not use LpcRequestWaitReplyPort * because it will send LPC_REQUEST which will fail (in server side). */ LeaveCrit(); Status = LpcRequestPort(CsrApiPort, (PPORT_MESSAGE)&m); EnterCrit(); if (gpEventScanGhosts == NULL) { return FALSE; } if (!NT_SUCCESS(Status)) { return FALSE; } } if (!(TestWF(pwnd, WFINDESTROY) || TestWF(pwnd, WFDESTROYED))) { return AddGhostOwnersAndOwnees(pwnd); } return FALSE; } /***************************************************************************\ * RemoveGhost * * This function is called from xxxFreeWindow to check and takes care * of business when pwnd is either a ghost or a hung window. \***************************************************************************/ VOID RemoveGhost( PWND pwnd) { PGHOST* ppghost; PGHOST pghost; CheckCritIn(); for (ppghost = &gpghostFirst; *ppghost != NULL; ppghost = &(*ppghost)->pghostNext) { pghost = *ppghost; /* * If this window matches the hung window, then set an event to * destroy the corresponding ghost window. If the ghost window hasn't * been created yet, we can nuke the structure in context. */ if (pghost->pwnd == pwnd) { if (pghost->pwndGhost == NULL) { UnlinkAndFreeGhost(ppghost, pghost); } else { pghost->pwnd = NULL; KeSetEvent(gpEventScanGhosts, EVENT_INCREMENT, FALSE); } break; } /* * If this window matches the ghost window, just remove the * structure from the list. */ if (pghost->pwndGhost == pwnd) { UnlinkAndFreeGhost(ppghost, pghost); break; } } } /***************************************************************************\ * PaintGhost * * Draw the ghost window look. \***************************************************************************/ VOID PaintGhost( PWND pwnd, HDC hdc) { PGHOST pghost; HBITMAP hbmOld; RECT rc; LONG cx, cy; #if GHOST_AGGRESSIVE HFONT hfont, hfontOld; WCHAR szHung[MAXSTRING]; ULONG cch; SIZE size; LONG xText; LOGFONTW lf; #endif pghost = GhostFromGhostPwnd(pwnd); if (pghost == NULL) { return; } rc.left = rc.top = 0; rc.right = pwnd->rcClient.right - pwnd->rcClient.left; rc.bottom = pwnd->rcClient.bottom - pwnd->rcClient.top; if (pghost->hbm != NULL) { cx = pghost->rcClient.right - pghost->rcClient.left; cy = pghost->rcClient.bottom - pghost->rcClient.top; hbmOld = GreSelectBitmap(ghdcMem, pghost->hbm); GreExtSelectClipRgn(hdc, pghost->hrgn, RGN_COPY); GreBitBlt(hdc, pghost->rcClient.left, pghost->rcClient.top, cx, cy, ghdcMem, 0, 0, SRCCOPY, 0); GreSelectBitmap(ghdcMem, hbmOld); SetRectRgnIndirect(ghrgnGDC, &rc); SubtractRgn(ghrgnGDC, ghrgnGDC, pghost->hrgn); GreExtSelectClipRgn(hdc, ghrgnGDC, RGN_COPY); } FillRect(hdc, &rc, SYSHBR(WINDOW)); GreExtSelectClipRgn(hdc, NULL, RGN_COPY); #if GHOST_AGGRESSIVE ServerLoadString(hModuleWin, STR_HUNG, szHung, ARRAY_SIZE(szHung)); cch = wcslen(szHung); GreSetTextColor(hdc, RGB(0, 0, 255)); GreSetBkColor(hdc, RGB(255, 255, 0)); GreExtGetObjectW(gpsi->hCaptionFont, sizeof(LOGFONTW), &lf); lf.lfHeight = (lf.lfHeight * 3) / 2; lf.lfWeight = FW_BOLD; hfont = GreCreateFontIndirectW(&lf); hfontOld = GreSelectFont(hdc, hfont); GreGetTextExtentW(hdc, szHung, cch, &size, GGTE_WIN3_EXTENT); xText = max(0, ((rc.right - rc.left) - size.cx) / 2); GreExtTextOutW(hdc, xText, 0, 0, NULL, szHung, cch, NULL); GreSelectFont(hdc, hfontOld); GreDeleteObject(hfont); #endif } /***************************************************************************\ * xxxGhostWndProc * * Processes messages for ghost windows. \***************************************************************************/ LRESULT xxxGhostWndProc( PWND pwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { PGHOST pghost; VALIDATECLASSANDSIZE(pwnd, uMsg, wParam, lParam, FNID_GHOST, WM_NCCREATE); switch (uMsg) { case WM_CLOSE: pghost = GhostFromGhostPwnd(pwnd); /* * Do the end task on the hung thread when the user tries to close * the ghost window. */ if (pghost != NULL && pghost->pwnd != NULL) { PostShellHookMessages(HSHELL_ENDTASK, (LPARAM)HWq(pghost->pwnd)); } return 0; case WM_LBUTTONDOWN: pghost = GhostFromGhostPwnd(pwnd); if (pghost != NULL) { if (pghost->fWarningText) { return 0; } else { pghost->fWarningText = TRUE; } } xxxAddWarningText(pwnd); return 0; case WM_SIZE: /* * Since we have wrapped, flowing text, repaint it when sizing. */ xxxInvalidateRect(pwnd, NULL, TRUE); return 0; case WM_ERASEBKGND: PaintGhost(pwnd, (HDC)wParam); return 1; case WM_SETCURSOR: /* * Show the hung app cursor over the client. */ if (LOWORD(lParam) == HTCLIENT) { zzzSetCursor(SYSCUR(WAIT)); return 1; } case WM_EXITSIZEMOVE: pghost = GhostFromGhostPwnd(pwnd); if (pghost != NULL) { pghost->fSizedOrMoved = TRUE; } /* * FALL THROUGH to DWP. */ default: return xxxDefWindowProc(pwnd, uMsg, wParam, lParam); } } #endif