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.
550 lines
15 KiB
550 lines
15 KiB
/****************************** 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
|