|
|
/****************************** Module Header ******************************\
* Module Name: enumwin.c * * Copyright (c) 1985 - 1999, Microsoft Corporation * * Contains the EnumWindows API, BuildHwndList and related functions. * * History: * 10-20-90 darrinm Created. * ??-??-?? ianja Added Revalidation code * 02-19-91 JimA Added enum access checks \***************************************************************************/
#include "precomp.h"
#pragma hdrstop
PBWL pbwlCache;
#if DBG
PBWL pbwlCachePrev; #endif
PBWL InternalBuildHwndList(PBWL pbwl, PWND pwnd, UINT flags); PBWL InternalBuildHwndOwnerList(PBWL pbwl, PWND pwndStart, PWND pwndOwner); #ifdef FE_IME
PBWL InternalRebuildHwndListForIMEClass(PBWL pbwl, BOOL fRemoveChild); PWND InternalGetIMEOwner(HWND hwnd, BOOL fRetIMEWnd); #endif
/***************************************************************************\
* xxxInternalEnumWindow * * History: * 10-20-90 darrinm Ported from Win 3.0 sources. * 02-06-91 IanJa rename: the call to lpfn can leave the critsect. * 02-19-91 JimA Added enum access check \***************************************************************************/
BOOL xxxInternalEnumWindow( PWND pwndNext, WNDENUMPROC_PWND lpfn, LPARAM lParam, UINT flags) { HWND *phwnd; PWND pwnd; PBWL pbwl; BOOL fSuccess; TL tlpwnd;
CheckLock(pwndNext);
if ((pbwl = BuildHwndList(pwndNext, flags, NULL)) == NULL) return FALSE;
fSuccess = TRUE; for (phwnd = pbwl->rghwnd; *phwnd != (HWND)1; phwnd++) {
/*
* Lock the window before we pass it off to the app. */ if ((pwnd = RevalidateHwnd(*phwnd)) != NULL) {
/*
* Call the application. */ ThreadLockAlways(pwnd, &tlpwnd); fSuccess = (*lpfn)(pwnd, lParam); ThreadUnlock(&tlpwnd); if (!fSuccess) break; } }
FreeHwndList(pbwl);
return fSuccess; }
/***************************************************************************\
* BuildHwndList * * History: * 10-20-90 darrinm Ported from Win 3.0 sources. \***************************************************************************/
#define CHWND_BWLCREATE 32
PBWL BuildHwndList( PWND pwnd, UINT flags, PTHREADINFO pti) { PBWL pbwl;
CheckCritIn();
if ((pbwl = pbwlCache) != NULL) {
/*
* We're using the cache now; zero it out. */ #if DBG
pbwlCachePrev = pbwlCache; #endif
pbwlCache = NULL;
#if DBG
{ PBWL pbwlT; /*
* pbwlCache shouldn't be in the global linked list. */ for (pbwlT = gpbwlList; pbwlT != NULL; pbwlT = pbwlT->pbwlNext) { UserAssert(pbwlT != pbwl); } } #endif
} else {
/*
* sizeof(BWL) includes the first element of array. */ pbwl = (PBWL)UserAllocPool(sizeof(BWL) + sizeof(PWND) * CHWND_BWLCREATE, TAG_WINDOWLIST); if (pbwl == NULL) return NULL;
pbwl->phwndMax = &pbwl->rghwnd[CHWND_BWLCREATE - 1]; } pbwl->phwndNext = pbwl->rghwnd;
/*
* We'll use ptiOwner as temporary storage for the thread we're * scanning for. It will get reset to the proper thing at the bottom * of this routine. */ pbwl->ptiOwner = pti;
#ifdef OWNERLIST
if (flags & BWL_ENUMOWNERLIST) { pbwl = InternalBuildHwndOwnerList(pbwl, pwnd, NULL); } else { pbwl = InternalBuildHwndList(pbwl, pwnd, flags); } #else
pbwl = InternalBuildHwndList(pbwl, pwnd, flags); #endif
/*
* If phwndNext == phwndMax, it indicates that the pbwl has failed to expand. * The list is no longer valid, so we should just bail. */ if (pbwl->phwndNext >= pbwl->phwndMax) { UserAssert(pbwl->phwndNext == pbwl->phwndMax); /*
* Even if we had picked pbwl from the global single cache (pbwlCache), * it should have already been unlinked from the global link list when it was put in the cache. * So we should just free it without manupilating the link pointers. * If we have allocated the pwbl for ourselves, we can simply free it. * In both cases, we should just call UserFreePool(). * As the side effect, it may make some room by providing a free pool block. */ UserFreePool(pbwl); return NULL; }
/*
* Stick in the terminator. */ *pbwl->phwndNext = (HWND)1;
#ifdef FE_IME
if (flags & BWL_ENUMIMELAST) { UserAssert(IS_IME_ENABLED()); /*
* For IME windows. * Rebuild window list for EnumWindows API. Because ACCESS 2.0 assumes * the first window that is called CallBack Functions in the task is * Q-Card Wnd. We should change the order of IME windows */ pbwl = InternalRebuildHwndListForIMEClass(pbwl, (flags & BWL_REMOVEIMECHILD) == BWL_REMOVEIMECHILD); } #endif
/*
* Finally link this guy into the list. */ pbwl->ptiOwner = PtiCurrent(); pbwl->pbwlNext = gpbwlList; gpbwlList = pbwl;
/*
* We should have given out the cache if it was available */ UserAssert(pbwlCache == NULL);
return pbwl; }
/***************************************************************************\
* ExpandWindowList * * This routine expands a window list. * * 01-16-92 ScottLu Created. \***************************************************************************/
BOOL ExpandWindowList( PBWL *ppbwl) { PBWL pbwl; PBWL pbwlT; HWND *phwnd;
pbwl = *ppbwl; phwnd = pbwl->phwndNext;
/*
* Map phwnd to an offset. */ phwnd = (HWND *)((BYTE *)phwnd - (BYTE *)pbwl);
/*
* Increase size of BWL by 8 slots. (8 + 1) is * added since phwnd is "sizeof(HWND)" less * than actual size of handle. */ pbwlT = (PBWL)UserReAllocPool((HANDLE)pbwl, PtrToUlong(phwnd) + sizeof(PWND), PtrToUlong(phwnd) + (BWL_CHWNDMORE + 1) * sizeof(PWND), TAG_WINDOWLIST);
/*
* Did alloc succeed? */ if (pbwlT != NULL) pbwl = pbwlT; /* Yes, use new block. */
/*
* Map phwnd back into a pointer. */ phwnd = (HWND *)((ULONG_PTR)pbwl + (ULONG_PTR)phwnd);
/*
* Did ReAlloc() fail? */ if (pbwlT == NULL) { RIPMSG0(RIP_WARNING, "ExpandWindowList: out of memory."); return FALSE; }
/*
* Reset phwndMax. */ pbwl->phwndNext = phwnd; pbwl->phwndMax = phwnd + BWL_CHWNDMORE;
*ppbwl = pbwl;
return TRUE; }
#ifdef OWNERLIST
/***************************************************************************\
* InternalBuildHwndOwnerList * * Builds an hwnd list sorted by owner. Ownees go first. Shutdown uses this for * WM_CLOSE messages. * * 01-16-93 ScottLu Created. \***************************************************************************/
PBWL InternalBuildHwndOwnerList( PBWL pbwl, PWND pwndStart, PWND pwndOwner) { PWND pwndT;
/*
* Put ownees first in the list. */ for (pwndT = pwndStart; pwndT != NULL; pwndT = pwndT->spwndNext) {
/*
* Not the ownee we're looking for? Continue. */ if (pwndT->spwndOwner != pwndOwner) continue;
/*
* Only top level windows that have system menus (the ones that can * receive a WM_CLOSE message). */ if (!TestWF(pwndT, WFSYSMENU)) continue;
/*
* Add it and its ownees to our list. */ pbwl = InternalBuildHwndOwnerList(pbwl, pwndStart, pwndT);
/*
* If ExpandWindowList() failed in recursive calls, * just bail here. */ if (pbwl->phwndNext >= pbwl->phwndMax) { UserAssert(pbwl->phwndNext == pbwl->phwndMax); return pbwl; } UserAssert(pbwl->phwndNext < pbwl->phwndMax); }
/*
* Finally add this owner to our list. */ if (pwndOwner != NULL) { UserAssert(pbwl->phwndNext < pbwl->phwndMax); *pbwl->phwndNext = HWq(pwndOwner); pbwl->phwndNext++; if (pbwl->phwndNext == pbwl->phwndMax) { if (!ExpandWindowList(&pbwl)) return pbwl; } }
return pbwl; }
#endif
/***************************************************************************\
* InternalBuildHwndList * * History: * 10-20-90 darrinm Ported from Win 3.0 sources. \***************************************************************************/
#define BWLGROW 8
PBWL InternalBuildHwndList( PBWL pbwl, PWND pwnd, UINT flags) { /*
* NOTE: pbwl->phwndNext is used as a place to keep * the phwnd across calls to InternalBuildHwndList(). * This is OK since we don't link pbwl into the list * of pbwl's until after we've finished enumerating windows. */
while (pwnd != NULL) { /*
* Make sure it matches the thread id, if there is one. */ if (pbwl->ptiOwner == NULL || pbwl->ptiOwner == GETPTI(pwnd)) { UserAssert(pbwl->phwndNext < pbwl->phwndMax); *pbwl->phwndNext = HWq(pwnd); pbwl->phwndNext++; if (pbwl->phwndNext == pbwl->phwndMax) { #if EMULATE_EXPAND_FAILURE
static int n = 0; if (++n % 32 == 0) { RIPMSG0(RIP_WARNING, "InternalBuildHwndList: emulating ExpandWindowList failure."); break; } #endif
if (!ExpandWindowList(&pbwl)) break; } }
/*
* Should we step through the Child windows? */ if ((flags & BWL_ENUMCHILDREN) && pwnd->spwndChild != NULL) { pbwl = InternalBuildHwndList(pbwl, pwnd->spwndChild, BWL_ENUMLIST | BWL_ENUMCHILDREN); /*
* If ExpandWindowList() failed in the recursive call, * we should just bail. */ if (pbwl->phwndNext >= pbwl->phwndMax) { UserAssert(pbwl->phwndNext == pbwl->phwndMax); RIPMSG1(RIP_WARNING, "InternalBuildHwndList: failed to expand BWL in enumerating children. pbwl=%#p", pbwl); break; } UserAssert(pbwl->phwndNext < pbwl->phwndMax); }
/*
* Are we enumerating only one window? */ if (!(flags & BWL_ENUMLIST)) break;
pwnd = pwnd->spwndNext; }
return pbwl; }
/***************************************************************************\
* FreeHwndList * * History: * 10-20-90 darrinm Ported from Win 3.0 sources. \***************************************************************************/
void FreeHwndList( PBWL pbwl) { PBWL *ppbwl; PBWL pbwlT;
CheckCritIn();
/*
* We should never have an active bwl that is the free cached bwl */ UserAssert(pbwl != pbwlCache);
/*
* Unlink this bwl from the list. */ for (ppbwl = &gpbwlList; *ppbwl != NULL; ppbwl = &(*ppbwl)->pbwlNext) { if (*ppbwl == pbwl) { *ppbwl = pbwl->pbwlNext;
/*
* If the cache is empty or this pbwl is larger than the * cached one, save the pbwl there. */ if (pbwlCache == NULL) { pbwlCache = pbwl; } else if ((pbwl->phwndMax - pbwl->rghwnd) > (pbwlCache->phwndMax - pbwlCache->rghwnd)) { pbwlT = pbwlCache; pbwlCache = pbwl; UserFreePool((HANDLE)pbwlT); } else { UserFreePool((HANDLE)pbwl); } return; } }
/*
* Assert if we couldn't find the pbwl in the list... */ UserAssert(FALSE); }
#ifdef FE_IME
PBWL InternalRebuildHwndListForIMEClass( PBWL pbwl, BOOL fRemoveChild) { PHWND phwndIME, phwndIMECur, phwnd, phwndCur; DWORD dwSize = (DWORD)((BYTE *)pbwl->phwndMax - (BYTE *)pbwl) + sizeof(HWND);
phwndIMECur = phwndIME = (PHWND)UserAllocPool(dwSize, TAG_WINDOWLIST); if (phwndIME == NULL) { RIPMSG0(RIP_WARNING, "RebuildHwndListForIMEClass: invalid phwndIME"); return pbwl; }
phwndCur = pbwl->rghwnd;
for (phwnd = pbwl->rghwnd; *phwnd != (HWND)1; phwnd++) { PWND pwndIMEOwner;
// Find the IME class or CS_IME window in the owners of hwnd.
// When fRemoveChild is TRUE, we want IME class window as the return
// of InternalGetIMEOwner.
if (pwndIMEOwner = InternalGetIMEOwner(*phwnd, fRemoveChild)) { try { if (!fRemoveChild || (pwndIMEOwner->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME] && ((PIMEWND)pwndIMEOwner)->pimeui != NULL && !ProbeAndReadStructure(((PIMEWND)pwndIMEOwner)->pimeui, IMEUI).fChildThreadDef)) { *phwndIMECur++ = *phwnd; } } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { } } else { *phwndCur++ = *phwnd; } }
// Here NULL s used as terminator.
*phwndIMECur = NULL;
phwndIMECur = phwndIME; while(*phwndIMECur != NULL) *phwndCur++ = *phwndIMECur++;
if (*phwndCur != (HWND)1) { RIPMSG0(RIP_WARNING, "RebuildHwndListForIMEClass: Where is terminator?"); *phwndCur = (HWND)1; }
UserFreePool((HANDLE)phwndIME); return pbwl; }
PWND InternalGetIMEOwner( HWND hwnd, BOOL fRetIMEWnd) { PWND pwnd, pwndT, pwndIME;
pwnd = RevalidateHwnd(hwnd); if (pwnd == NULL) return NULL;
for (pwndT = pwnd; pwndT != NULL; pwndT = pwndT->spwndOwner) { if (TestCF(pwndT,CFIME) || pwndT->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]) {
if (!fRetIMEWnd) return pwndT;
pwndIME = pwndT;
while (pwndT && (pwndT->pcls->atomClassName != gpsi->atomSysClass[ICLS_IME])) pwndT = pwndT->spwndOwner;
if (pwndT) pwndIME = pwndT; else RIPMSG0(RIP_WARNING, "Can't find IME Class window");
return pwndIME; } }
return NULL; }
#endif
|