mirror of https://github.com/lianthony/NT4.0
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.
514 lines
15 KiB
514 lines
15 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: hungapp.c
|
|
*
|
|
* Copyright (c) 1985-95, Microsoft Corporation
|
|
*
|
|
*
|
|
* History:
|
|
* 03-10-92 DavidPe Created.
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
/*
|
|
* Millisecond defines for hung-app heuristics.
|
|
*/
|
|
#define HUNGAPPDEMONFREQUENCY 1000
|
|
#define FOREVER (DWORD)(-1)
|
|
|
|
|
|
/***************************************************************************\
|
|
* SetHungFlag
|
|
*
|
|
* Sets the specified redraw-if-hung flag in the window and adds the
|
|
* window to the list of windows to redraw if hung.
|
|
*
|
|
* 08-23-93 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
VOID SetHungFlag(
|
|
PWND pwnd,
|
|
WORD wFlag)
|
|
{
|
|
|
|
/*
|
|
* If the window has no hung redraw bits set and it's a top-level
|
|
* window, add it to the redraw list.
|
|
*/
|
|
if (!TestWF(pwnd, WFANYHUNGREDRAW) && pwnd->spwndParent == PWNDDESKTOP(pwnd)) {
|
|
|
|
/*
|
|
* No more free entries left in the list, so expand it.
|
|
*/
|
|
if (gphrl->iFirstFree == gphrl->cEntries) {
|
|
PHUNGREDRAWLIST p;
|
|
DWORD dwSize = gphrl->cEntries * sizeof(PWND) +
|
|
sizeof(HUNGREDRAWLIST);
|
|
|
|
p = UserReAllocPool(gphrl, dwSize, sizeof(HUNGREDRAWLIST) +
|
|
((CHRLINCR + gphrl->cEntries - 1) * sizeof(PWND)),
|
|
TAG_HUNGLIST);
|
|
|
|
/*
|
|
* If the realloc failed, we'll simply set the flags and
|
|
* return. This window won't be redrawn if hung.
|
|
*/
|
|
if (p == NULL) {
|
|
SetWF(pwnd, wFlag);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Put the newly allocated entries on the free list.
|
|
*/
|
|
RtlZeroMemory(&p->apwndRedraw[p->cEntries],
|
|
CHRLINCR * sizeof(PWND));
|
|
gphrl = p;
|
|
gphrl->cEntries += CHRLINCR;
|
|
}
|
|
|
|
/*
|
|
* Have the window reference the first free entry.
|
|
*/
|
|
pwnd->iHungRedraw = gphrl->iFirstFree;
|
|
gphrl->apwndRedraw[gphrl->iFirstFree++] = pwnd;
|
|
}
|
|
|
|
SetWF(pwnd, wFlag);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* ClearHungFlag
|
|
*
|
|
* Clears the specified redraw-if-hung flag in the window and if no other
|
|
* redraw-if-hung flags remain, remove the window from list of windows
|
|
* to be redrawn if hung.
|
|
*
|
|
* 08-23-93 JimA Created.
|
|
\***************************************************************************/
|
|
|
|
VOID ClearHungFlag(
|
|
PWND pwnd,
|
|
WORD wFlag)
|
|
{
|
|
ClrWF(pwnd, wFlag);
|
|
if (!TestWF(pwnd, WFANYHUNGREDRAW) && pwnd->iHungRedraw != -1) {
|
|
int iEnd = gphrl->iFirstFree - 1;
|
|
|
|
/*
|
|
* Remove the window from the redraw list and compact it. If
|
|
* the entry is not the last in the list, move the last
|
|
* entry in the list into the spot being freed.
|
|
*/
|
|
if (pwnd->iHungRedraw != iEnd) {
|
|
gphrl->apwndRedraw[pwnd->iHungRedraw] = gphrl->
|
|
apwndRedraw[iEnd];
|
|
gphrl->apwndRedraw[iEnd]->iHungRedraw = pwnd->iHungRedraw;
|
|
}
|
|
|
|
/*
|
|
* Free the last entry.
|
|
*/
|
|
gphrl->apwndRedraw[iEnd] = NULL;
|
|
gphrl->iFirstFree--;
|
|
|
|
pwnd->iHungRedraw = -1;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* FHungApp
|
|
*
|
|
*
|
|
* 02-28-92 DavidPe Created.
|
|
\***************************************************************************/
|
|
|
|
BOOL FHungApp(
|
|
PTHREADINFO pti,
|
|
DWORD dwTimeFromLastRead)
|
|
{
|
|
|
|
/*
|
|
* An app is considered hung if it isn't waiting for input, isn't in
|
|
* startup processing, and hasn't called PeekMessage() within the
|
|
* specified timeout.
|
|
*/
|
|
if (!(pti->ppi->W32PF_Flags & W32PF_APPSTARTING) &&
|
|
!(pti->pcti->fsWakeMask & (QS_MOUSE | QS_KEY)) &&
|
|
((NtGetTickCount() - GET_TIME_LAST_READ(pti)) > dwTimeFromLastRead)) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* RedrawHungWindowFrame
|
|
*
|
|
*
|
|
* 02-28-92 DavidPe Created.
|
|
\***************************************************************************/
|
|
|
|
VOID RedrawHungWindowFrame(
|
|
PWND pwnd,
|
|
BOOL fActive)
|
|
{
|
|
HDC hdc;
|
|
TL tlpwnd;
|
|
UINT wFlags = DC_NC | DC_NOSENDMSG;
|
|
|
|
ThreadLock(pwnd, &tlpwnd);
|
|
|
|
if (fActive)
|
|
wFlags |= DC_ACTIVE;
|
|
|
|
hdc = _GetDCEx(pwnd, NULL, DCX_USESTYLE | DCX_WINDOW);
|
|
xxxDrawCaptionBar(pwnd, hdc, wFlags);
|
|
_ReleaseDC(hdc);
|
|
|
|
ThreadUnlock(&tlpwnd);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* RedrawHungWindow
|
|
*
|
|
* If the hrgnFullDrag is NULL, redraw the hung window's entire update region,
|
|
* otherwise, only redraw the intersection of the window's update region
|
|
* with the FullDrag region.
|
|
*
|
|
* 02-28-92 DavidPe Created.
|
|
\***************************************************************************/
|
|
|
|
VOID RedrawHungWindow(
|
|
PWND pwnd,
|
|
HRGN hrgnFullDrag)
|
|
{
|
|
HDC hdc;
|
|
HBRUSH hbr;
|
|
HRGN hrgnUpdate;
|
|
RECT rc;
|
|
TL tlpwnd;
|
|
UINT flags;
|
|
W32PID sid;
|
|
DWORD dwColor;
|
|
|
|
CheckCritIn();
|
|
|
|
if (pwnd->hrgnUpdate == NULL) {
|
|
return;
|
|
}
|
|
/*
|
|
* First calculate hrgnUpdate.
|
|
*/
|
|
if (pwnd->hrgnUpdate > MAXREGION) {
|
|
hrgnUpdate = GreCreateRectRgn(0, 0, 0, 0);
|
|
if (hrgnUpdate == NULL) {
|
|
hrgnUpdate = MAXREGION;
|
|
|
|
} else if (CopyRgn(hrgnUpdate, pwnd->hrgnUpdate) == ERROR) {
|
|
GreDeleteObject(hrgnUpdate);
|
|
hrgnUpdate = MAXREGION;
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
* For our purposes, we need a real hrgnUpdate, so try and
|
|
* create one if even if the entire window needs updating.
|
|
*/
|
|
CopyRect(&rc, &pwnd->rcWindow);
|
|
hrgnUpdate = GreCreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
|
|
if (hrgnUpdate == NULL) {
|
|
hrgnUpdate = MAXREGION;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we're redrawing because we're full dragging and if the window's
|
|
* update region does not intersect with the Full drag
|
|
* update region, don't erase the hung window again. This is to prevent
|
|
* flickering when a window has been invalidated by another window doing
|
|
* full drag and hasn't received the paint message yet.
|
|
* This way, only if there is a new region that has been invalidated will
|
|
* we redraw the hung window.
|
|
*/
|
|
if (hrgnFullDrag && hrgnUpdate != MAXREGION &&
|
|
IntersectRgn(hrgnUpdate, hrgnUpdate, hrgnFullDrag) == NULLREGION) {
|
|
GreDeleteObject(hrgnUpdate);
|
|
return;
|
|
}
|
|
|
|
ThreadLock(pwnd, &tlpwnd);
|
|
|
|
hdc = _GetDCEx(pwnd, hrgnUpdate, DCX_USESTYLE | DCX_WINDOW |
|
|
DCX_INTERSECTRGN | DCX_NODELETERGN | DCX_LOCKWINDOWUPDATE);
|
|
xxxDrawWindowFrame(pwnd, hdc, TRUE, TestwndFrameOn(pwnd));
|
|
_ReleaseDC(hdc);
|
|
|
|
CopyRect(&rc, &pwnd->rcWindow);
|
|
xxxCalcClientRect(pwnd, &rc, TRUE);
|
|
GreSetRectRgn(hrgnInv2, rc.left, rc.top, rc.right, rc.bottom);
|
|
|
|
if (hrgnUpdate > MAXREGION) {
|
|
switch (IntersectRgn(hrgnUpdate, hrgnUpdate, hrgnInv2)) {
|
|
|
|
case ERROR:
|
|
GreDeleteObject(hrgnUpdate);
|
|
hrgnUpdate = MAXREGION;
|
|
break;
|
|
|
|
case NULLREGION:
|
|
/*
|
|
* There is nothing in the client area to repaint.
|
|
* Blow the region away, and decrement the paint count
|
|
* if possible.
|
|
*/
|
|
GreDeleteObject(hrgnUpdate);
|
|
hrgnUpdate = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Erase the rest of the window.
|
|
*/
|
|
|
|
/*
|
|
* Get a window dc so that the menu and scroll bar areas are erased
|
|
* appropriately. But make sure it is clipped so that the children
|
|
* get clipped out correctly! If we don't do this, this we could erase
|
|
* children that aren't invalid.
|
|
*
|
|
* Note: DCX_WINDOW and DCX_USESTYLE will never clip out children.
|
|
* Need to pass the clipping styles in directly, instead of passing
|
|
* DCX_USESTYLE.
|
|
*/
|
|
flags = DCX_INTERSECTRGN | DCX_WINDOW | DCX_CACHE;
|
|
if (TestWF(pwnd, WFCLIPSIBLINGS))
|
|
flags |= DCX_CLIPSIBLINGS;
|
|
if (TestWF(pwnd, WFCLIPCHILDREN))
|
|
flags |= DCX_CLIPCHILDREN;
|
|
|
|
hdc = _GetDCEx(pwnd, hrgnUpdate, flags);
|
|
|
|
if (pwnd == pwnd->head.rpdesk->pDeskInfo->spwndBkGnd) {
|
|
|
|
InternalPaintDesktop((PDESKWND)(PWNDDESKTOP(pwnd)), hdc, TRUE);
|
|
|
|
} else {
|
|
|
|
rc = pwnd->rcWindow;
|
|
|
|
OffsetRect(&rc, -pwnd->rcWindow.left, -pwnd->rcWindow.top);
|
|
|
|
/*
|
|
* Erase the rest of the window using the window' class background
|
|
* brush.
|
|
*/
|
|
if ((hbr = pwnd->pcls->hbrBackground) != NULL) {
|
|
if ((DWORD)hbr <= COLOR_ENDCOLORS + 1)
|
|
hbr = ahbrSystem[(DWORD)hbr - 1];
|
|
} else {
|
|
/*
|
|
* Use the window brush for windows and 3.x dialogs,
|
|
* Use the COLOR3D brush for 4.x dialogs.
|
|
*/
|
|
if (TestWF(pwnd, WFDIALOGWINDOW) && TestWF(pwnd, WFWIN40COMPAT))
|
|
hbr = SYSHBR(3DFACE);
|
|
else
|
|
hbr = SYSHBR(WINDOW);
|
|
}
|
|
|
|
/*
|
|
* If the window's class background brush is public, use it.
|
|
*/
|
|
sid = (W32PID)GreGetObjectOwner((HOBJ)hbr, BRUSH_TYPE);
|
|
if (sid == (W32PID)GetCurrentProcessId() || sid == OBJECT_OWNER_PUBLIC) {
|
|
|
|
FillRect(hdc, &rc, hbr);
|
|
|
|
} else {
|
|
|
|
/*
|
|
* The window's class background brush is not public.
|
|
* We get its color and set the color of our own public brush and use
|
|
* that for the background brush.
|
|
*/
|
|
|
|
/*
|
|
* If the window is a console window, get the console background brush.
|
|
* This brush will be different than the console class brush if the user
|
|
* changed the console background color.
|
|
*/
|
|
if (gatomConsoleClass == pwnd->pcls->atomClassName) {
|
|
|
|
dwColor = _GetWindowLong(pwnd, GWL_CONSOLE_BKCOLOR, FALSE);
|
|
|
|
} else {
|
|
|
|
if ((dwColor = GreGetBrushColor(hbr)) == -1)
|
|
dwColor = GreGetBrushColor(SYSHBR(WINDOW));
|
|
}
|
|
|
|
GreSetSolidBrush(hbrHungApp, dwColor);
|
|
|
|
FillRect(hdc, &rc, hbrHungApp);
|
|
}
|
|
}
|
|
_ReleaseDC(hdc);
|
|
|
|
/*
|
|
* The window has been erased and framed. It only did this because the
|
|
* app hasn't done it yet:
|
|
*
|
|
* - the app hasn't erased and frame yet.
|
|
* - the app is in the middle of erasing and framing.
|
|
*
|
|
* The app could not of completed erasing and framing, because the
|
|
* WFREDRAWIFHUNG bit is cleared when this successfully completes.
|
|
*
|
|
* Given that the app may be in the middle of erasing and framing, we
|
|
* need to set both the erase and frame bits *again* so it erasing and
|
|
* frames over again (if we don't, it never will). If the app hasn't
|
|
* done any erasing/framing yet, this is a nop.
|
|
*/
|
|
SetWF(pwnd, WFSENDNCPAINT);
|
|
SetWF(pwnd, WFSENDERASEBKGND);
|
|
|
|
#if 0
|
|
/*
|
|
* Always set WFUPDATEDIRTY: we don't want the app to draw, then stop
|
|
* and have the hung app thread draw, and then allow the app to validate
|
|
* itself: Mark the update region dirty - cannot be validated until the
|
|
* app calls a painting function and acknowledges the update region.
|
|
*/
|
|
|
|
/*
|
|
* 7/7/96, vadimg: This causes a problem with apps that call
|
|
* GetUpdateRect/Rgn which clears the WFUPDATEDIRTY flag. If the
|
|
* HungAppMonitor kicks in in the middle of the paint, the flag will be
|
|
* incorrectly set, and the app's ValidateRect will not succeed. Example:
|
|
* VisualSlick Edit. It doesn't matter if we set the flag here,
|
|
* InternalInvlaidate3 sets when necessary anyway.
|
|
*/
|
|
|
|
SetWF(pwnd, WFUPDATEDIRTY);
|
|
#endif
|
|
|
|
#ifdef WIN95DOESTHIS
|
|
/*
|
|
* Go through all the children and redraw hung ones too.
|
|
*/
|
|
if (hrgnFullDrag != NULL) {
|
|
PWND pwndT;
|
|
|
|
for (pwndT = pwnd->spwndChild; pwndT != NULL; pwndT = pwndT->spwndNext) {
|
|
|
|
if (TestWF(pwndT, WFREDRAWIFHUNG) && (FHungApp(GETPTI(pwndT), CMSHUNGAPPTIMEOUT))) {
|
|
ClearHungFlag(pwndT, WFREDRAWIFHUNG);
|
|
RedrawHungWindow(pwndT, NULL);
|
|
}
|
|
|
|
if (TestWF(pwndT, WFDESTROYED)) {
|
|
break;
|
|
}
|
|
} /* for */
|
|
}
|
|
#endif
|
|
|
|
ThreadUnlock(&tlpwnd);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* HungAppDemon
|
|
*
|
|
* NOTE: RIT timers (like this one) get called while inside an EnterCrit block.
|
|
*
|
|
* We keep a list of redraw-if-hung windows in a list that remains in a
|
|
* single page to avoid touching the windows themselves each time through
|
|
* this routine. Touching the windows causes a bunch of unnecessary paging
|
|
* and in effect keeps all of the pages that contain top-level windows
|
|
* resident at all times; this is very wasteful.
|
|
*
|
|
* 02-28-92 DavidPe Created.
|
|
\***************************************************************************/
|
|
|
|
LONG HungAppDemon(
|
|
PWND pwnd,
|
|
UINT message,
|
|
DWORD wParam,
|
|
LONG lParam)
|
|
{
|
|
PWND *ppwndRedraw;
|
|
int cEntries;
|
|
UNREFERENCED_PARAMETER(message);
|
|
UNREFERENCED_PARAMETER(wParam);
|
|
UNREFERENCED_PARAMETER(lParam);
|
|
|
|
/*
|
|
* See if we should start the screen saver.
|
|
*/
|
|
IdleTimerProc();
|
|
|
|
/*
|
|
* If it is time to hide the app starting cursor, do it.
|
|
*/
|
|
if (NtGetTickCount() >= gtimeStartCursorHide) {
|
|
CalcStartCursorHide(NULL, 0);
|
|
}
|
|
|
|
/*
|
|
* Now check to see if there are any top-level
|
|
* windows that need redrawing.
|
|
*/
|
|
if (grpdeskRitInput == NULL || grpdeskRitInput->pDeskInfo->spwnd == NULL)
|
|
return 0;
|
|
|
|
/*
|
|
* Walk down the list of redraw-if-hung windows. Loop
|
|
* until we hit the end of the array or find a NULL.
|
|
*/
|
|
cEntries = gphrl->cEntries;
|
|
for (ppwndRedraw = gphrl->apwndRedraw; cEntries > 0 &&
|
|
*ppwndRedraw != NULL; ) {
|
|
|
|
/*
|
|
* See if the app is hung. If so, do the appropriate
|
|
* redrawing.
|
|
*/
|
|
pwnd = *ppwndRedraw;
|
|
if (FHungApp(GETPTI(pwnd), CMSHUNGAPPTIMEOUT)) {
|
|
if (TestWF(pwnd, WFREDRAWFRAMEIFHUNG)) {
|
|
|
|
/*
|
|
* WFREDRAWFRAMEIFHUNG will be cleared in the process
|
|
* of drawing the frame, no need to clear it here.
|
|
*/
|
|
RedrawHungWindowFrame(pwnd, TestwndFrameOn(pwnd));
|
|
}
|
|
|
|
if (TestWF(pwnd, WFREDRAWIFHUNG)) {
|
|
ClearHungFlag(pwnd, WFREDRAWIFHUNG);
|
|
RedrawHungWindow(pwnd, NULL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Step to the next entry only if the flags have been cleared,
|
|
* i.e. pwnd != *ppwndRedraw.
|
|
*/
|
|
if (pwnd == *ppwndRedraw) {
|
|
ppwndRedraw++;
|
|
cEntries--;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|