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.
 
 
 
 
 
 

498 lines
15 KiB

/****************************** Module Header ******************************\
* Module Name: hungapp.c
*
* Copyright (c) 1985 - 1999, Microsoft Corporation
*
*
* History:
* 03-10-92 DavidPe Created.
\***************************************************************************/
#include "precomp.h"
#pragma hdrstop
/***************************************************************************\
* SetHungFlag
*
* Sets the specified redraw-if-hung flag in the window and adds the
* window to the list of windows to redraw if hung.
* Windows that are not top-level get the bit set, but aren't added to the list
*
* 08-23-93 JimA Created.
\***************************************************************************/
#define CHRLINCR 10
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)) {
/*
* Add pwnd to the Hung Redraw Volatile Window Pointer List.
*/
VWPLAdd(&gpvwplHungRedraw, pwnd, CHRLINCR);
}
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.
* Many windows have WFREDRAW* bits set, but aren't in the list (only those
* that were top-level were added).
*
* 08-23-93 JimA Created.
\***************************************************************************/
VOID ClearHungFlag(
PWND pwnd,
WORD wFlag)
{
BOOL fInRedrawList = TestWF(pwnd, WFANYHUNGREDRAW);
ClrWF(pwnd, wFlag);
if (!TestWF(pwnd, WFANYHUNGREDRAW) && fInRedrawList) {
/*
* Remove the window from the redraw list and possibly compact it.
*/
VWPLRemove(&gpvwplHungRedraw, pwnd);
}
}
/***************************************************************************\
* 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 (((NtGetTickCount() - GET_TIME_LAST_READ(pti)) > dwTimeFromLastRead) &&
!((pti->pcti->fsWakeMask & QS_INPUT) && (PsGetThreadFreezeCount(pti->pEThread) == 0)) &&
!(pti->ppi->W32PF_Flags & W32PF_APPSTARTING)) {
return TRUE;
}
return FALSE;
}
/***************************************************************************\
* xxxRedrawHungWindowFrame
*
*
* 02-28-92 DavidPe Created.
\***************************************************************************/
VOID xxxRedrawHungWindowFrame(
PWND pwnd,
BOOL fActive)
{
HDC hdc;
UINT wFlags = DC_NC | DC_NOSENDMSG;
CheckLock(pwnd);
#ifdef HUNGAPP_GHOSTING
ClearHungFlag(pwnd, WFREDRAWFRAMEIFHUNG);
SignalGhost(pwnd);
return;
#endif // HUNGAPP_GHOSTING
if (IsInsideUserApiHook()) {
return;
}
if (fActive) {
wFlags |= DC_ACTIVE;
}
hdc = _GetDCEx(pwnd, NULL, DCX_USESTYLE | DCX_WINDOW);
xxxDrawCaptionBar(pwnd, hdc, wFlags);
_ReleaseDC(hdc);
}
/***************************************************************************\
* xxxRedrawHungWindow
*
* 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 xxxRedrawHungWindow(
PWND pwnd,
HRGN hrgnFullDrag)
{
HDC hdc;
HBRUSH hbr;
HRGN hrgnUpdate;
RECT rc;
TL tlpwnd;
UINT flags;
W32PID sid;
DWORD dwColor;
PWND pwndDesk;
TL tlpwndDesk;
CheckCritIn();
CheckLock(pwnd);
if (pwnd->hrgnUpdate == NULL) {
return;
}
#ifdef HUNGAPP_GHOSTING
/*
* Don't bother doing anything here when the window isn't even visible.
*/
if (!TestWF(pwnd, WFVISIBLE)) {
return;
}
/*
* This function can be called from the full-drag code to quick redraw
* windows that aren't hung. In that case check if that thread is hung.
*/
if ((hrgnFullDrag == NULL) || (hrgnFullDrag != NULL &&
FHungApp(GETPTI(pwnd), CMSHUNGAPPTIMEOUT))) {
SignalGhost(pwnd);
return;
}
UserAssert(gptiCurrent != gptiRit);
#endif
/*
* First calculate hrgnUpdate.
*/
if (pwnd->hrgnUpdate > HRGN_FULL) {
hrgnUpdate = CreateEmptyRgn();
if (hrgnUpdate == NULL) {
hrgnUpdate = HRGN_FULL;
} else if (CopyRgn(hrgnUpdate, pwnd->hrgnUpdate) == ERROR) {
GreDeleteObject(hrgnUpdate);
hrgnUpdate = HRGN_FULL;
}
} 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 = GreCreateRectRgnIndirect(&rc);
if (hrgnUpdate == NULL) {
hrgnUpdate = HRGN_FULL;
}
}
/*
* 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 != HRGN_FULL &&
IntersectRgn(hrgnUpdate, hrgnUpdate, hrgnFullDrag) == NULLREGION) {
GreDeleteObject(hrgnUpdate);
return;
}
ThreadLock(pwnd, &tlpwnd);
if (IsInsideUserApiHook()) {
xxxInternalInvalidate(pwnd, hrgnUpdate, RDW_INVALIDATE |
RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN);
} else {
hdc = _GetDCEx(pwnd, hrgnUpdate, DCX_USESTYLE | DCX_WINDOW |
DCX_INTERSECTRGN | DCX_NODELETERGN | DCX_LOCKWINDOWUPDATE);
xxxDrawWindowFrame(pwnd, hdc, DF_HUNGREDRAW | (TestwndFrameOn(pwnd) ? DF_ACTIVE : 0L));
_ReleaseDC(hdc);
}
CopyRect(&rc, &pwnd->rcWindow);
xxxCalcClientRect(pwnd, &rc, TRUE);
SetRectRgnIndirect(ghrgnInv2, &rc);
if (hrgnUpdate > HRGN_FULL) {
switch (IntersectRgn(hrgnUpdate, hrgnUpdate, ghrgnInv2)) {
case ERROR:
GreDeleteObject(hrgnUpdate);
hrgnUpdate = HRGN_FULL;
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.
* When pwnd isn't WFCLIPCHILDREN, make sure valid children bits
* don't get overwritten if the child is in the middle of BeginPaint
* or just completed it's painting and it's hrgnUpdate is NULL.
*/
if (hrgnUpdate != NULL && !TestWF(pwnd, WFCLIPCHILDREN)) {
RECT rcT;
PWND pwndT;
if (hrgnUpdate == HRGN_FULL) {
rc = pwnd->rcWindow;
} else {
GreGetRgnBox(hrgnUpdate, &rc);
}
for (pwndT = pwnd->spwndChild; pwndT != NULL;
pwndT = pwndT->spwndNext) {
if (TestWF(pwndT, WFVISIBLE) &&
(TestWF(pwndT, WFSTARTPAINT) || pwndT->hrgnUpdate == NULL) &&
IntersectRect(&rcT, &rc, &pwndT->rcWindow)) {
/*
* This invalidate call won't leave the critial section. In
* reality the entire xxxRedrawHungWindow must not leave
* the critical section.
*/
BEGINATOMICCHECK();
xxxInternalInvalidate(pwndT, hrgnUpdate, RDW_INVALIDATE |
RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN);
ENDATOMICCHECK();
}
}
}
/*
* 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) {
pwndDesk = PWNDDESKTOP(pwnd);
ThreadLock(pwndDesk, &tlpwndDesk);
xxxInternalPaintDesktop(PWNDDESKTOP(pwnd), hdc, TRUE);
ThreadUnlock(&tlpwndDesk);
} else {
rc = pwnd->rcWindow;
OffsetRect(&rc, -pwnd->rcWindow.left, -pwnd->rcWindow.top);
/*
* Erase the rest of the window using the window's class background
* brush.
*/
if ((hbr = pwnd->pcls->hbrBackground) != NULL) {
if (hbr <= (HBRUSH)COLOR_ENDCOLORS + 1) {
hbr = SYSHBRUSH((ULONG_PTR)hbr - 1);
}
} else {
/*
* Use the window brush for windows and 3.x dialogs, and 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 == OBJECT_OWNER_PUBLIC ||
sid == (W32PID)(ULONG_PTR)PsGetCurrentProcessId()) {
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);
} else {
if ((dwColor = GreGetBrushColor(hbr)) == -1) {
dwColor = GreGetBrushColor(SYSHBR(WINDOW));
}
}
GreSetSolidBrush(ghbrHungApp, dwColor);
FillRect(hdc, &rc, ghbrHungApp);
}
}
_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);
/*
* 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.
*/
SetWF(pwnd, WFUPDATEDIRTY);
ThreadUnlock(&tlpwnd);
}
/***************************************************************************\
* xxxHungAppDemon
*
* NOTE: RIT timers (like this one) get called while inside the critical
* section.
*
* 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.
\***************************************************************************/
VOID xxxHungAppDemon(
PWND pwnd,
UINT message,
UINT_PTR nID,
LPARAM lParam)
{
TL tlpwnd;
DWORD nPwndHungRedraw;
PWND pwndHungRedraw;
UNREFERENCED_PARAMETER(message);
UNREFERENCED_PARAMETER(nID);
UNREFERENCED_PARAMETER(lParam);
UNREFERENCED_PARAMETER(pwnd);
CheckLock(pwnd);
/*
* See if we should start the screen saver.
*/
IdleTimerProc();
/*
* If it is time to hide the app starting cursor, do it.
*/
if (NtGetTickCount() >= gtimeStartCursorHide) {
/*
* No need to DeferWinEventNotify()
*/
zzzCalcStartCursorHide(NULL, 0);
}
/*
* Now check to see if there are any top-level windows that need
* redrawing.
*/
if (grpdeskRitInput == NULL || grpdeskRitInput->pDeskInfo->spwnd == NULL) {
return;
}
/*
* Walk down the list of redraw-if-hung windows. Loop until we hit the
* end of the array or find a NULL.
*/
nPwndHungRedraw = 0;
pwndHungRedraw = NULL;
while (pwndHungRedraw = VWPLNext(gpvwplHungRedraw, pwndHungRedraw, &nPwndHungRedraw)) {
/*
* See if the app is hung. If so, do the appropriate redrawing.
*/
if (FHungApp(GETPTI(pwndHungRedraw), CMSHUNGAPPTIMEOUT)) {
ThreadLock(pwndHungRedraw, &tlpwnd);
if (TestWF(pwndHungRedraw, WFREDRAWFRAMEIFHUNG)) {
/*
* WFREDRAWFRAMEIFHUNG will be cleared in the process of
* drawing the frame, so no need to clear it here.
*/
xxxRedrawHungWindowFrame(pwndHungRedraw,
TestwndFrameOn(pwndHungRedraw));
}
if (TestWF(pwndHungRedraw, WFREDRAWIFHUNG)) {
ClearHungFlag(pwndHungRedraw, WFREDRAWIFHUNG);
xxxRedrawHungWindow(pwndHungRedraw, NULL);
}
ThreadUnlock(&tlpwnd);
}
}
}