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.

1028 lines
32 KiB

  1. /**************************** Module Header ********************************\
  2. * Module Name: winable2.c
  3. *
  4. * This has the following Active Accesibility API
  5. * GetGUIThreadInfo
  6. * GetTitleBarInfo
  7. * GetScrollBarInfo
  8. * GetMenuBarInfo
  9. * GetComboxBoxInfo
  10. * GetListBoxInfo
  11. *
  12. * The Winevent hooks are handled in winable.c.
  13. *
  14. * Copyright (c) 1985 - 1999, Microsoft Corporation
  15. *
  16. * History:
  17. * 08-30-96 IanJa Ported from Windows '95
  18. \***************************************************************************/
  19. #include "precomp.h"
  20. #pragma hdrstop
  21. /*****************************************************************************\
  22. * _GetGUIThreadInfo
  23. *
  24. * This gets GUI information out of context. If you pass in a NULL thread ID,
  25. * we will get the 'global' information, using the foreground thread. This
  26. * is guaranteed to be the real active window, focus window, etc. Yes, you
  27. * could do it yourself by calling GetForegroundWindow, getting the thread ID
  28. * of that window via GetWindowThreadProcessId, then passing the ID into
  29. * GetGUIThreadInfo(). However, that takes three calls and aside from being
  30. * a pain, anything could happen in the middle. So passing in NULL gets
  31. * you stuff in one call and hence also works right.
  32. *
  33. * This function returns FALSE if the thread doesn't have a queue or the
  34. * thread ID is bogus.
  35. \*****************************************************************************/
  36. BOOL _GetGUIThreadInfo(
  37. PTHREADINFO pti,
  38. PGUITHREADINFO pgui)
  39. {
  40. PQ pq;
  41. /*
  42. * Validate threadinfo structure
  43. */
  44. if (pgui->cbSize != sizeof(GUITHREADINFO)) {
  45. RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "GUITHREADINFO.cbSize %d is wrong", pgui->cbSize);
  46. return FALSE;
  47. }
  48. /*
  49. * Is this a valid initialized GUI thread?
  50. */
  51. if (pti != NULL) {
  52. pq = pti->pq;
  53. } else {
  54. /*
  55. * Use the foreground queue. To get menu state information we must also
  56. * figure out the right pti. This matches _GetForegroundWindow() logic.
  57. */
  58. if ((pq = gpqForeground) == NULL) {
  59. return FALSE;
  60. }
  61. if (pq->spwndActive && (GETPTI(pq->spwndActive)->pq == pq)) {
  62. pti = GETPTI(pq->spwndActive);
  63. if (PtiCurrentShared()->rpdesk != pti->rpdesk) {
  64. RIPERR0(ERROR_ACCESS_DENIED, RIP_VERBOSE, "Foreground window on different desktop");
  65. return FALSE;
  66. }
  67. }
  68. }
  69. UserAssert(pq != NULL);
  70. /*
  71. * For C2 security, verify that pq and pti are on the current thread's desktop.
  72. * We can't directly determine which desktop pq belongs to, but we can at
  73. * least ensure that any caret info we return is not from another desktop
  74. */
  75. if (pq->caret.spwnd &&
  76. (GETPTI(pq->caret.spwnd)->rpdesk != PtiCurrentShared()->rpdesk)) {
  77. RIPERR0(ERROR_ACCESS_DENIED, RIP_VERBOSE, "Foreground caret on different desktop");
  78. return FALSE;
  79. }
  80. if (pti && (pti->rpdesk != PtiCurrentShared()->rpdesk)) {
  81. RIPERR0(ERROR_ACCESS_DENIED, RIP_VERBOSE, "Foreground thread on different desktop");
  82. return FALSE;
  83. }
  84. pgui->flags = 0;
  85. pgui->hwndMoveSize = NULL;
  86. pgui->hwndMenuOwner = NULL;
  87. /*
  88. * Get menu information from the THREADINFO.
  89. */
  90. if (pti != NULL) {
  91. if (pti->pmsd && !pti->pmsd->fTrackCancelled && pti->pmsd->spwnd) {
  92. pgui->flags |= GUI_INMOVESIZE;
  93. pgui->hwndMoveSize = HWq(pti->pmsd->spwnd);
  94. }
  95. if (pti->pMenuState && pti->pMenuState->pGlobalPopupMenu) {
  96. pgui->flags |= GUI_INMENUMODE;
  97. if (pti->pMenuState->pGlobalPopupMenu->fHasMenuBar) {
  98. if (pti->pMenuState->pGlobalPopupMenu->fIsSysMenu) {
  99. pgui->flags |= GUI_SYSTEMMENUMODE;
  100. }
  101. } else {
  102. pgui->flags |= GUI_POPUPMENUMODE;
  103. }
  104. if (pti->pMenuState->pGlobalPopupMenu->spwndNotify) {
  105. pgui->hwndMenuOwner = HWq(pti->pMenuState->pGlobalPopupMenu->spwndNotify);
  106. }
  107. }
  108. if (pti->TIF_flags & TIF_16BIT) {
  109. pgui->flags |= GUI_16BITTASK;
  110. }
  111. }
  112. /*
  113. * Get the rest of the information from the queue.
  114. */
  115. pgui->hwndActive = HW(pq->spwndActive);
  116. pgui->hwndFocus = HW(pq->spwndFocus);
  117. pgui->hwndCapture = HW(pq->spwndCapture);
  118. pgui->hwndCaret = NULL;
  119. if (pq->caret.spwnd) {
  120. pgui->hwndCaret = HWq(pq->caret.spwnd);
  121. if ((GETPTI(pq->caret.spwnd) != PtiCurrentShared()) &&
  122. (pq->caret.spwnd->pcls->style & CS_OWNDC)) {
  123. /*
  124. * This is the case where we are being called by a different
  125. * thread than created the window, and the window has a
  126. * private DC. We have to do extra work to be able to
  127. * return the desired information.
  128. * These coords are always relative to the client of hwndCaret.
  129. */
  130. pgui->rcCaret.left = pq->caret.xOwnDc;
  131. pgui->rcCaret.right = pgui->rcCaret.left + pq->caret.cxOwnDc;
  132. pgui->rcCaret.top = pq->caret.yOwnDc;
  133. pgui->rcCaret.bottom = pgui->rcCaret.top + pq->caret.cyOwnDc;
  134. } else {
  135. /*
  136. * These coords are still in logical coordinates. Ie, these
  137. * are the coordinates we draw at in UT_InvertCaret.
  138. */
  139. pgui->rcCaret.left = pq->caret.x;
  140. pgui->rcCaret.right = pgui->rcCaret.left + pq->caret.cx;
  141. pgui->rcCaret.top = pq->caret.y;
  142. pgui->rcCaret.bottom = pgui->rcCaret.top + pq->caret.cy;
  143. }
  144. if (pq->caret.iHideLevel == 0) {
  145. pgui->flags |= GUI_CARETBLINKING;
  146. }
  147. } else if (pti && (pti->ppi->W32PF_Flags & W32PF_CONSOLEHASFOCUS)) {
  148. /*
  149. * The thread is running in the console window with focus. Pull
  150. * out the info from the console pseudo caret.
  151. */
  152. pgui->hwndCaret = pti->rpdesk->cciConsole.hwnd;
  153. pgui->rcCaret = pti->rpdesk->cciConsole.rc;
  154. } else {
  155. SetRectEmpty(&pgui->rcCaret);
  156. }
  157. return TRUE;
  158. }
  159. /****************************************************************************\
  160. * xxxGetTitleBarInfo
  161. *
  162. * Gets info about a window's title bar. If the window is bogus or
  163. * doesn't have a titlebar, this will fail.
  164. \****************************************************************************/
  165. BOOL xxxGetTitleBarInfo(
  166. PWND pwnd,
  167. PTITLEBARINFO ptbi)
  168. {
  169. int cxB;
  170. CheckLock(pwnd);
  171. /*
  172. * Validate TITLEBARINFO structure.
  173. */
  174. if (ptbi->cbSize != sizeof(TITLEBARINFO)) {
  175. RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "TITLEBARINFO.cbSize %d is wrong", ptbi->cbSize);
  176. return FALSE;
  177. }
  178. RtlZeroMemory(&ptbi->rgstate, sizeof(ptbi->rgstate));
  179. ptbi->rgstate[INDEX_TITLEBAR_SELF] |= STATE_SYSTEM_FOCUSABLE;
  180. if (TestWF(pwnd, WFBORDERMASK) != LOBYTE(WFCAPTION)) {
  181. // No titlebar.
  182. ptbi->rgstate[INDEX_TITLEBAR_SELF] |= STATE_SYSTEM_INVISIBLE;
  183. return TRUE;
  184. }
  185. if (!TestWF(pwnd, WFMINIMIZED) && !TestWF(pwnd, WFCPRESENT)) {
  186. // Off screen (didn't fit)
  187. ptbi->rgstate[INDEX_TITLEBAR_SELF] |= STATE_SYSTEM_OFFSCREEN;
  188. SetRectEmpty(&ptbi->rcTitleBar);
  189. return TRUE;
  190. }
  191. /*
  192. * Get titlebar rect.
  193. */
  194. ptbi->rcTitleBar = pwnd->rcWindow;
  195. cxB = GetWindowBorders(pwnd->style, pwnd->ExStyle, TRUE, FALSE);
  196. InflateRect(&ptbi->rcTitleBar, -cxB * SYSMET(CXBORDER), -cxB * SYSMET(CYBORDER));
  197. if (TestWF(pwnd, WEFTOOLWINDOW)) {
  198. ptbi->rcTitleBar.bottom = ptbi->rcTitleBar.top + SYSMET(CYSMCAPTION);
  199. } else {
  200. ptbi->rcTitleBar.bottom = ptbi->rcTitleBar.top + SYSMET(CYCAPTION);
  201. }
  202. /*
  203. * Don't include the system menu area!
  204. */
  205. if (TestWF(pwnd, WFSYSMENU) && _HasCaptionIcon(pwnd)) {
  206. ptbi->rcTitleBar.left += (ptbi->rcTitleBar.bottom - ptbi->rcTitleBar.top - SYSMET(CYBORDER));
  207. }
  208. /*
  209. * Close button.
  210. */
  211. if (!TestWF(pwnd, WFSYSMENU) && TestWF(pwnd, WFWIN40COMPAT)) {
  212. ptbi->rgstate[INDEX_TITLEBAR_CLOSEBUTTON] |= STATE_SYSTEM_INVISIBLE;
  213. } else {
  214. if (!xxxMNCanClose(pwnd)) {
  215. ptbi->rgstate[INDEX_TITLEBAR_CLOSEBUTTON] |= STATE_SYSTEM_UNAVAILABLE;
  216. }
  217. if (TestWF(pwnd, WFCLOSEBUTTONDOWN)) {
  218. ptbi->rgstate[INDEX_TITLEBAR_CLOSEBUTTON] |= STATE_SYSTEM_PRESSED;
  219. }
  220. }
  221. /*
  222. * Max button.
  223. */
  224. if (!TestWF(pwnd, WFSYSMENU) && TestWF(pwnd, WFWIN40COMPAT)) {
  225. ptbi->rgstate[INDEX_TITLEBAR_MAXBUTTON] |= STATE_SYSTEM_INVISIBLE;
  226. } else {
  227. if (!TestWF(pwnd, WFMAXBOX)) {
  228. if (!TestWF(pwnd, WFMINBOX)) {
  229. ptbi->rgstate[INDEX_TITLEBAR_MAXBUTTON] |= STATE_SYSTEM_INVISIBLE;
  230. } else {
  231. ptbi->rgstate[INDEX_TITLEBAR_MAXBUTTON] |= STATE_SYSTEM_UNAVAILABLE;
  232. }
  233. }
  234. if (TestWF(pwnd, WFZOOMBUTTONDOWN)) {
  235. ptbi->rgstate[INDEX_TITLEBAR_MAXBUTTON] |= STATE_SYSTEM_PRESSED;
  236. }
  237. }
  238. /*
  239. * Min button.
  240. */
  241. if (!TestWF(pwnd, WFSYSMENU) && TestWF(pwnd, WFWIN40COMPAT)) {
  242. ptbi->rgstate[INDEX_TITLEBAR_MINBUTTON] |= STATE_SYSTEM_INVISIBLE;
  243. } else {
  244. if (!TestWF(pwnd, WFMINBOX)) {
  245. if (!TestWF(pwnd, WFMAXBOX)) {
  246. ptbi->rgstate[INDEX_TITLEBAR_MINBUTTON] |= STATE_SYSTEM_INVISIBLE;
  247. } else {
  248. ptbi->rgstate[INDEX_TITLEBAR_MINBUTTON] |= STATE_SYSTEM_UNAVAILABLE;
  249. }
  250. }
  251. if (TestWF(pwnd, WFREDUCEBUTTONDOWN)) {
  252. ptbi->rgstate[INDEX_TITLEBAR_MINBUTTON] |= STATE_SYSTEM_PRESSED;
  253. }
  254. }
  255. /*
  256. * Help button.
  257. */
  258. if (!TestWF(pwnd, WEFCONTEXTHELP) || TestWF(pwnd, WFMINBOX) ||
  259. TestWF(pwnd, WFMAXBOX)) {
  260. ptbi->rgstate[INDEX_TITLEBAR_HELPBUTTON] |= STATE_SYSTEM_INVISIBLE;
  261. } else {
  262. if (TestWF(pwnd, WFHELPBUTTONDOWN)) {
  263. ptbi->rgstate[INDEX_TITLEBAR_HELPBUTTON] |= STATE_SYSTEM_PRESSED;
  264. }
  265. }
  266. // IME button BOGUS!
  267. ptbi->rgstate[INDEX_TITLEBAR_IMEBUTTON] = STATE_SYSTEM_INVISIBLE;
  268. return TRUE;
  269. }
  270. /*****************************************************************************\
  271. * xxxGetScrollBarInfo
  272. *
  273. * Gets state & location information about a scrollbar.
  274. *
  275. * Note we fill in the minimal amount of useful info. OLEACC is responsible
  276. * for extrapolation. I.E., if both the line up and line down buttons are
  277. * disabled, the whole scrollbar is, and the thumb is invisible.
  278. \*****************************************************************************/
  279. BOOL xxxGetScrollBarInfo(
  280. PWND pwnd,
  281. LONG idObject,
  282. PSCROLLBARINFO psbi)
  283. {
  284. UINT wDisable;
  285. BOOL fVertical;
  286. SBCALC SBCalc;
  287. PCLS pcls;
  288. CheckLock(pwnd);
  289. /*
  290. * Validate SCROLLBARINFO structure.
  291. */
  292. if (psbi->cbSize != sizeof(SCROLLBARINFO)) {
  293. RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING,
  294. "SCROLLBARINFO.cbSize 0x%x is wrong", psbi->cbSize);
  295. return FALSE;
  296. }
  297. pcls = pwnd->pcls;
  298. if ((idObject == OBJID_CLIENT) &&
  299. (GETFNID(pwnd) != FNID_SCROLLBAR) &&
  300. (pcls->atomClassName != gpsi->atomSysClass[ICLS_SCROLLBAR])) {
  301. return (BOOL)xxxSendMessage(pwnd, SBM_GETSCROLLBARINFO, 0, (LPARAM)psbi);
  302. }
  303. RtlZeroMemory(&psbi->rgstate, sizeof(psbi->rgstate));
  304. /*
  305. * Calculate where everything is.
  306. */
  307. if (idObject == OBJID_CLIENT) {
  308. RECT rc;
  309. wDisable = ((PSBWND)pwnd)->wDisableFlags;
  310. fVertical = ((PSBWND)pwnd)->fVert;
  311. GetRect(pwnd, &rc, GRECT_CLIENT | GRECT_CLIENTCOORDS);
  312. CalcSBStuff2(&SBCalc, &rc, (PSBDATA)&((PSBWND)pwnd)->SBCalc, ((PSBWND)pwnd)->fVert);
  313. } else {
  314. /*
  315. * Is this window scrollbar here?
  316. */
  317. if (idObject == OBJID_VSCROLL) {
  318. fVertical = TRUE;
  319. if (!TestWF(pwnd, WFVSCROLL)) {
  320. // No scrollbar.
  321. psbi->rgstate[INDEX_SCROLLBAR_SELF] |= STATE_SYSTEM_INVISIBLE;
  322. } else if (!TestWF(pwnd, WFVPRESENT)) {
  323. // Window too short to display it.
  324. psbi->rgstate[INDEX_SCROLLBAR_SELF] |= STATE_SYSTEM_OFFSCREEN;
  325. }
  326. } else if (idObject == OBJID_HSCROLL) {
  327. fVertical = FALSE;
  328. if (! TestWF(pwnd, WFHSCROLL)) {
  329. // No scrollbar.
  330. psbi->rgstate[INDEX_SCROLLBAR_SELF] |= STATE_SYSTEM_INVISIBLE;
  331. } else if (! TestWF(pwnd, WFHPRESENT)) {
  332. psbi->rgstate[INDEX_SCROLLBAR_SELF] |= STATE_SYSTEM_OFFSCREEN;
  333. }
  334. } else {
  335. RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "invalid idObject %d", idObject);
  336. return FALSE;
  337. }
  338. if (psbi->rgstate[INDEX_SCROLLBAR_SELF] & STATE_SYSTEM_INVISIBLE) {
  339. return TRUE;
  340. }
  341. wDisable = GetWndSBDisableFlags(pwnd, fVertical);
  342. if (!(psbi->rgstate[INDEX_SCROLLBAR_SELF] & STATE_SYSTEM_OFFSCREEN)) {
  343. CalcSBStuff(pwnd, &SBCalc, fVertical);
  344. }
  345. }
  346. /*
  347. * Setup button states.
  348. */
  349. if (wDisable & LTUPFLAG) {
  350. psbi->rgstate[INDEX_SCROLLBAR_UP] |= STATE_SYSTEM_UNAVAILABLE;
  351. psbi->rgstate[INDEX_SCROLLBAR_UPPAGE] |= STATE_SYSTEM_UNAVAILABLE;
  352. }
  353. if (wDisable & RTDNFLAG) {
  354. psbi->rgstate[INDEX_SCROLLBAR_DOWN] |= STATE_SYSTEM_UNAVAILABLE;
  355. psbi->rgstate[INDEX_SCROLLBAR_DOWNPAGE] |= STATE_SYSTEM_UNAVAILABLE;
  356. }
  357. if ((wDisable & (LTUPFLAG | RTDNFLAG)) == (LTUPFLAG | RTDNFLAG)) {
  358. psbi->rgstate[INDEX_SCROLLBAR_SELF] |= STATE_SYSTEM_UNAVAILABLE;
  359. }
  360. /*
  361. * Button pressed?
  362. */
  363. if (TestWF(pwnd, WFSCROLLBUTTONDOWN) &&
  364. ((idObject != OBJID_VSCROLL) || TestWF(pwnd, WFVERTSCROLLTRACK))) {
  365. if (TestWF(pwnd, WFLINEUPBUTTONDOWN)) {
  366. psbi->rgstate[INDEX_SCROLLBAR_UP] |= STATE_SYSTEM_PRESSED;
  367. }
  368. if (TestWF(pwnd, WFPAGEUPBUTTONDOWN)) {
  369. psbi->rgstate[INDEX_SCROLLBAR_UPPAGE] |= STATE_SYSTEM_PRESSED;
  370. }
  371. if (TestWF(pwnd, WFPAGEDNBUTTONDOWN)) {
  372. psbi->rgstate[INDEX_SCROLLBAR_DOWNPAGE] |= STATE_SYSTEM_PRESSED;
  373. }
  374. if (TestWF(pwnd, WFLINEDNBUTTONDOWN)) {
  375. psbi->rgstate[INDEX_SCROLLBAR_DOWN] |= STATE_SYSTEM_PRESSED;
  376. }
  377. }
  378. /*
  379. * Fill in area locations.
  380. */
  381. if (!(psbi->rgstate[INDEX_SCROLLBAR_SELF] & STATE_SYSTEM_OFFSCREEN)) {
  382. if (fVertical) {
  383. psbi->rcScrollBar.left = SBCalc.pxLeft;
  384. psbi->rcScrollBar.top = SBCalc.pxTop;
  385. psbi->rcScrollBar.right = SBCalc.pxRight;
  386. psbi->rcScrollBar.bottom = SBCalc.pxBottom;
  387. } else {
  388. psbi->rcScrollBar.left = SBCalc.pxTop;
  389. psbi->rcScrollBar.top = SBCalc.pxLeft;
  390. psbi->rcScrollBar.right = SBCalc.pxBottom;
  391. psbi->rcScrollBar.bottom = SBCalc.pxRight;
  392. }
  393. if (idObject == OBJID_CLIENT) {
  394. OffsetRect(&psbi->rcScrollBar, pwnd->rcClient.left, pwnd->rcClient.top);
  395. } else {
  396. OffsetRect(&psbi->rcScrollBar, pwnd->rcWindow.left, pwnd->rcWindow.top);
  397. }
  398. psbi->dxyLineButton = (SBCalc.pxUpArrow - SBCalc.pxTop);
  399. psbi->xyThumbTop = (SBCalc.pxThumbTop - SBCalc.pxTop);
  400. psbi->xyThumbBottom = (SBCalc.pxThumbBottom - SBCalc.pxTop);
  401. /*
  402. * Is the thumb all the way to the left/top? If so, page up is
  403. * not visible.
  404. */
  405. if (SBCalc.pxThumbTop == SBCalc.pxUpArrow) {
  406. psbi->rgstate[INDEX_SCROLLBAR_UPPAGE] |= STATE_SYSTEM_INVISIBLE;
  407. }
  408. /*
  409. * Is the thumb all the way to the right/down? If so, page down
  410. * is not visible.
  411. */
  412. if (SBCalc.pxThumbBottom == SBCalc.pxDownArrow) {
  413. psbi->rgstate[INDEX_SCROLLBAR_DOWNPAGE] |= STATE_SYSTEM_INVISIBLE;
  414. }
  415. }
  416. return TRUE;
  417. }
  418. /*****************************************************************************\
  419. * _GetAncestor
  420. *
  421. * This gets one of:
  422. * * The _real_ parent. This does NOT include the owner, unlike GetParent().
  423. * Stops at a top level window unless we start with the desktop. In which
  424. * case, we return the desktop.
  425. * * The _real_ root, caused by walking up the chain getting the ancestor.
  426. * * The _real_ owned root, caused by GetParent()ing up.
  427. \*****************************************************************************/
  428. PWND _GetAncestor(
  429. PWND pwnd,
  430. UINT gaFlags)
  431. {
  432. PWND pwndParent;
  433. /*
  434. * If we start with the desktop, the message window or the mother window,
  435. * return NULL.
  436. */
  437. if (pwnd == PWNDDESKTOP(pwnd) ||
  438. pwnd == PWNDMESSAGE(pwnd) ||
  439. pwnd->spwndParent == NULL) {
  440. return NULL;
  441. }
  442. switch (gaFlags) {
  443. case GA_PARENT:
  444. pwnd = pwnd->spwndParent;
  445. break;
  446. case GA_ROOT:
  447. while ((pwnd->spwndParent != PWNDDESKTOP(pwnd)) &&
  448. (pwnd->spwndParent != PWNDMESSAGE(pwnd))) {
  449. pwnd = pwnd->spwndParent;
  450. }
  451. break;
  452. case GA_ROOTOWNER:
  453. while (pwndParent = _GetParent(pwnd)) {
  454. pwnd = pwndParent;
  455. }
  456. break;
  457. }
  458. return pwnd;
  459. }
  460. /*****************************************************************************\
  461. * _RealChildWindowFromPoint
  462. *
  463. * This returns the REAL child window at a point. The problem is that
  464. * ChildWindowFromPoint() doesn't deal with HTTRANSPARENT areas of
  465. * standard controls. We want to return a child behind a groupbox if it
  466. * is in the "clear" area. But we want to return a static field always
  467. * even though it too returns HTTRANSPARENT.
  468. \*****************************************************************************/
  469. PWND _RealChildWindowFromPoint(
  470. PWND pwndParent,
  471. POINT pt)
  472. {
  473. PWND pwndChild;
  474. PWND pwndSave;
  475. if (pwndParent != PWNDDESKTOP(pwndParent)) {
  476. pt.x += pwndParent->rcClient.left;
  477. pt.y += pwndParent->rcClient.top;
  478. }
  479. /*
  480. * Is this point even in the parent?
  481. */
  482. if (!PtInRect(&pwndParent->rcClient, pt) ||
  483. (pwndParent->hrgnClip && !GrePtInRegion(pwndParent->hrgnClip, pt.x, pt.y))) {
  484. // Nope
  485. return NULL;
  486. }
  487. pwndSave = NULL;
  488. /*
  489. * Loop through the children.
  490. */
  491. for (pwndChild = pwndParent->spwndChild; pwndChild; pwndChild = pwndChild->spwndNext) {
  492. if (!TestWF(pwndChild, WFVISIBLE))
  493. continue;
  494. /*
  495. * Is this point in the child's window?
  496. */
  497. if (!PtInRect(&pwndChild->rcWindow, pt) ||
  498. (pwndChild->hrgnClip && !GrePtInRegion(pwndChild->hrgnClip, pt.x, pt.y)))
  499. continue;
  500. /*
  501. * OK, we are in somebody's window. Is this by chance a group box?
  502. */
  503. if (IS_BUTTON(pwndChild)) {
  504. if (TestWF(pwndChild, BFTYPEMASK) == LOBYTE(BS_GROUPBOX)) {
  505. pwndSave = pwndChild;
  506. continue;
  507. }
  508. }
  509. return pwndChild;
  510. }
  511. /*
  512. * Did we save a groupbox which turned out to have nothing behind it
  513. * at that point?
  514. */
  515. if (pwndSave) {
  516. return pwndSave;
  517. } else {
  518. return pwndParent;
  519. }
  520. }
  521. /*****************************************************************************\
  522. * xxxGetMenuBarInfo
  523. *
  524. * This succeeds if the menu/menu item exists.
  525. *
  526. * Parameters:
  527. * pwnd window
  528. * idObject this can be OBJID_MENU, OBJID_SYSMENU, or OBJID_CLIENT
  529. * idItem which thing do we need info on? 0..cItems. 0 indicates
  530. * the menu itself, 1 is the first item on the menu...
  531. * pmbi Pointer to a MENUBARINFO structure that gets filled in
  532. *
  533. \*****************************************************************************/
  534. BOOL xxxGetMenuBarInfo(
  535. PWND pwnd,
  536. long idObject,
  537. long idItem,
  538. PMENUBARINFO pmbi)
  539. {
  540. PMENU pMenu;
  541. int cBorders;
  542. PITEM pItem;
  543. PPOPUPMENU ppopup;
  544. CheckLock(pwnd);
  545. /*
  546. * Validate MENUBARINFO structure.
  547. */
  548. if (pmbi->cbSize != sizeof(MENUBARINFO)) {
  549. RIPERR1(ERROR_INVALID_PARAMETER,
  550. RIP_WARNING,
  551. "MENUBARINFO.cbSize 0x%x is wrong",
  552. pmbi->cbSize);
  553. return FALSE;
  554. }
  555. /*
  556. * Initialize the fields.
  557. */
  558. SetRectEmpty(&pmbi->rcBar);
  559. pmbi->hMenu = NULL;
  560. pmbi->hwndMenu = NULL;
  561. pmbi->fBarFocused = FALSE;
  562. pmbi->fFocused = FALSE;
  563. /*
  564. * Get the menu handle we will deal with.
  565. */
  566. if (idObject == OBJID_MENU) {
  567. int cBorders;
  568. if (TestWF(pwnd, WFCHILD) || !pwnd->spmenu) {
  569. return FALSE;
  570. }
  571. pMenu = pwnd->spmenu;
  572. if (!pMenu) {
  573. return FALSE;
  574. }
  575. /*
  576. * If we have an item, is it in the valid range?
  577. */
  578. if ((idItem < 0) || ((DWORD)idItem > pMenu->cItems)) {
  579. return FALSE;
  580. }
  581. /*
  582. * Menu handle.
  583. */
  584. pmbi->hMenu = PtoHq(pMenu);
  585. /*
  586. * Menu rect.
  587. */
  588. if (pMenu->cxMenu && pMenu->cyMenu) {
  589. if (!idItem) {
  590. cBorders = GetWindowBorders(pwnd->style, pwnd->ExStyle, TRUE, FALSE);
  591. pmbi->rcBar.left = pwnd->rcWindow.left + cBorders * SYSMET(CXBORDER);
  592. pmbi->rcBar.top = pwnd->rcWindow.top + cBorders * SYSMET(CYBORDER);
  593. if (TestWF(pwnd, WFCPRESENT)) {
  594. pmbi->rcBar.top += (TestWF(pwnd, WEFTOOLWINDOW) ? SYSMET(CYSMCAPTION) : SYSMET(CYCAPTION));
  595. }
  596. pmbi->rcBar.right = pmbi->rcBar.left + pMenu->cxMenu;
  597. pmbi->rcBar.bottom = pmbi->rcBar.top + pMenu->cyMenu;
  598. } else {
  599. pItem = pMenu->rgItems + idItem - 1;
  600. pmbi->rcBar.left = pwnd->rcWindow.left + pItem->xItem;
  601. pmbi->rcBar.top = pwnd->rcWindow.top + pItem->yItem;
  602. pmbi->rcBar.right = pmbi->rcBar.left + pItem->cxItem;
  603. pmbi->rcBar.bottom = pmbi->rcBar.top + pItem->cyItem;
  604. }
  605. }
  606. /*
  607. * Are we currently in app menu bar mode?
  608. */
  609. ppopup = GetpGlobalPopupMenu(pwnd);
  610. if (ppopup && ppopup->fHasMenuBar && !ppopup->fIsSysMenu &&
  611. (ppopup->spwndNotify == pwnd)) {
  612. pmbi->fBarFocused = TRUE;
  613. if (!idItem) {
  614. pmbi->fFocused = TRUE;
  615. } else if (ppopup->ppopupmenuRoot->posSelectedItem == (UINT)idItem-1) {
  616. pmbi->fFocused = TRUE;
  617. UserAssert(ppopup->ppopupmenuRoot);
  618. pmbi->hwndMenu = HW(ppopup->ppopupmenuRoot->spwndNextPopup);
  619. }
  620. }
  621. } else if (idObject == OBJID_SYSMENU) {
  622. if (!TestWF(pwnd, WFSYSMENU)) {
  623. return FALSE;
  624. }
  625. pMenu = xxxGetSysMenu(pwnd, FALSE);
  626. if (!pMenu) {
  627. return FALSE;
  628. }
  629. // If we have an item, is it in the valid range?
  630. if ((idItem < 0) || ((DWORD)idItem > pMenu->cItems))
  631. return FALSE;
  632. pmbi->hMenu = PtoHq(pMenu);
  633. /*
  634. * Menu rect
  635. */
  636. if (_HasCaptionIcon(pwnd)) {
  637. // The menu and single item take up the same space
  638. cBorders = GetWindowBorders(pwnd->style, pwnd->ExStyle, TRUE, FALSE);
  639. pmbi->rcBar.left = pwnd->rcWindow.left + cBorders * SYSMET(CXBORDER);
  640. pmbi->rcBar.top = pwnd->rcWindow.top + cBorders * SYSMET(CYBORDER);
  641. pmbi->rcBar.right = pmbi->rcBar.left +
  642. (TestWF(pwnd, WEFTOOLWINDOW) ? SYSMET(CXSMSIZE) : SYSMET(CXSIZE));
  643. pmbi->rcBar.bottom = pmbi->rcBar.top +
  644. (TestWF(pwnd, WEFTOOLWINDOW) ? SYSMET(CYSMSIZE) : SYSMET(CYSIZE));
  645. }
  646. /*
  647. * Are we currently in system menu bar mode?
  648. */
  649. ppopup = GetpGlobalPopupMenu(pwnd);
  650. if (ppopup && ppopup->fHasMenuBar && ppopup->fIsSysMenu &&
  651. (ppopup->spwndNotify == pwnd))
  652. {
  653. pmbi->fBarFocused = TRUE;
  654. if (!idItem) {
  655. pmbi->fFocused = TRUE;
  656. } else if (ppopup->ppopupmenuRoot->posSelectedItem == (UINT)idItem - 1) {
  657. pmbi->fFocused = TRUE;
  658. UserAssert(ppopup->ppopupmenuRoot);
  659. pmbi->hwndMenu = HW(ppopup->ppopupmenuRoot->spwndNextPopup);
  660. }
  661. }
  662. } else if (idObject == OBJID_CLIENT) {
  663. HMENU hMenu = (HMENU)xxxSendMessage(pwnd, MN_GETHMENU, 0, 0);
  664. pMenu = ValidateHmenu(hMenu);
  665. if (!pMenu) {
  666. return FALSE;
  667. }
  668. // If we have an item, is it in the valid range?
  669. if ((idItem < 0) || ((DWORD)idItem > pMenu->cItems)) {
  670. return FALSE;
  671. }
  672. pmbi->hMenu = hMenu;
  673. if (!idItem) {
  674. pmbi->rcBar = pwnd->rcClient;
  675. } else {
  676. pItem = pMenu->rgItems + idItem - 1;
  677. pmbi->rcBar.left = pwnd->rcClient.left + pItem->xItem;
  678. pmbi->rcBar.top = pwnd->rcClient.top + pItem->yItem;
  679. pmbi->rcBar.right = pmbi->rcBar.left + pItem->cxItem;
  680. pmbi->rcBar.bottom = pmbi->rcBar.top + pItem->cyItem;
  681. }
  682. /*
  683. * Are we currently in popup mode with us as one of the popups
  684. * showing?
  685. *
  686. * Since malicious code could handle MN_GETHMENU and return a valid
  687. * HMENU *w/o* pwnd being a real MENUWND, we need to explicitly
  688. * check the fnid.
  689. */
  690. if (GETFNID(pwnd) == FNID_MENU &&
  691. (ppopup = ((PMENUWND)pwnd)->ppopupmenu) &&
  692. (ppopup->ppopupmenuRoot == GetpGlobalPopupMenu(pwnd))) {
  693. pmbi->fBarFocused = TRUE;
  694. if (!idItem) {
  695. pmbi->fFocused = TRUE;
  696. } else if ((UINT)idItem == ppopup->posSelectedItem + 1) {
  697. pmbi->fFocused = TRUE;
  698. pmbi->hwndMenu = HW(ppopup->spwndNextPopup);
  699. }
  700. }
  701. } else {
  702. return FALSE;
  703. }
  704. return TRUE;
  705. }
  706. /***************************************************************************\
  707. * xxxGetComboBoxInfo
  708. *
  709. * This returns combobox information for either a combo or its dropdown
  710. * list.
  711. \***************************************************************************/
  712. BOOL xxxGetComboBoxInfo(
  713. PWND pwnd,
  714. PCOMBOBOXINFO pcbi)
  715. {
  716. PCLS pcls;
  717. COMBOBOXINFO cbi = {
  718. sizeof cbi,
  719. };
  720. BOOL fOtherProcess;
  721. BOOL bRetval = FALSE;
  722. WORD wWindowType = 0;
  723. CheckLock(pwnd);
  724. /*
  725. * Make sure it is a combobox or a dropdown.
  726. */
  727. pcls = pwnd->pcls;
  728. if ((GETFNID(pwnd) == FNID_COMBOBOX) ||
  729. (pcls->atomClassName == gpsi->atomSysClass[ICLS_COMBOBOX])) {
  730. wWindowType = FNID_COMBOBOX;
  731. } else if ((GETFNID(pwnd) == FNID_COMBOLISTBOX) ||
  732. (pcls->atomClassName == gpsi->atomSysClass[ICLS_COMBOLISTBOX])) {
  733. wWindowType = FNID_COMBOLISTBOX;
  734. } else {
  735. return (BOOL)xxxSendMessage(pwnd, CB_GETCOMBOBOXINFO, 0, (LPARAM)pcbi);
  736. }
  737. /*
  738. * Validate combo structure
  739. */
  740. if (pcbi->cbSize != sizeof(COMBOBOXINFO)) {
  741. RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "COMBOBOXINFO.cbSize %d is wrong", pcbi->cbSize);
  742. return FALSE;
  743. }
  744. if (fOtherProcess = (GETPTI(pwnd)->ppi != PpiCurrent())) {
  745. KeAttachProcess(PsGetProcessPcb(GETPTI(pwnd)->ppi->Process));
  746. }
  747. try {
  748. PCBOX ccxPcboxSnap;
  749. PWND ccxPwndSnap;
  750. HWND ccxHwndSnap;
  751. /*
  752. * Snap and probe the CBOX structure, since it is client side.
  753. */
  754. if (wWindowType == FNID_COMBOBOX) {
  755. ccxPcboxSnap = ((PCOMBOWND)pwnd)->pcbox;
  756. } else {
  757. PLBIV ccxPlbSnap;
  758. /*
  759. * If this is a listbox, we must snap and probe the LBIV structure
  760. * in order to get to the CBOX structure.
  761. */
  762. ccxPlbSnap = ((PLBWND)pwnd)->pLBIV;
  763. if (!ccxPlbSnap) {
  764. goto errorexit;
  765. }
  766. ProbeForRead(ccxPlbSnap, sizeof(LBIV), DATAALIGN);
  767. ccxPcboxSnap = ccxPlbSnap->pcbox;
  768. }
  769. if (!ccxPcboxSnap) {
  770. goto errorexit;
  771. }
  772. ProbeForRead(ccxPcboxSnap, sizeof(CBOX), DATAALIGN);
  773. /*
  774. * Get the combo information now.
  775. */
  776. /*
  777. * Snap and probe the client side pointer to the Combo window.
  778. */
  779. ccxPwndSnap = ccxPcboxSnap->spwnd;
  780. ProbeForRead(ccxPwndSnap, sizeof(HEAD), DATAALIGN);
  781. cbi.hwndCombo = HWCCX(ccxPwndSnap);
  782. /*
  783. * Snap & probe the client side pointer to the Edit window.
  784. * To compare spwndEdit and pwnd, we should compare handles
  785. * since spwndEdit is a client-side address and pwnd is a
  786. * kernel-mode address,
  787. */
  788. ccxPwndSnap = ccxPcboxSnap->spwndEdit;
  789. /*
  790. * If combobox is not fully initialized and spwndEdit is NULL,
  791. * we should fail.
  792. */
  793. ProbeForRead(ccxPwndSnap, sizeof(HEAD), DATAALIGN);
  794. ccxHwndSnap = HWCCX(ccxPwndSnap);
  795. if (ccxHwndSnap == HW(pwnd)) {
  796. /*
  797. * ComboBox doesn't have Edit control.
  798. */
  799. cbi.hwndItem = NULL;
  800. } else {
  801. cbi.hwndItem = HWCCX(ccxPwndSnap);
  802. }
  803. /*
  804. * Snap and probe the client side pointer to the List window
  805. */
  806. ccxPwndSnap = ccxPcboxSnap->spwndList;
  807. /*
  808. * If combobox is not fully initialized and spwndList is NULL,
  809. * we should fail.
  810. */
  811. ProbeForRead(ccxPwndSnap, sizeof(HEAD), DATAALIGN);
  812. cbi.hwndList = HWCCX(ccxPwndSnap);
  813. /*
  814. * Snap the rest of the combo information. We don't need to probe
  815. * any of these, since there are no more indirections.
  816. */
  817. cbi.rcItem = ccxPcboxSnap->editrc;
  818. cbi.rcButton = ccxPcboxSnap->buttonrc;
  819. /*
  820. * Button state.
  821. */
  822. cbi.stateButton = 0;
  823. if (ccxPcboxSnap->CBoxStyle == CBS_SIMPLE) {
  824. cbi.stateButton |= STATE_SYSTEM_INVISIBLE;
  825. }
  826. if (ccxPcboxSnap->fButtonPressed) {
  827. cbi.stateButton |= STATE_SYSTEM_PRESSED;
  828. }
  829. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  830. goto errorexit;
  831. }
  832. *pcbi = cbi;
  833. bRetval = TRUE;
  834. errorexit:
  835. if (fOtherProcess) {
  836. KeDetachProcess();
  837. }
  838. return bRetval;
  839. }
  840. /***************************************************************************\
  841. * xxxGetListBoxInfo
  842. *
  843. * Currently returns back the # of items per column. There is no way to get
  844. * or calculate this info any other way in a multicolumn list.
  845. *
  846. * For now, no structure is returned. If we ever need one more thing, make one.
  847. \***************************************************************************/
  848. DWORD xxxGetListBoxInfo(
  849. PWND pwnd)
  850. {
  851. PCLS pcls;
  852. DWORD dwRet = 0;
  853. BOOL fOtherProcess;
  854. CheckLock(pwnd);
  855. /*
  856. * Make sure it is a combobox or a dropdown.
  857. */
  858. pcls = pwnd->pcls;
  859. if ((pcls->atomClassName != gpsi->atomSysClass[ICLS_LISTBOX]) &&
  860. (GETFNID(pwnd) != FNID_LISTBOX)) {
  861. return (DWORD)xxxSendMessage(pwnd, LB_GETLISTBOXINFO, 0, 0);
  862. }
  863. if (fOtherProcess = (GETPTI(pwnd)->ppi != PpiCurrent())) {
  864. KeAttachProcess(PsGetProcessPcb(GETPTI(pwnd)->ppi->Process));
  865. }
  866. try {
  867. PLBIV ccxPlbSnap;
  868. /*
  869. * Snap and probe the pointer to the LBIV, since it is client-side.
  870. */
  871. ccxPlbSnap = ((PLBWND)pwnd)->pLBIV;
  872. if (!ccxPlbSnap) {
  873. goto errorexit;
  874. }
  875. ProbeForRead(ccxPlbSnap, sizeof(LBIV), DATAALIGN);
  876. if (ccxPlbSnap->fMultiColumn) {
  877. dwRet = ccxPlbSnap->itemsPerColumn;
  878. } else {
  879. dwRet = ccxPlbSnap->cMac;
  880. }
  881. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  882. dwRet = 0;
  883. }
  884. errorexit:
  885. if (fOtherProcess) {
  886. KeDetachProcess();
  887. }
  888. return dwRet;
  889. }