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.

550 lines
15 KiB

  1. /****************************** Module Header ******************************\
  2. * Module Name: enumwin.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * Contains the EnumWindows API, BuildHwndList and related functions.
  7. *
  8. * History:
  9. * 10-20-90 darrinm Created.
  10. * ??-??-?? ianja Added Revalidation code
  11. * 02-19-91 JimA Added enum access checks
  12. \***************************************************************************/
  13. #include "precomp.h"
  14. #pragma hdrstop
  15. PBWL pbwlCache;
  16. #if DBG
  17. PBWL pbwlCachePrev;
  18. #endif
  19. PBWL InternalBuildHwndList(PBWL pbwl, PWND pwnd, UINT flags);
  20. PBWL InternalBuildHwndOwnerList(PBWL pbwl, PWND pwndStart, PWND pwndOwner);
  21. #ifdef FE_IME
  22. PBWL InternalRebuildHwndListForIMEClass(PBWL pbwl, BOOL fRemoveChild);
  23. PWND InternalGetIMEOwner(HWND hwnd, BOOL fRetIMEWnd);
  24. #endif
  25. /***************************************************************************\
  26. * xxxInternalEnumWindow
  27. *
  28. * History:
  29. * 10-20-90 darrinm Ported from Win 3.0 sources.
  30. * 02-06-91 IanJa rename: the call to lpfn can leave the critsect.
  31. * 02-19-91 JimA Added enum access check
  32. \***************************************************************************/
  33. BOOL xxxInternalEnumWindow(
  34. PWND pwndNext,
  35. WNDENUMPROC_PWND lpfn,
  36. LPARAM lParam,
  37. UINT flags)
  38. {
  39. HWND *phwnd;
  40. PWND pwnd;
  41. PBWL pbwl;
  42. BOOL fSuccess;
  43. TL tlpwnd;
  44. CheckLock(pwndNext);
  45. if ((pbwl = BuildHwndList(pwndNext, flags, NULL)) == NULL)
  46. return FALSE;
  47. fSuccess = TRUE;
  48. for (phwnd = pbwl->rghwnd; *phwnd != (HWND)1; phwnd++) {
  49. /*
  50. * Lock the window before we pass it off to the app.
  51. */
  52. if ((pwnd = RevalidateHwnd(*phwnd)) != NULL) {
  53. /*
  54. * Call the application.
  55. */
  56. ThreadLockAlways(pwnd, &tlpwnd);
  57. fSuccess = (*lpfn)(pwnd, lParam);
  58. ThreadUnlock(&tlpwnd);
  59. if (!fSuccess)
  60. break;
  61. }
  62. }
  63. FreeHwndList(pbwl);
  64. return fSuccess;
  65. }
  66. /***************************************************************************\
  67. * BuildHwndList
  68. *
  69. * History:
  70. * 10-20-90 darrinm Ported from Win 3.0 sources.
  71. \***************************************************************************/
  72. #define CHWND_BWLCREATE 32
  73. PBWL BuildHwndList(
  74. PWND pwnd,
  75. UINT flags,
  76. PTHREADINFO pti)
  77. {
  78. PBWL pbwl;
  79. CheckCritIn();
  80. if ((pbwl = pbwlCache) != NULL) {
  81. /*
  82. * We're using the cache now; zero it out.
  83. */
  84. #if DBG
  85. pbwlCachePrev = pbwlCache;
  86. #endif
  87. pbwlCache = NULL;
  88. #if DBG
  89. {
  90. PBWL pbwlT;
  91. /*
  92. * pbwlCache shouldn't be in the global linked list.
  93. */
  94. for (pbwlT = gpbwlList; pbwlT != NULL; pbwlT = pbwlT->pbwlNext) {
  95. UserAssert(pbwlT != pbwl);
  96. }
  97. }
  98. #endif
  99. } else {
  100. /*
  101. * sizeof(BWL) includes the first element of array.
  102. */
  103. pbwl = (PBWL)UserAllocPool(sizeof(BWL) + sizeof(PWND) * CHWND_BWLCREATE,
  104. TAG_WINDOWLIST);
  105. if (pbwl == NULL)
  106. return NULL;
  107. pbwl->phwndMax = &pbwl->rghwnd[CHWND_BWLCREATE - 1];
  108. }
  109. pbwl->phwndNext = pbwl->rghwnd;
  110. /*
  111. * We'll use ptiOwner as temporary storage for the thread we're
  112. * scanning for. It will get reset to the proper thing at the bottom
  113. * of this routine.
  114. */
  115. pbwl->ptiOwner = pti;
  116. #ifdef OWNERLIST
  117. if (flags & BWL_ENUMOWNERLIST) {
  118. pbwl = InternalBuildHwndOwnerList(pbwl, pwnd, NULL);
  119. } else {
  120. pbwl = InternalBuildHwndList(pbwl, pwnd, flags);
  121. }
  122. #else
  123. pbwl = InternalBuildHwndList(pbwl, pwnd, flags);
  124. #endif
  125. /*
  126. * If phwndNext == phwndMax, it indicates that the pbwl has failed to expand.
  127. * The list is no longer valid, so we should just bail.
  128. */
  129. if (pbwl->phwndNext >= pbwl->phwndMax) {
  130. UserAssert(pbwl->phwndNext == pbwl->phwndMax);
  131. /*
  132. * Even if we had picked pbwl from the global single cache (pbwlCache),
  133. * it should have already been unlinked from the global link list when it was put in the cache.
  134. * So we should just free it without manupilating the link pointers.
  135. * If we have allocated the pwbl for ourselves, we can simply free it.
  136. * In both cases, we should just call UserFreePool().
  137. * As the side effect, it may make some room by providing a free pool block.
  138. */
  139. UserFreePool(pbwl);
  140. return NULL;
  141. }
  142. /*
  143. * Stick in the terminator.
  144. */
  145. *pbwl->phwndNext = (HWND)1;
  146. #ifdef FE_IME
  147. if (flags & BWL_ENUMIMELAST) {
  148. UserAssert(IS_IME_ENABLED());
  149. /*
  150. * For IME windows.
  151. * Rebuild window list for EnumWindows API. Because ACCESS 2.0 assumes
  152. * the first window that is called CallBack Functions in the task is
  153. * Q-Card Wnd. We should change the order of IME windows
  154. */
  155. pbwl = InternalRebuildHwndListForIMEClass(pbwl,
  156. (flags & BWL_REMOVEIMECHILD) == BWL_REMOVEIMECHILD);
  157. }
  158. #endif
  159. /*
  160. * Finally link this guy into the list.
  161. */
  162. pbwl->ptiOwner = PtiCurrent();
  163. pbwl->pbwlNext = gpbwlList;
  164. gpbwlList = pbwl;
  165. /*
  166. * We should have given out the cache if it was available
  167. */
  168. UserAssert(pbwlCache == NULL);
  169. return pbwl;
  170. }
  171. /***************************************************************************\
  172. * ExpandWindowList
  173. *
  174. * This routine expands a window list.
  175. *
  176. * 01-16-92 ScottLu Created.
  177. \***************************************************************************/
  178. BOOL ExpandWindowList(
  179. PBWL *ppbwl)
  180. {
  181. PBWL pbwl;
  182. PBWL pbwlT;
  183. HWND *phwnd;
  184. pbwl = *ppbwl;
  185. phwnd = pbwl->phwndNext;
  186. /*
  187. * Map phwnd to an offset.
  188. */
  189. phwnd = (HWND *)((BYTE *)phwnd - (BYTE *)pbwl);
  190. /*
  191. * Increase size of BWL by 8 slots. (8 + 1) is
  192. * added since phwnd is "sizeof(HWND)" less
  193. * than actual size of handle.
  194. */
  195. pbwlT = (PBWL)UserReAllocPool((HANDLE)pbwl,
  196. PtrToUlong(phwnd) + sizeof(PWND),
  197. PtrToUlong(phwnd) + (BWL_CHWNDMORE + 1) * sizeof(PWND),
  198. TAG_WINDOWLIST);
  199. /*
  200. * Did alloc succeed?
  201. */
  202. if (pbwlT != NULL)
  203. pbwl = pbwlT; /* Yes, use new block. */
  204. /*
  205. * Map phwnd back into a pointer.
  206. */
  207. phwnd = (HWND *)((ULONG_PTR)pbwl + (ULONG_PTR)phwnd);
  208. /*
  209. * Did ReAlloc() fail?
  210. */
  211. if (pbwlT == NULL) {
  212. RIPMSG0(RIP_WARNING, "ExpandWindowList: out of memory.");
  213. return FALSE;
  214. }
  215. /*
  216. * Reset phwndMax.
  217. */
  218. pbwl->phwndNext = phwnd;
  219. pbwl->phwndMax = phwnd + BWL_CHWNDMORE;
  220. *ppbwl = pbwl;
  221. return TRUE;
  222. }
  223. #ifdef OWNERLIST
  224. /***************************************************************************\
  225. * InternalBuildHwndOwnerList
  226. *
  227. * Builds an hwnd list sorted by owner. Ownees go first. Shutdown uses this for
  228. * WM_CLOSE messages.
  229. *
  230. * 01-16-93 ScottLu Created.
  231. \***************************************************************************/
  232. PBWL InternalBuildHwndOwnerList(
  233. PBWL pbwl,
  234. PWND pwndStart,
  235. PWND pwndOwner)
  236. {
  237. PWND pwndT;
  238. /*
  239. * Put ownees first in the list.
  240. */
  241. for (pwndT = pwndStart; pwndT != NULL; pwndT = pwndT->spwndNext) {
  242. /*
  243. * Not the ownee we're looking for? Continue.
  244. */
  245. if (pwndT->spwndOwner != pwndOwner)
  246. continue;
  247. /*
  248. * Only top level windows that have system menus (the ones that can
  249. * receive a WM_CLOSE message).
  250. */
  251. if (!TestWF(pwndT, WFSYSMENU))
  252. continue;
  253. /*
  254. * Add it and its ownees to our list.
  255. */
  256. pbwl = InternalBuildHwndOwnerList(pbwl, pwndStart, pwndT);
  257. /*
  258. * If ExpandWindowList() failed in recursive calls,
  259. * just bail here.
  260. */
  261. if (pbwl->phwndNext >= pbwl->phwndMax) {
  262. UserAssert(pbwl->phwndNext == pbwl->phwndMax);
  263. return pbwl;
  264. }
  265. UserAssert(pbwl->phwndNext < pbwl->phwndMax);
  266. }
  267. /*
  268. * Finally add this owner to our list.
  269. */
  270. if (pwndOwner != NULL) {
  271. UserAssert(pbwl->phwndNext < pbwl->phwndMax);
  272. *pbwl->phwndNext = HWq(pwndOwner);
  273. pbwl->phwndNext++;
  274. if (pbwl->phwndNext == pbwl->phwndMax) {
  275. if (!ExpandWindowList(&pbwl))
  276. return pbwl;
  277. }
  278. }
  279. return pbwl;
  280. }
  281. #endif
  282. /***************************************************************************\
  283. * InternalBuildHwndList
  284. *
  285. * History:
  286. * 10-20-90 darrinm Ported from Win 3.0 sources.
  287. \***************************************************************************/
  288. #define BWLGROW 8
  289. PBWL InternalBuildHwndList(
  290. PBWL pbwl,
  291. PWND pwnd,
  292. UINT flags)
  293. {
  294. /*
  295. * NOTE: pbwl->phwndNext is used as a place to keep
  296. * the phwnd across calls to InternalBuildHwndList().
  297. * This is OK since we don't link pbwl into the list
  298. * of pbwl's until after we've finished enumerating windows.
  299. */
  300. while (pwnd != NULL) {
  301. /*
  302. * Make sure it matches the thread id, if there is one.
  303. */
  304. if (pbwl->ptiOwner == NULL || pbwl->ptiOwner == GETPTI(pwnd)) {
  305. UserAssert(pbwl->phwndNext < pbwl->phwndMax);
  306. *pbwl->phwndNext = HWq(pwnd);
  307. pbwl->phwndNext++;
  308. if (pbwl->phwndNext == pbwl->phwndMax) {
  309. #if EMULATE_EXPAND_FAILURE
  310. static int n = 0;
  311. if (++n % 32 == 0) {
  312. RIPMSG0(RIP_WARNING, "InternalBuildHwndList: emulating ExpandWindowList failure.");
  313. break;
  314. }
  315. #endif
  316. if (!ExpandWindowList(&pbwl))
  317. break;
  318. }
  319. }
  320. /*
  321. * Should we step through the Child windows?
  322. */
  323. if ((flags & BWL_ENUMCHILDREN) && pwnd->spwndChild != NULL) {
  324. pbwl = InternalBuildHwndList(pbwl, pwnd->spwndChild, BWL_ENUMLIST | BWL_ENUMCHILDREN);
  325. /*
  326. * If ExpandWindowList() failed in the recursive call,
  327. * we should just bail.
  328. */
  329. if (pbwl->phwndNext >= pbwl->phwndMax) {
  330. UserAssert(pbwl->phwndNext == pbwl->phwndMax);
  331. RIPMSG1(RIP_WARNING, "InternalBuildHwndList: failed to expand BWL in enumerating children. pbwl=%#p", pbwl);
  332. break;
  333. }
  334. UserAssert(pbwl->phwndNext < pbwl->phwndMax);
  335. }
  336. /*
  337. * Are we enumerating only one window?
  338. */
  339. if (!(flags & BWL_ENUMLIST))
  340. break;
  341. pwnd = pwnd->spwndNext;
  342. }
  343. return pbwl;
  344. }
  345. /***************************************************************************\
  346. * FreeHwndList
  347. *
  348. * History:
  349. * 10-20-90 darrinm Ported from Win 3.0 sources.
  350. \***************************************************************************/
  351. void FreeHwndList(
  352. PBWL pbwl)
  353. {
  354. PBWL *ppbwl;
  355. PBWL pbwlT;
  356. CheckCritIn();
  357. /*
  358. * We should never have an active bwl that is the free cached bwl
  359. */
  360. UserAssert(pbwl != pbwlCache);
  361. /*
  362. * Unlink this bwl from the list.
  363. */
  364. for (ppbwl = &gpbwlList; *ppbwl != NULL; ppbwl = &(*ppbwl)->pbwlNext) {
  365. if (*ppbwl == pbwl) {
  366. *ppbwl = pbwl->pbwlNext;
  367. /*
  368. * If the cache is empty or this pbwl is larger than the
  369. * cached one, save the pbwl there.
  370. */
  371. if (pbwlCache == NULL) {
  372. pbwlCache = pbwl;
  373. } else if ((pbwl->phwndMax - pbwl->rghwnd) >
  374. (pbwlCache->phwndMax - pbwlCache->rghwnd)) {
  375. pbwlT = pbwlCache;
  376. pbwlCache = pbwl;
  377. UserFreePool((HANDLE)pbwlT);
  378. } else {
  379. UserFreePool((HANDLE)pbwl);
  380. }
  381. return;
  382. }
  383. }
  384. /*
  385. * Assert if we couldn't find the pbwl in the list...
  386. */
  387. UserAssert(FALSE);
  388. }
  389. #ifdef FE_IME
  390. PBWL InternalRebuildHwndListForIMEClass(
  391. PBWL pbwl,
  392. BOOL fRemoveChild)
  393. {
  394. PHWND phwndIME, phwndIMECur, phwnd, phwndCur;
  395. DWORD dwSize = (DWORD)((BYTE *)pbwl->phwndMax - (BYTE *)pbwl) + sizeof(HWND);
  396. phwndIMECur = phwndIME = (PHWND)UserAllocPool(dwSize, TAG_WINDOWLIST);
  397. if (phwndIME == NULL) {
  398. RIPMSG0(RIP_WARNING, "RebuildHwndListForIMEClass: invalid phwndIME");
  399. return pbwl;
  400. }
  401. phwndCur = pbwl->rghwnd;
  402. for (phwnd = pbwl->rghwnd; *phwnd != (HWND)1; phwnd++) {
  403. PWND pwndIMEOwner;
  404. // Find the IME class or CS_IME window in the owners of hwnd.
  405. // When fRemoveChild is TRUE, we want IME class window as the return
  406. // of InternalGetIMEOwner.
  407. if (pwndIMEOwner = InternalGetIMEOwner(*phwnd, fRemoveChild)) {
  408. try {
  409. if (!fRemoveChild ||
  410. (pwndIMEOwner->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME] &&
  411. ((PIMEWND)pwndIMEOwner)->pimeui != NULL &&
  412. !ProbeAndReadStructure(((PIMEWND)pwndIMEOwner)->pimeui, IMEUI).fChildThreadDef))
  413. {
  414. *phwndIMECur++ = *phwnd;
  415. }
  416. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  417. }
  418. } else {
  419. *phwndCur++ = *phwnd;
  420. }
  421. }
  422. // Here NULL s used as terminator.
  423. *phwndIMECur = NULL;
  424. phwndIMECur = phwndIME;
  425. while(*phwndIMECur != NULL)
  426. *phwndCur++ = *phwndIMECur++;
  427. if (*phwndCur != (HWND)1) {
  428. RIPMSG0(RIP_WARNING, "RebuildHwndListForIMEClass: Where is terminator?");
  429. *phwndCur = (HWND)1;
  430. }
  431. UserFreePool((HANDLE)phwndIME);
  432. return pbwl;
  433. }
  434. PWND InternalGetIMEOwner(
  435. HWND hwnd,
  436. BOOL fRetIMEWnd)
  437. {
  438. PWND pwnd, pwndT, pwndIME;
  439. pwnd = RevalidateHwnd(hwnd);
  440. if (pwnd == NULL)
  441. return NULL;
  442. for (pwndT = pwnd; pwndT != NULL; pwndT = pwndT->spwndOwner) {
  443. if (TestCF(pwndT,CFIME) ||
  444. pwndT->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]) {
  445. if (!fRetIMEWnd)
  446. return pwndT;
  447. pwndIME = pwndT;
  448. while (pwndT && (pwndT->pcls->atomClassName != gpsi->atomSysClass[ICLS_IME]))
  449. pwndT = pwndT->spwndOwner;
  450. if (pwndT)
  451. pwndIME = pwndT;
  452. else
  453. RIPMSG0(RIP_WARNING, "Can't find IME Class window");
  454. return pwndIME;
  455. }
  456. }
  457. return NULL;
  458. }
  459. #endif