Windows NT 4.0 source code leak
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

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