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

/***************************** 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