/**************************** Module Header ********************************\ * Module Name: winable2.c * * This has the following Active Accesibility API * GetGUIThreadInfo * GetTitleBarInfo * GetScrollBarInfo * GetMenuBarInfo * GetComboxBoxInfo * GetListBoxInfo * * The Winevent hooks are handled in winable.c. * * Copyright (c) 1985 - 1999, Microsoft Corporation * * History: * 08-30-96 IanJa Ported from Windows '95 \***************************************************************************/ #include "precomp.h" #pragma hdrstop /*****************************************************************************\ * _GetGUIThreadInfo * * This gets GUI information out of context. If you pass in a NULL thread ID, * we will get the 'global' information, using the foreground thread. This * is guaranteed to be the real active window, focus window, etc. Yes, you * could do it yourself by calling GetForegroundWindow, getting the thread ID * of that window via GetWindowThreadProcessId, then passing the ID into * GetGUIThreadInfo(). However, that takes three calls and aside from being * a pain, anything could happen in the middle. So passing in NULL gets * you stuff in one call and hence also works right. * * This function returns FALSE if the thread doesn't have a queue or the * thread ID is bogus. \*****************************************************************************/ BOOL _GetGUIThreadInfo( PTHREADINFO pti, PGUITHREADINFO pgui) { PQ pq; /* * Validate threadinfo structure */ if (pgui->cbSize != sizeof(GUITHREADINFO)) { RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "GUITHREADINFO.cbSize %d is wrong", pgui->cbSize); return FALSE; } /* * Is this a valid initialized GUI thread? */ if (pti != NULL) { pq = pti->pq; } else { /* * Use the foreground queue. To get menu state information we must also * figure out the right pti. This matches _GetForegroundWindow() logic. */ if ((pq = gpqForeground) == NULL) { return FALSE; } if (pq->spwndActive && (GETPTI(pq->spwndActive)->pq == pq)) { pti = GETPTI(pq->spwndActive); if (PtiCurrentShared()->rpdesk != pti->rpdesk) { RIPERR0(ERROR_ACCESS_DENIED, RIP_VERBOSE, "Foreground window on different desktop"); return FALSE; } } } UserAssert(pq != NULL); /* * For C2 security, verify that pq and pti are on the current thread's desktop. * We can't directly determine which desktop pq belongs to, but we can at * least ensure that any caret info we return is not from another desktop */ if (pq->caret.spwnd && (GETPTI(pq->caret.spwnd)->rpdesk != PtiCurrentShared()->rpdesk)) { RIPERR0(ERROR_ACCESS_DENIED, RIP_VERBOSE, "Foreground caret on different desktop"); return FALSE; } if (pti && (pti->rpdesk != PtiCurrentShared()->rpdesk)) { RIPERR0(ERROR_ACCESS_DENIED, RIP_VERBOSE, "Foreground thread on different desktop"); return FALSE; } pgui->flags = 0; pgui->hwndMoveSize = NULL; pgui->hwndMenuOwner = NULL; /* * Get menu information from the THREADINFO. */ if (pti != NULL) { if (pti->pmsd && !pti->pmsd->fTrackCancelled && pti->pmsd->spwnd) { pgui->flags |= GUI_INMOVESIZE; pgui->hwndMoveSize = HWq(pti->pmsd->spwnd); } if (pti->pMenuState && pti->pMenuState->pGlobalPopupMenu) { pgui->flags |= GUI_INMENUMODE; if (pti->pMenuState->pGlobalPopupMenu->fHasMenuBar) { if (pti->pMenuState->pGlobalPopupMenu->fIsSysMenu) { pgui->flags |= GUI_SYSTEMMENUMODE; } } else { pgui->flags |= GUI_POPUPMENUMODE; } if (pti->pMenuState->pGlobalPopupMenu->spwndNotify) { pgui->hwndMenuOwner = HWq(pti->pMenuState->pGlobalPopupMenu->spwndNotify); } } if (pti->TIF_flags & TIF_16BIT) { pgui->flags |= GUI_16BITTASK; } } /* * Get the rest of the information from the queue. */ pgui->hwndActive = HW(pq->spwndActive); pgui->hwndFocus = HW(pq->spwndFocus); pgui->hwndCapture = HW(pq->spwndCapture); pgui->hwndCaret = NULL; if (pq->caret.spwnd) { pgui->hwndCaret = HWq(pq->caret.spwnd); if ((GETPTI(pq->caret.spwnd) != PtiCurrentShared()) && (pq->caret.spwnd->pcls->style & CS_OWNDC)) { /* * This is the case where we are being called by a different * thread than created the window, and the window has a * private DC. We have to do extra work to be able to * return the desired information. * These coords are always relative to the client of hwndCaret. */ pgui->rcCaret.left = pq->caret.xOwnDc; pgui->rcCaret.right = pgui->rcCaret.left + pq->caret.cxOwnDc; pgui->rcCaret.top = pq->caret.yOwnDc; pgui->rcCaret.bottom = pgui->rcCaret.top + pq->caret.cyOwnDc; } else { /* * These coords are still in logical coordinates. Ie, these * are the coordinates we draw at in UT_InvertCaret. */ pgui->rcCaret.left = pq->caret.x; pgui->rcCaret.right = pgui->rcCaret.left + pq->caret.cx; pgui->rcCaret.top = pq->caret.y; pgui->rcCaret.bottom = pgui->rcCaret.top + pq->caret.cy; } if (pq->caret.iHideLevel == 0) { pgui->flags |= GUI_CARETBLINKING; } } else if (pti && (pti->ppi->W32PF_Flags & W32PF_CONSOLEHASFOCUS)) { /* * The thread is running in the console window with focus. Pull * out the info from the console pseudo caret. */ pgui->hwndCaret = pti->rpdesk->cciConsole.hwnd; pgui->rcCaret = pti->rpdesk->cciConsole.rc; } else { SetRectEmpty(&pgui->rcCaret); } return TRUE; } /****************************************************************************\ * xxxGetTitleBarInfo * * Gets info about a window's title bar. If the window is bogus or * doesn't have a titlebar, this will fail. \****************************************************************************/ BOOL xxxGetTitleBarInfo( PWND pwnd, PTITLEBARINFO ptbi) { int cxB; CheckLock(pwnd); /* * Validate TITLEBARINFO structure. */ if (ptbi->cbSize != sizeof(TITLEBARINFO)) { RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "TITLEBARINFO.cbSize %d is wrong", ptbi->cbSize); return FALSE; } RtlZeroMemory(&ptbi->rgstate, sizeof(ptbi->rgstate)); ptbi->rgstate[INDEX_TITLEBAR_SELF] |= STATE_SYSTEM_FOCUSABLE; if (TestWF(pwnd, WFBORDERMASK) != LOBYTE(WFCAPTION)) { // No titlebar. ptbi->rgstate[INDEX_TITLEBAR_SELF] |= STATE_SYSTEM_INVISIBLE; return TRUE; } if (!TestWF(pwnd, WFMINIMIZED) && !TestWF(pwnd, WFCPRESENT)) { // Off screen (didn't fit) ptbi->rgstate[INDEX_TITLEBAR_SELF] |= STATE_SYSTEM_OFFSCREEN; SetRectEmpty(&ptbi->rcTitleBar); return TRUE; } /* * Get titlebar rect. */ ptbi->rcTitleBar = pwnd->rcWindow; cxB = GetWindowBorders(pwnd->style, pwnd->ExStyle, TRUE, FALSE); InflateRect(&ptbi->rcTitleBar, -cxB * SYSMET(CXBORDER), -cxB * SYSMET(CYBORDER)); if (TestWF(pwnd, WEFTOOLWINDOW)) { ptbi->rcTitleBar.bottom = ptbi->rcTitleBar.top + SYSMET(CYSMCAPTION); } else { ptbi->rcTitleBar.bottom = ptbi->rcTitleBar.top + SYSMET(CYCAPTION); } /* * Don't include the system menu area! */ if (TestWF(pwnd, WFSYSMENU) && _HasCaptionIcon(pwnd)) { ptbi->rcTitleBar.left += (ptbi->rcTitleBar.bottom - ptbi->rcTitleBar.top - SYSMET(CYBORDER)); } /* * Close button. */ if (!TestWF(pwnd, WFSYSMENU) && TestWF(pwnd, WFWIN40COMPAT)) { ptbi->rgstate[INDEX_TITLEBAR_CLOSEBUTTON] |= STATE_SYSTEM_INVISIBLE; } else { if (!xxxMNCanClose(pwnd)) { ptbi->rgstate[INDEX_TITLEBAR_CLOSEBUTTON] |= STATE_SYSTEM_UNAVAILABLE; } if (TestWF(pwnd, WFCLOSEBUTTONDOWN)) { ptbi->rgstate[INDEX_TITLEBAR_CLOSEBUTTON] |= STATE_SYSTEM_PRESSED; } } /* * Max button. */ if (!TestWF(pwnd, WFSYSMENU) && TestWF(pwnd, WFWIN40COMPAT)) { ptbi->rgstate[INDEX_TITLEBAR_MAXBUTTON] |= STATE_SYSTEM_INVISIBLE; } else { if (!TestWF(pwnd, WFMAXBOX)) { if (!TestWF(pwnd, WFMINBOX)) { ptbi->rgstate[INDEX_TITLEBAR_MAXBUTTON] |= STATE_SYSTEM_INVISIBLE; } else { ptbi->rgstate[INDEX_TITLEBAR_MAXBUTTON] |= STATE_SYSTEM_UNAVAILABLE; } } if (TestWF(pwnd, WFZOOMBUTTONDOWN)) { ptbi->rgstate[INDEX_TITLEBAR_MAXBUTTON] |= STATE_SYSTEM_PRESSED; } } /* * Min button. */ if (!TestWF(pwnd, WFSYSMENU) && TestWF(pwnd, WFWIN40COMPAT)) { ptbi->rgstate[INDEX_TITLEBAR_MINBUTTON] |= STATE_SYSTEM_INVISIBLE; } else { if (!TestWF(pwnd, WFMINBOX)) { if (!TestWF(pwnd, WFMAXBOX)) { ptbi->rgstate[INDEX_TITLEBAR_MINBUTTON] |= STATE_SYSTEM_INVISIBLE; } else { ptbi->rgstate[INDEX_TITLEBAR_MINBUTTON] |= STATE_SYSTEM_UNAVAILABLE; } } if (TestWF(pwnd, WFREDUCEBUTTONDOWN)) { ptbi->rgstate[INDEX_TITLEBAR_MINBUTTON] |= STATE_SYSTEM_PRESSED; } } /* * Help button. */ if (!TestWF(pwnd, WEFCONTEXTHELP) || TestWF(pwnd, WFMINBOX) || TestWF(pwnd, WFMAXBOX)) { ptbi->rgstate[INDEX_TITLEBAR_HELPBUTTON] |= STATE_SYSTEM_INVISIBLE; } else { if (TestWF(pwnd, WFHELPBUTTONDOWN)) { ptbi->rgstate[INDEX_TITLEBAR_HELPBUTTON] |= STATE_SYSTEM_PRESSED; } } // IME button BOGUS! ptbi->rgstate[INDEX_TITLEBAR_IMEBUTTON] = STATE_SYSTEM_INVISIBLE; return TRUE; } /*****************************************************************************\ * xxxGetScrollBarInfo * * Gets state & location information about a scrollbar. * * Note we fill in the minimal amount of useful info. OLEACC is responsible * for extrapolation. I.E., if both the line up and line down buttons are * disabled, the whole scrollbar is, and the thumb is invisible. \*****************************************************************************/ BOOL xxxGetScrollBarInfo( PWND pwnd, LONG idObject, PSCROLLBARINFO psbi) { UINT wDisable; BOOL fVertical; SBCALC SBCalc; PCLS pcls; CheckLock(pwnd); /* * Validate SCROLLBARINFO structure. */ if (psbi->cbSize != sizeof(SCROLLBARINFO)) { RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "SCROLLBARINFO.cbSize 0x%x is wrong", psbi->cbSize); return FALSE; } pcls = pwnd->pcls; if ((idObject == OBJID_CLIENT) && (GETFNID(pwnd) != FNID_SCROLLBAR) && (pcls->atomClassName != gpsi->atomSysClass[ICLS_SCROLLBAR])) { return (BOOL)xxxSendMessage(pwnd, SBM_GETSCROLLBARINFO, 0, (LPARAM)psbi); } RtlZeroMemory(&psbi->rgstate, sizeof(psbi->rgstate)); /* * Calculate where everything is. */ if (idObject == OBJID_CLIENT) { RECT rc; wDisable = ((PSBWND)pwnd)->wDisableFlags; fVertical = ((PSBWND)pwnd)->fVert; GetRect(pwnd, &rc, GRECT_CLIENT | GRECT_CLIENTCOORDS); CalcSBStuff2(&SBCalc, &rc, (PSBDATA)&((PSBWND)pwnd)->SBCalc, ((PSBWND)pwnd)->fVert); } else { /* * Is this window scrollbar here? */ if (idObject == OBJID_VSCROLL) { fVertical = TRUE; if (!TestWF(pwnd, WFVSCROLL)) { // No scrollbar. psbi->rgstate[INDEX_SCROLLBAR_SELF] |= STATE_SYSTEM_INVISIBLE; } else if (!TestWF(pwnd, WFVPRESENT)) { // Window too short to display it. psbi->rgstate[INDEX_SCROLLBAR_SELF] |= STATE_SYSTEM_OFFSCREEN; } } else if (idObject == OBJID_HSCROLL) { fVertical = FALSE; if (! TestWF(pwnd, WFHSCROLL)) { // No scrollbar. psbi->rgstate[INDEX_SCROLLBAR_SELF] |= STATE_SYSTEM_INVISIBLE; } else if (! TestWF(pwnd, WFHPRESENT)) { psbi->rgstate[INDEX_SCROLLBAR_SELF] |= STATE_SYSTEM_OFFSCREEN; } } else { RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "invalid idObject %d", idObject); return FALSE; } if (psbi->rgstate[INDEX_SCROLLBAR_SELF] & STATE_SYSTEM_INVISIBLE) { return TRUE; } wDisable = GetWndSBDisableFlags(pwnd, fVertical); if (!(psbi->rgstate[INDEX_SCROLLBAR_SELF] & STATE_SYSTEM_OFFSCREEN)) { CalcSBStuff(pwnd, &SBCalc, fVertical); } } /* * Setup button states. */ if (wDisable & LTUPFLAG) { psbi->rgstate[INDEX_SCROLLBAR_UP] |= STATE_SYSTEM_UNAVAILABLE; psbi->rgstate[INDEX_SCROLLBAR_UPPAGE] |= STATE_SYSTEM_UNAVAILABLE; } if (wDisable & RTDNFLAG) { psbi->rgstate[INDEX_SCROLLBAR_DOWN] |= STATE_SYSTEM_UNAVAILABLE; psbi->rgstate[INDEX_SCROLLBAR_DOWNPAGE] |= STATE_SYSTEM_UNAVAILABLE; } if ((wDisable & (LTUPFLAG | RTDNFLAG)) == (LTUPFLAG | RTDNFLAG)) { psbi->rgstate[INDEX_SCROLLBAR_SELF] |= STATE_SYSTEM_UNAVAILABLE; } /* * Button pressed? */ if (TestWF(pwnd, WFSCROLLBUTTONDOWN) && ((idObject != OBJID_VSCROLL) || TestWF(pwnd, WFVERTSCROLLTRACK))) { if (TestWF(pwnd, WFLINEUPBUTTONDOWN)) { psbi->rgstate[INDEX_SCROLLBAR_UP] |= STATE_SYSTEM_PRESSED; } if (TestWF(pwnd, WFPAGEUPBUTTONDOWN)) { psbi->rgstate[INDEX_SCROLLBAR_UPPAGE] |= STATE_SYSTEM_PRESSED; } if (TestWF(pwnd, WFPAGEDNBUTTONDOWN)) { psbi->rgstate[INDEX_SCROLLBAR_DOWNPAGE] |= STATE_SYSTEM_PRESSED; } if (TestWF(pwnd, WFLINEDNBUTTONDOWN)) { psbi->rgstate[INDEX_SCROLLBAR_DOWN] |= STATE_SYSTEM_PRESSED; } } /* * Fill in area locations. */ if (!(psbi->rgstate[INDEX_SCROLLBAR_SELF] & STATE_SYSTEM_OFFSCREEN)) { if (fVertical) { psbi->rcScrollBar.left = SBCalc.pxLeft; psbi->rcScrollBar.top = SBCalc.pxTop; psbi->rcScrollBar.right = SBCalc.pxRight; psbi->rcScrollBar.bottom = SBCalc.pxBottom; } else { psbi->rcScrollBar.left = SBCalc.pxTop; psbi->rcScrollBar.top = SBCalc.pxLeft; psbi->rcScrollBar.right = SBCalc.pxBottom; psbi->rcScrollBar.bottom = SBCalc.pxRight; } if (idObject == OBJID_CLIENT) { OffsetRect(&psbi->rcScrollBar, pwnd->rcClient.left, pwnd->rcClient.top); } else { OffsetRect(&psbi->rcScrollBar, pwnd->rcWindow.left, pwnd->rcWindow.top); } psbi->dxyLineButton = (SBCalc.pxUpArrow - SBCalc.pxTop); psbi->xyThumbTop = (SBCalc.pxThumbTop - SBCalc.pxTop); psbi->xyThumbBottom = (SBCalc.pxThumbBottom - SBCalc.pxTop); /* * Is the thumb all the way to the left/top? If so, page up is * not visible. */ if (SBCalc.pxThumbTop == SBCalc.pxUpArrow) { psbi->rgstate[INDEX_SCROLLBAR_UPPAGE] |= STATE_SYSTEM_INVISIBLE; } /* * Is the thumb all the way to the right/down? If so, page down * is not visible. */ if (SBCalc.pxThumbBottom == SBCalc.pxDownArrow) { psbi->rgstate[INDEX_SCROLLBAR_DOWNPAGE] |= STATE_SYSTEM_INVISIBLE; } } return TRUE; } /*****************************************************************************\ * _GetAncestor * * This gets one of: * * The _real_ parent. This does NOT include the owner, unlike GetParent(). * Stops at a top level window unless we start with the desktop. In which * case, we return the desktop. * * The _real_ root, caused by walking up the chain getting the ancestor. * * The _real_ owned root, caused by GetParent()ing up. \*****************************************************************************/ PWND _GetAncestor( PWND pwnd, UINT gaFlags) { PWND pwndParent; /* * If we start with the desktop, the message window or the mother window, * return NULL. */ if (pwnd == PWNDDESKTOP(pwnd) || pwnd == PWNDMESSAGE(pwnd) || pwnd->spwndParent == NULL) { return NULL; } switch (gaFlags) { case GA_PARENT: pwnd = pwnd->spwndParent; break; case GA_ROOT: while ((pwnd->spwndParent != PWNDDESKTOP(pwnd)) && (pwnd->spwndParent != PWNDMESSAGE(pwnd))) { pwnd = pwnd->spwndParent; } break; case GA_ROOTOWNER: while (pwndParent = _GetParent(pwnd)) { pwnd = pwndParent; } break; } return pwnd; } /*****************************************************************************\ * _RealChildWindowFromPoint * * This returns the REAL child window at a point. The problem is that * ChildWindowFromPoint() doesn't deal with HTTRANSPARENT areas of * standard controls. We want to return a child behind a groupbox if it * is in the "clear" area. But we want to return a static field always * even though it too returns HTTRANSPARENT. \*****************************************************************************/ PWND _RealChildWindowFromPoint( PWND pwndParent, POINT pt) { PWND pwndChild; PWND pwndSave; if (pwndParent != PWNDDESKTOP(pwndParent)) { pt.x += pwndParent->rcClient.left; pt.y += pwndParent->rcClient.top; } /* * Is this point even in the parent? */ if (!PtInRect(&pwndParent->rcClient, pt) || (pwndParent->hrgnClip && !GrePtInRegion(pwndParent->hrgnClip, pt.x, pt.y))) { // Nope return NULL; } pwndSave = NULL; /* * Loop through the children. */ for (pwndChild = pwndParent->spwndChild; pwndChild; pwndChild = pwndChild->spwndNext) { if (!TestWF(pwndChild, WFVISIBLE)) continue; /* * Is this point in the child's window? */ if (!PtInRect(&pwndChild->rcWindow, pt) || (pwndChild->hrgnClip && !GrePtInRegion(pwndChild->hrgnClip, pt.x, pt.y))) continue; /* * OK, we are in somebody's window. Is this by chance a group box? */ if (IS_BUTTON(pwndChild)) { if (TestWF(pwndChild, BFTYPEMASK) == LOBYTE(BS_GROUPBOX)) { pwndSave = pwndChild; continue; } } return pwndChild; } /* * Did we save a groupbox which turned out to have nothing behind it * at that point? */ if (pwndSave) { return pwndSave; } else { return pwndParent; } } /*****************************************************************************\ * xxxGetMenuBarInfo * * This succeeds if the menu/menu item exists. * * Parameters: * pwnd window * idObject this can be OBJID_MENU, OBJID_SYSMENU, or OBJID_CLIENT * idItem which thing do we need info on? 0..cItems. 0 indicates * the menu itself, 1 is the first item on the menu... * pmbi Pointer to a MENUBARINFO structure that gets filled in * \*****************************************************************************/ BOOL xxxGetMenuBarInfo( PWND pwnd, long idObject, long idItem, PMENUBARINFO pmbi) { PMENU pMenu; int cBorders; PITEM pItem; PPOPUPMENU ppopup; CheckLock(pwnd); /* * Validate MENUBARINFO structure. */ if (pmbi->cbSize != sizeof(MENUBARINFO)) { RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "MENUBARINFO.cbSize 0x%x is wrong", pmbi->cbSize); return FALSE; } /* * Initialize the fields. */ SetRectEmpty(&pmbi->rcBar); pmbi->hMenu = NULL; pmbi->hwndMenu = NULL; pmbi->fBarFocused = FALSE; pmbi->fFocused = FALSE; /* * Get the menu handle we will deal with. */ if (idObject == OBJID_MENU) { int cBorders; if (TestWF(pwnd, WFCHILD) || !pwnd->spmenu) { return FALSE; } pMenu = pwnd->spmenu; if (!pMenu) { return FALSE; } /* * If we have an item, is it in the valid range? */ if ((idItem < 0) || ((DWORD)idItem > pMenu->cItems)) { return FALSE; } /* * Menu handle. */ pmbi->hMenu = PtoHq(pMenu); /* * Menu rect. */ if (pMenu->cxMenu && pMenu->cyMenu) { if (!idItem) { cBorders = GetWindowBorders(pwnd->style, pwnd->ExStyle, TRUE, FALSE); pmbi->rcBar.left = pwnd->rcWindow.left + cBorders * SYSMET(CXBORDER); pmbi->rcBar.top = pwnd->rcWindow.top + cBorders * SYSMET(CYBORDER); if (TestWF(pwnd, WFCPRESENT)) { pmbi->rcBar.top += (TestWF(pwnd, WEFTOOLWINDOW) ? SYSMET(CYSMCAPTION) : SYSMET(CYCAPTION)); } pmbi->rcBar.right = pmbi->rcBar.left + pMenu->cxMenu; pmbi->rcBar.bottom = pmbi->rcBar.top + pMenu->cyMenu; } else { pItem = pMenu->rgItems + idItem - 1; pmbi->rcBar.left = pwnd->rcWindow.left + pItem->xItem; pmbi->rcBar.top = pwnd->rcWindow.top + pItem->yItem; pmbi->rcBar.right = pmbi->rcBar.left + pItem->cxItem; pmbi->rcBar.bottom = pmbi->rcBar.top + pItem->cyItem; } } /* * Are we currently in app menu bar mode? */ ppopup = GetpGlobalPopupMenu(pwnd); if (ppopup && ppopup->fHasMenuBar && !ppopup->fIsSysMenu && (ppopup->spwndNotify == pwnd)) { pmbi->fBarFocused = TRUE; if (!idItem) { pmbi->fFocused = TRUE; } else if (ppopup->ppopupmenuRoot->posSelectedItem == (UINT)idItem-1) { pmbi->fFocused = TRUE; UserAssert(ppopup->ppopupmenuRoot); pmbi->hwndMenu = HW(ppopup->ppopupmenuRoot->spwndNextPopup); } } } else if (idObject == OBJID_SYSMENU) { if (!TestWF(pwnd, WFSYSMENU)) { return FALSE; } pMenu = xxxGetSysMenu(pwnd, FALSE); if (!pMenu) { return FALSE; } // If we have an item, is it in the valid range? if ((idItem < 0) || ((DWORD)idItem > pMenu->cItems)) return FALSE; pmbi->hMenu = PtoHq(pMenu); /* * Menu rect */ if (_HasCaptionIcon(pwnd)) { // The menu and single item take up the same space cBorders = GetWindowBorders(pwnd->style, pwnd->ExStyle, TRUE, FALSE); pmbi->rcBar.left = pwnd->rcWindow.left + cBorders * SYSMET(CXBORDER); pmbi->rcBar.top = pwnd->rcWindow.top + cBorders * SYSMET(CYBORDER); pmbi->rcBar.right = pmbi->rcBar.left + (TestWF(pwnd, WEFTOOLWINDOW) ? SYSMET(CXSMSIZE) : SYSMET(CXSIZE)); pmbi->rcBar.bottom = pmbi->rcBar.top + (TestWF(pwnd, WEFTOOLWINDOW) ? SYSMET(CYSMSIZE) : SYSMET(CYSIZE)); } /* * Are we currently in system menu bar mode? */ ppopup = GetpGlobalPopupMenu(pwnd); if (ppopup && ppopup->fHasMenuBar && ppopup->fIsSysMenu && (ppopup->spwndNotify == pwnd)) { pmbi->fBarFocused = TRUE; if (!idItem) { pmbi->fFocused = TRUE; } else if (ppopup->ppopupmenuRoot->posSelectedItem == (UINT)idItem - 1) { pmbi->fFocused = TRUE; UserAssert(ppopup->ppopupmenuRoot); pmbi->hwndMenu = HW(ppopup->ppopupmenuRoot->spwndNextPopup); } } } else if (idObject == OBJID_CLIENT) { HMENU hMenu = (HMENU)xxxSendMessage(pwnd, MN_GETHMENU, 0, 0); pMenu = ValidateHmenu(hMenu); if (!pMenu) { return FALSE; } // If we have an item, is it in the valid range? if ((idItem < 0) || ((DWORD)idItem > pMenu->cItems)) { return FALSE; } pmbi->hMenu = hMenu; if (!idItem) { pmbi->rcBar = pwnd->rcClient; } else { pItem = pMenu->rgItems + idItem - 1; pmbi->rcBar.left = pwnd->rcClient.left + pItem->xItem; pmbi->rcBar.top = pwnd->rcClient.top + pItem->yItem; pmbi->rcBar.right = pmbi->rcBar.left + pItem->cxItem; pmbi->rcBar.bottom = pmbi->rcBar.top + pItem->cyItem; } /* * Are we currently in popup mode with us as one of the popups * showing? * * Since malicious code could handle MN_GETHMENU and return a valid * HMENU *w/o* pwnd being a real MENUWND, we need to explicitly * check the fnid. */ if (GETFNID(pwnd) == FNID_MENU && (ppopup = ((PMENUWND)pwnd)->ppopupmenu) && (ppopup->ppopupmenuRoot == GetpGlobalPopupMenu(pwnd))) { pmbi->fBarFocused = TRUE; if (!idItem) { pmbi->fFocused = TRUE; } else if ((UINT)idItem == ppopup->posSelectedItem + 1) { pmbi->fFocused = TRUE; pmbi->hwndMenu = HW(ppopup->spwndNextPopup); } } } else { return FALSE; } return TRUE; } /***************************************************************************\ * xxxGetComboBoxInfo * * This returns combobox information for either a combo or its dropdown * list. \***************************************************************************/ BOOL xxxGetComboBoxInfo( PWND pwnd, PCOMBOBOXINFO pcbi) { PCLS pcls; COMBOBOXINFO cbi = { sizeof cbi, }; BOOL fOtherProcess; BOOL bRetval = FALSE; WORD wWindowType = 0; CheckLock(pwnd); /* * Make sure it is a combobox or a dropdown. */ pcls = pwnd->pcls; if ((GETFNID(pwnd) == FNID_COMBOBOX) || (pcls->atomClassName == gpsi->atomSysClass[ICLS_COMBOBOX])) { wWindowType = FNID_COMBOBOX; } else if ((GETFNID(pwnd) == FNID_COMBOLISTBOX) || (pcls->atomClassName == gpsi->atomSysClass[ICLS_COMBOLISTBOX])) { wWindowType = FNID_COMBOLISTBOX; } else { return (BOOL)xxxSendMessage(pwnd, CB_GETCOMBOBOXINFO, 0, (LPARAM)pcbi); } /* * Validate combo structure */ if (pcbi->cbSize != sizeof(COMBOBOXINFO)) { RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "COMBOBOXINFO.cbSize %d is wrong", pcbi->cbSize); return FALSE; } if (fOtherProcess = (GETPTI(pwnd)->ppi != PpiCurrent())) { KeAttachProcess(PsGetProcessPcb(GETPTI(pwnd)->ppi->Process)); } try { PCBOX ccxPcboxSnap; PWND ccxPwndSnap; HWND ccxHwndSnap; /* * Snap and probe the CBOX structure, since it is client side. */ if (wWindowType == FNID_COMBOBOX) { ccxPcboxSnap = ((PCOMBOWND)pwnd)->pcbox; } else { PLBIV ccxPlbSnap; /* * If this is a listbox, we must snap and probe the LBIV structure * in order to get to the CBOX structure. */ ccxPlbSnap = ((PLBWND)pwnd)->pLBIV; if (!ccxPlbSnap) { goto errorexit; } ProbeForRead(ccxPlbSnap, sizeof(LBIV), DATAALIGN); ccxPcboxSnap = ccxPlbSnap->pcbox; } if (!ccxPcboxSnap) { goto errorexit; } ProbeForRead(ccxPcboxSnap, sizeof(CBOX), DATAALIGN); /* * Get the combo information now. */ /* * Snap and probe the client side pointer to the Combo window. */ ccxPwndSnap = ccxPcboxSnap->spwnd; ProbeForRead(ccxPwndSnap, sizeof(HEAD), DATAALIGN); cbi.hwndCombo = HWCCX(ccxPwndSnap); /* * Snap & probe the client side pointer to the Edit window. * To compare spwndEdit and pwnd, we should compare handles * since spwndEdit is a client-side address and pwnd is a * kernel-mode address, */ ccxPwndSnap = ccxPcboxSnap->spwndEdit; /* * If combobox is not fully initialized and spwndEdit is NULL, * we should fail. */ ProbeForRead(ccxPwndSnap, sizeof(HEAD), DATAALIGN); ccxHwndSnap = HWCCX(ccxPwndSnap); if (ccxHwndSnap == HW(pwnd)) { /* * ComboBox doesn't have Edit control. */ cbi.hwndItem = NULL; } else { cbi.hwndItem = HWCCX(ccxPwndSnap); } /* * Snap and probe the client side pointer to the List window */ ccxPwndSnap = ccxPcboxSnap->spwndList; /* * If combobox is not fully initialized and spwndList is NULL, * we should fail. */ ProbeForRead(ccxPwndSnap, sizeof(HEAD), DATAALIGN); cbi.hwndList = HWCCX(ccxPwndSnap); /* * Snap the rest of the combo information. We don't need to probe * any of these, since there are no more indirections. */ cbi.rcItem = ccxPcboxSnap->editrc; cbi.rcButton = ccxPcboxSnap->buttonrc; /* * Button state. */ cbi.stateButton = 0; if (ccxPcboxSnap->CBoxStyle == CBS_SIMPLE) { cbi.stateButton |= STATE_SYSTEM_INVISIBLE; } if (ccxPcboxSnap->fButtonPressed) { cbi.stateButton |= STATE_SYSTEM_PRESSED; } } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { goto errorexit; } *pcbi = cbi; bRetval = TRUE; errorexit: if (fOtherProcess) { KeDetachProcess(); } return bRetval; } /***************************************************************************\ * xxxGetListBoxInfo * * Currently returns back the # of items per column. There is no way to get * or calculate this info any other way in a multicolumn list. * * For now, no structure is returned. If we ever need one more thing, make one. \***************************************************************************/ DWORD xxxGetListBoxInfo( PWND pwnd) { PCLS pcls; DWORD dwRet = 0; BOOL fOtherProcess; CheckLock(pwnd); /* * Make sure it is a combobox or a dropdown. */ pcls = pwnd->pcls; if ((pcls->atomClassName != gpsi->atomSysClass[ICLS_LISTBOX]) && (GETFNID(pwnd) != FNID_LISTBOX)) { return (DWORD)xxxSendMessage(pwnd, LB_GETLISTBOXINFO, 0, 0); } if (fOtherProcess = (GETPTI(pwnd)->ppi != PpiCurrent())) { KeAttachProcess(PsGetProcessPcb(GETPTI(pwnd)->ppi->Process)); } try { PLBIV ccxPlbSnap; /* * Snap and probe the pointer to the LBIV, since it is client-side. */ ccxPlbSnap = ((PLBWND)pwnd)->pLBIV; if (!ccxPlbSnap) { goto errorexit; } ProbeForRead(ccxPlbSnap, sizeof(LBIV), DATAALIGN); if (ccxPlbSnap->fMultiColumn) { dwRet = ccxPlbSnap->itemsPerColumn; } else { dwRet = ccxPlbSnap->cMac; } } except (W32ExceptionHandler(FALSE, RIP_WARNING)) { dwRet = 0; } errorexit: if (fOtherProcess) { KeDetachProcess(); } return dwRet; }