// Copyright (c) 1996-1999 Microsoft Corporation // ========================================================================== // File: A P I . C P P // // Copyright 1995-1996 Microsoft Corporation. All Rights Reserved. // Microsoft Confidential. // ========================================================================== // Includes -------------------------------------------------------------- #include "oleacc_p.h" #include "default.h" #include "classmap.h" #include "ctors.h" #include "verdefs.h" #include "Win64Helper.h" #include "w95trace.h" #define CCH_ROLESTATEMAX 128 #ifndef WMOBJ_SAMETHREAD #define WMOBJ_SAMETHREAD 0xFFFFFFFF #endif HRESULT CreateRemoteProxy6432(HWND hwnd, long idObject, REFIID riid, void ** ppvObject); STDAPI ORIGINAL_AccessibleObjectFromWindow(HWND hwnd, DWORD dwId, REFIID riid, void **ppvObject); HRESULT WrapObject( IUnknown * punk, REFIID riid, void ** ppv ); STDAPI AccessibleObjectFromWindow(HWND hwnd, DWORD dwId, REFIID riid, void **ppvObject) { HRESULT hr = ORIGINAL_AccessibleObjectFromWindow(hwnd, dwId, riid, ppvObject); if( hr == S_OK && ppvObject && *ppvObject ) { // Only wrap object if it supports IAccessible. // Some users of AOFW may want something other than an IAccessible - // eg some native OM interface - shouldn't wrap those. IUnknown * punk = (IUnknown *) * ppvObject; IAccessible * pAcc = NULL; hr = punk->QueryInterface( IID_IAccessible, (void **) & pAcc ); if( hr != S_OK || pAcc == NULL ) { // Not an IAccessible - don't wrap, leave as-is... return S_OK; } pAcc->Release(); // It's really an IAccessible - wrap the object... hr = WrapObject( punk, riid, ppvObject ); punk->Release(); } return hr; } // -------------------------------------------------------------------------- // // AccessibleObjectFromWindow() // // This gets an interface pointer from the object specified by dwId inside // of the window. // // ALL object creation takes place through this API, even for internally // used objects. Client-side wrapping takes place here. // // -------------------------------------------------------------------------- STDAPI ORIGINAL_AccessibleObjectFromWindow(HWND hwnd, DWORD dwId, REFIID riid, void **ppvObject) { DWORD_PTR ref; WPARAM wParam = 0; if (IsBadWritePtr(ppvObject,sizeof(void*))) return (E_INVALIDARG); // clear out-param *ppvObject = NULL; ref = 0; // // Window can be NULL (cursor, alert, sound) // Window can also be bad (trying to talk to window that generated event and // client is getting events out of context, and window is gone) // if (IsWindow(hwnd)) { if( GetWindowThreadProcessId( hwnd, NULL) == GetCurrentThreadId() ) { wParam = WMOBJ_SAMETHREAD; } else { wParam = GetCurrentProcessId(); // If, by some chance, this process's pid equals the magic 'samethread' value, // then fall back on the less efficient '0' technique instead. // (see oleacc.doc for more details...) if( wParam == WMOBJ_SAMETHREAD ) wParam = 0; } SendMessageTimeout(hwnd, WM_GETOBJECT, wParam, dwId, SMTO_ABORTIFHUNG, 10000, &ref); } if (FAILED((HRESULT)ref)) return (HRESULT)ref; else if (ref) return ObjectFromLresult(ref, riid, wParam, ppvObject); else { // // Is this the ID of an object we understand and a REFIID we can // handle? BOGUS! For now, we always create the object and QI // on it, only to fail if the riid isn't one we know. // //----------------------------------------------------------------- // [v-jaycl, 5/15/97] Handle custom OBJIDs -- TODO: UNTESTED!!! //----------------------------------------------------------------- // if (fCreateDefObjs && ((LONG)dwId <= 0)) if (fCreateDefObjs ) { return CreateStdAccessibleObject(hwnd, dwId, riid, ppvObject); } return(E_FAIL); } } // -------------------------------------------------------------------------- // // GetRoleTextA() // // Loads the string for the specified role. If the role is bogus, we will // get nothing since the role area is at the end of the string table. We // return the number of chars of the string. // // CWO: 12/3/96, we now return 0 if the string ptr passed in was bogus // // The caller can pass in a NULL buffer, in which case we just return the // # of chars so that he can turn around and allocate something the right // size. // // -------------------------------------------------------------------------- #ifdef UNICODE STDAPI_(UINT) GetRoleTextW(DWORD lRole, LPWSTR lpszRole, UINT cchRoleMax) #else STDAPI_(UINT) GetRoleTextA(DWORD lRole, LPSTR lpszRole, UINT cchRoleMax) #endif { TCHAR szRoleT[CCH_ROLESTATEMAX]; // NULL string is valid, use our temporary string and return count if (!lpszRole) { lpszRole = szRoleT; cchRoleMax = CCH_ROLESTATEMAX; } else { // CWO: Added 12/3/96, Error checking of parameters if (IsBadWritePtr(lpszRole,(sizeof(TCHAR) * cchRoleMax))) { SetLastError(ERROR_INVALID_PARAMETER); return (0); } } if( cchRoleMax == 1 ) { // Special case for 1-len string - we expect it to copy nothing, but // NUL-terminate (for consistency with other cases) - but LoadString // just returns 0 w/o terminating... *lpszRole = '\0'; return 0; } else return LoadString(hinstResDll, STR_ROLEFIRST+lRole, lpszRole, cchRoleMax); } // -------------------------------------------------------------------------- // // GetStateTextA() // // Loads the string for ONE particular state bit. We return the number of // characters in the string. // // CWO: 12/3/96, we now return 0 if the string ptr passed in was bogus // CWO, 12/4/96, Added parameter checking and set last error to // ERROR_INVALID_PARAMETER. // // Like GetRoleTextA(), the caller can pass in a NULL buffer. We will // simply return the character count necessary in that case. // // -------------------------------------------------------------------------- #ifdef UNICODE STDAPI_(UINT) GetStateTextW(DWORD lStateBit, LPWSTR lpszState, UINT cchStateMax) #else STDAPI_(UINT) GetStateTextA(DWORD lStateBit, LPSTR lpszState, UINT cchStateMax) #endif { TCHAR szStateT[CCH_ROLESTATEMAX]; int iStateBit; // // Figure out what state bit this is. // iStateBit = 0; while (lStateBit > 0) { lStateBit >>= 1; iStateBit++; } // NULL string is valid, use our temporary string and return count if (!lpszState) { lpszState = szStateT; cchStateMax = CCH_ROLESTATEMAX; } else { // CWO: Added 12/3/96, Error checking of parameters if (IsBadWritePtr(lpszState,(sizeof(TCHAR) * cchStateMax))) { SetLastError(ERROR_INVALID_PARAMETER); return (0); } } if( cchStateMax == 1 ) { // Special case for 1-len string - we expect it to copy nothing, but // NUL-terminate (for consistency with other cases) - but LoadString // just returns 0 w/o terminating... *lpszState = '\0'; return 0; } else return LoadString(hinstResDll, STR_STATEFIRST+iStateBit, lpszState, cchStateMax); } // -------------------------------------------------------------------------- // // [INTERNAL] // GetRoleStateTextWCommon() // // Calls GetRoleTextA or GetStateTextA (passed in through pfnGetRoleStateANSI // parameter), and converts resulting string to UNICODE. // // Ensures that... // (1) return value equals number of chars copied, excluding terminating NUL. // (2) if buffer is too small, as much of string as possible will be // copied (truncation occurs). // (2) terminating NUL added, even when trucation occurs. // // Eg. buffer of size 4 used when getting text for 'default'... // Buffer will contain 'def\0' (in unicode), // return value of 3, since 3 chars (excl. NUL) copied. // // This ensures comsistency with the 'A' versions of GetXText(). // // (Note that MultiByteToWideChar is not a particularly boundary-case- // friendly API - if the buffer is too short, it doesn't truncate neatly - // it *does not* add a terminating NUL, and returns 0! - so it's effectively // all-or-nothing, with no way of getting partial strings, for piecemeal // conversion, for example. To get around this, we use MBtoWC to translate // into a stack allocated buf of CCH_ROLEMAX, and then copy as necessary // from that to the output string, terminating/truncating neatly.) // // -------------------------------------------------------------------------- typedef UINT (WINAPI FN_GetRoleOrStateTextT)( DWORD lVal, LPTSTR lpszText, UINT cchTextMax ); #ifdef UNICODE STDAPI_(UINT) GetRoleStateTextACommon( FN_GetRoleOrStateTextT * pfnGetRoleStateThisCS, DWORD lVal, LPSTR lpszTextOtherCS, UINT cchTextMax) #else STDAPI_(UINT) GetRoleStateTextWCommon( FN_GetRoleOrStateTextT * pfnGetRoleStateThisCS, DWORD lVal, LPWSTR lpszTextOtherCS, UINT cchTextMax) #endif { TCHAR szTextThisCS[ CCH_ROLESTATEMAX ]; if( pfnGetRoleStateThisCS( lVal, szTextThisCS, CCH_ROLESTATEMAX ) == 0 ) return 0; // Note - cchPropLen includes the terminating nul... #ifdef UNICODE CHAR szTextOtherCS[ CCH_ROLESTATEMAX ]; int cchPropLen = WideCharToMultiByte( CP_ACP, 0, szTextThisCS, -1, szTextOtherCS, CCH_ROLESTATEMAX, NULL, NULL ); #else WCHAR szTextOtherCS[ CCH_ROLESTATEMAX ]; int cchPropLen = MultiByteToWideChar( CP_ACP, 0, szTextThisCS, -1, szTextOtherCS, CCH_ROLESTATEMAX ); #endif // unexpected error... if( cchPropLen == 0 ) return 0; // Ignore terminating NUL in length... cchPropLen--; // lpszRole == NULL means just return length... if( ! lpszTextOtherCS ) return cchPropLen; // (number of TCHARS, not bytes) else { // string requested... #ifdef UNICODE if( IsBadWritePtr( lpszTextOtherCS, ( sizeof(CHAR) * cchTextMax ) ) ) #else if( IsBadWritePtr( lpszTextOtherCS, ( sizeof(WCHAR) * cchTextMax ) ) ) #endif { SetLastError( ERROR_INVALID_PARAMETER ); return 0; } // need space for at least terminating NUL... if( cchTextMax <= 0 ) { SetLastError( ERROR_INSUFFICIENT_BUFFER ); return 0; } // Copy as much string as necessary (cchCopyLen excludes NUL)... // (-1 to reserve terminating NUL) int cchCopyLen = cchTextMax - 1; if( cchCopyLen > cchPropLen ) cchCopyLen = cchPropLen; #ifdef UNICODE // Copy/truncate the ANSI string... // TODO - is strncpy sufficient? Does it slice DBCS correctly? // +1 to add back space for terminating NUL, which lstrncpyA adds for us lstrcpynA( lpszTextOtherCS, szTextOtherCS, cchCopyLen + 1 ); #else // Since we're explicitly copying UNICODE, use of memcpy is safe... memcpy( lpszTextOtherCS, szTextOtherCS, cchCopyLen * sizeof( WCHAR ) ); lpszTextOtherCS[ cchCopyLen ] = '\0'; #endif return cchCopyLen; } } // -------------------------------------------------------------------------- // // GetRoleTextW() // // Like GetRoleTextA() but returns a UNICODE string. // // Calls GetRoleStateTextWCommon, which just calls GetStateTextA and // converts the result to UNICODE. // // -------------------------------------------------------------------------- #ifdef UNICODE STDAPI_(UINT) GetRoleTextA(DWORD lRole, LPSTR lpszRole, UINT cchRoleMax) { return GetRoleStateTextACommon( GetRoleTextW, lRole, lpszRole, cchRoleMax ); } #else STDAPI_(UINT) GetRoleTextW(DWORD lRole, LPWSTR lpszRole, UINT cchRoleMax) { return GetRoleStateTextWCommon( GetRoleTextA, lRole, lpszRole, cchRoleMax ); } #endif // -------------------------------------------------------------------------- // // GetStateTextW() // // Like GetStateTextA() but returns a UNICODE string. // // Calls GetRoleStateTextWCommon, which just calls GetStateTextA and // converts the result to UNICODE. // // // -------------------------------------------------------------------------- #ifdef UNICODE STDAPI_(UINT) GetStateTextA(DWORD lStateBit, LPSTR lpszState, UINT cchStateMax) { return GetRoleStateTextACommon( GetStateTextW, lStateBit, lpszState, cchStateMax ); } #else STDAPI_(UINT) GetStateTextW(DWORD lStateBit, LPWSTR lpszState, UINT cchStateMax) { return GetRoleStateTextWCommon( GetStateTextA, lStateBit, lpszState, cchStateMax ); } #endif // -------------------------------------------------------------------------- // // CreateStdAccessibleObject() // // See Also: CreateStdAccessibleProxy() in default.cpp // // This function takes an HWND and an OBJID. If the OBJID is one of the // system reserved IDs (OBJID_WINDOW, OBJID_CURSOR, OBJID_MENU, etc.) // we create a default object that implements the interface whose IID we // ask for. This is usually IAccessible, but might also be IDispatch, IText, // IEnumVARIANT... // // This function is used by both the AccessibleObjectFromWindow API // and apps that want to do a little of their own thing but let us // handle most of the work. // // -------------------------------------------------------------------------- STDAPI CreateStdAccessibleObject(HWND hwnd, LONG idObject, REFIID riid, void **ppvObject) { HRESULT hr; TCHAR szClassName[128]; BOOL bFound = FALSE; if (IsBadWritePtr(ppvObject,sizeof(void *))) return (E_INVALIDARG); *ppvObject = NULL; if (!hwnd && (idObject != OBJID_CURSOR)) return(E_FAIL); // We make an exception here for OBJID_SYSMENU and OBJID_MENU because they are bit // specific. All the othe objects are bit agnostic except for OBJID_CLIENT and // OBJID_WINDOW which are handled in FindAndCreateWindowClass. if ( idObject == OBJID_SYSMENU || idObject == OBJID_MENU ) { BOOL fIsSameBitness; HRESULT hr = SameBitness(hwnd, &fIsSameBitness); if ( FAILED(hr) ) return E_FAIL; // this should never happen if (!fIsSameBitness) return CreateRemoteProxy6432( hwnd, idObject, riid, ppvObject ); // If target window is of same bitness, fall through and create proxy locally... } switch(idObject) { case OBJID_SYSMENU: hr = CreateSysMenuBarObject(hwnd, idObject, riid, ppvObject); break; case OBJID_MENU: // HACK for IE4/Shell windows if( GetClassName (hwnd, szClassName,ARRAYSIZE(szClassName)) && ( (0 == lstrcmp (szClassName,TEXT("IEFrame"))) || (0 == lstrcmp (szClassName,TEXT("CabinetWClass"))) ) ) { HWND hwndWorker; HWND hwndRebar; HWND hwndSysPager; HWND hwndToolbar; VARIANT varChild; VARIANT varState; hwndWorker = NULL; while (!bFound) { hwndWorker = FindWindowEx (hwnd,hwndWorker,TEXT("Worker"),NULL); if (!hwndWorker) break; hwndRebar = FindWindowEx (hwndWorker,NULL,TEXT("RebarWindow32"),NULL); if (!hwndRebar) continue; hwndSysPager = NULL; while (!bFound) { hwndSysPager = FindWindowEx (hwndRebar,hwndSysPager,TEXT("SysPager"),NULL); if (!hwndSysPager) break; hwndToolbar = FindWindowEx (hwndSysPager,NULL,TEXT("ToolbarWindow32"),NULL); hr = AccessibleObjectFromWindow (hwndToolbar,OBJID_MENU, IID_IAccessible, ppvObject); if (SUCCEEDED(hr)) { varChild.vt=VT_I4; varChild.lVal = CHILDID_SELF; if (SUCCEEDED (((IAccessible*)*ppvObject)->get_accState(varChild,&varState))) { if (!(varState.lVal & STATE_SYSTEM_INVISIBLE)) bFound = TRUE; } } // If we got an IAccessible, but it's not needed here (doesn't // satisfy the above visibility test), then release it. if (!bFound && *ppvObject != NULL) ((IAccessible*)*ppvObject)->Release (); } } } // end if we are talking to an IE4/IE4 Shell window if (!bFound) hr = CreateMenuBarObject(hwnd, idObject, riid, ppvObject); break; case OBJID_CLIENT: hr = CreateClientObject(hwnd, idObject, riid, ppvObject); break; case OBJID_WINDOW: hr = CreateWindowObject(hwnd, idObject, riid, ppvObject); break; case OBJID_HSCROLL: case OBJID_VSCROLL: hr = CreateScrollBarObject(hwnd, idObject, riid, ppvObject); break; case OBJID_SIZEGRIP: hr = CreateSizeGripObject(hwnd, idObject, riid, ppvObject); break; case OBJID_TITLEBAR: hr = CreateTitleBarObject(hwnd, idObject, riid, ppvObject); break; case OBJID_CARET: hr = CreateCaretObject(hwnd, idObject, riid, ppvObject); break; case OBJID_CURSOR: hr = CreateCursorObject(hwnd, idObject, riid, ppvObject); break; default: //----------------------------------------------------------------- // [v-jaycl, 5/15/97] Handle custom OBJIDs -- // Second parameter to FindWindowClass() is irrelevant since // we're looking for a reg.handler, not an intrinsic window or client //----------------------------------------------------------------- return FindAndCreateWindowClass( hwnd, TRUE, CLASS_NONE, idObject, 0, riid, ppvObject ); } return(hr); } // -------------------------------------------------------------------------- // // CreateStdAccessibleProxyA() // // See Also: CreateStdAccessibleObject() // // Similar to CreateStdAccessibleObject, but this version allows you to // give a classname to use to specify the type of proxy you want - // eg. "Button" for a button proxy, and so on. // // This function takes a class name and an OBJID. If the OBJID is one of the // system reserved IDs (OBJID_WINDOW, OBJID_CURSOR, OBJID_MENU, etc.) // we create a default object that implements the interface whose IID we // ask for. This is usually IAccessible, but might also be IDispatch, IText, // IEnumVARIANT... // // // -------------------------------------------------------------------------- #ifdef UNICODE STDAPI CreateStdAccessibleProxyW( HWND hWnd, LPCWSTR pClassName, // UNICODE, not TCHAR LONG idObject, REFIID riid, void ** ppvObject ) #else STDAPI CreateStdAccessibleProxyA( HWND hWnd, LPCSTR pClassName, // ANSI, not TCHAR LONG idObject, REFIID riid, void ** ppvObject ) #endif { if( IsBadReadPtr( pClassName, sizeof(TCHAR) ) || IsBadWritePtr( ppvObject, sizeof(void*) ) ) { return E_INVALIDARG; } int RegHandlerIndex; CLASS_ENUM ceClass; // Try and find a native proxy or registered handler for this window/client... if( ! LookupWindowClassName( pClassName, FALSE, & ceClass, & RegHandlerIndex ) ) { // Nope - fail! ppvObject = NULL; return E_FAIL; } // At this point, ceClass != CLASS_NONE means we've found a class above, // ceClass == CLASS_NONE means it's a registered handler class, using index // RegHandlerIndex... // Now create the object... if( ceClass != CLASS_NONE ) { return g_ClassInfo[ ceClass ].lpfnCreate( hWnd, 0, riid, ppvObject ); } else { return CreateRegisteredHandler( hWnd, idObject, RegHandlerIndex, riid, ppvObject ); } } // -------------------------------------------------------------------------- // // CreateStdAccessibleProxyW/A() // // UNICODE/ANSI wrappers for CreateStdAccessibleProxy above // // -------------------------------------------------------------------------- #ifdef UNICODE STDAPI CreateStdAccessibleProxyA( HWND hWnd, LPCSTR pClassName, // ANSI, not TCHAR LONG idObject, REFIID riid, void ** ppvObject ) { if( IsBadReadPtr( pClassName, sizeof(CHAR) ) ) return E_INVALIDARG; WCHAR szClassNameW[ 256 ]; if( ! MultiByteToWideChar( CP_ACP, 0, pClassName, -1, szClassNameW, ARRAYSIZE( szClassNameW ) ) ) return E_FAIL; return CreateStdAccessibleProxyW( hWnd, szClassNameW, idObject, riid, ppvObject ); } #else STDAPI CreateStdAccessibleProxyW( HWND hWnd, LPCWSTR pClassName, // UNICODE, not TCHAR LONG idObject, REFIID riid, void ** ppvObject ) { if( IsBadReadPtr( pClassName, sizeof(WCHAR) ) ) return E_INVALIDARG; CHAR szClassNameA[ 256 ]; if( ! WideCharToMultiByte( CP_ACP, 0, pClassName, -1, szClassNameA, ARRAYSIZE( szClassNameA ), NULL, NULL ) ) return E_FAIL; return CreateStdAccessibleProxyA( hWnd, szClassNameA, idObject, riid, ppvObject ); } #endif // -------------------------------------------------------------------------- // // AccessibleObjectFromEvent() // // This takes care of getting the container and checking if the child // is an object in its own right. Standard stuff that everyone would have // to do. Basically a wrapper that uses AccessibleObjectFromWindow and // then get_accChild(). // // -------------------------------------------------------------------------- STDAPI AccessibleObjectFromEvent(HWND hwnd, DWORD dwId, DWORD dwChildId, IAccessible** ppacc, VARIANT* pvarChild) { HRESULT hr; IAccessible* pacc; IDispatch* pdispChild; VARIANT varT; //CWO, 12/4/96, Added check for valid window handle //CWO, 12/6/96, Allow a NULL window handle if (IsBadWritePtr(ppacc,sizeof(void*)) || IsBadWritePtr (pvarChild,sizeof(VARIANT)) || (!IsWindow(hwnd) && hwnd != NULL)) return (E_INVALIDARG); InitPv(ppacc); VariantInit(pvarChild); // // Try to get the object for the container // pacc = NULL; hr = AccessibleObjectFromWindow(hwnd, dwId, IID_IAccessible, (void**)&pacc); if (!SUCCEEDED(hr)) return(hr); if (!pacc) return(E_FAIL); // // Now, is the child an object? // VariantInit(&varT); varT.vt = VT_I4; varT.lVal = dwChildId; pdispChild = NULL; hr = pacc->get_accChild(varT, &pdispChild); if (SUCCEEDED(hr) && pdispChild) { // // Yes, it is. // // Release the parent. pacc->Release(); // Convert the child to an IAccessible* pacc = NULL; hr = pdispChild->QueryInterface(IID_IAccessible, (void**)&pacc); // Release the IDispatch* form of the child pdispChild->Release(); // Did it succeed? if (!SUCCEEDED(hr)) return(hr); if (!pacc) return(E_FAIL); // Yes. Clear out the lVal (0 is 'container' child id) varT.lVal = 0; } // // We have something. Return it. // *ppacc = pacc; VariantCopy(pvarChild, &varT); return(S_OK); } // -------------------------------------------------------------------------- // // AccessibleObjectFromPoint() // // Walks down the OLEACC hierarchy to get the object/element that is // at the current screen point. Starts with AccessibleObjectFromWindow // using WindowFromPoint() and then uses acc_HitTest to get to the // innermost object. // // -------------------------------------------------------------------------- STDAPI AccessibleObjectFromPoint(POINT ptScreen, IAccessible **ppAcc, VARIANT * pvarChild) { HRESULT hr; IAccessible * pAcc; VARIANT varChild; HWND hwndPoint; if (IsBadWritePtr(ppAcc,sizeof(void*)) || IsBadWritePtr (pvarChild,sizeof(VARIANT))) return (E_INVALIDARG); *ppAcc = NULL; pvarChild->vt = VT_EMPTY; // // Is this a valid screen point? // hwndPoint = WindowFromPoint(ptScreen); if (!hwndPoint) return(E_INVALIDARG); // // Get the top level window of this one and work our way down. We have // to do this because applications may implement Acc at an intermediate // level above the child window. Our default implementation will let us // get there and mesh. // hwndPoint = MyGetAncestor(hwndPoint, GA_ROOT); if (!hwndPoint) return(E_FAIL); hr = AccessibleObjectFromWindow(hwndPoint, OBJID_WINDOW, IID_IAccessible, (void **)&pAcc); // // OK, now we are cooking. // while (SUCCEEDED(hr)) { // // Get the child at this point in the container object. // VariantInit(&varChild); hr = pAcc->accHitTest(ptScreen.x, ptScreen.y, &varChild); if (!SUCCEEDED(hr)) { // Uh oh, error. This should never happen--something moved. pAcc->Release(); return(hr); } // // Did we get back a VT_DISPATCH? If so, there is a child object. // Otherwise, we have our thing (container object or child element // too small for object). // if (varChild.vt == VT_DISPATCH) { pAcc->Release(); if (! varChild.pdispVal) return(E_POINTER); pAcc = NULL; hr = varChild.pdispVal->QueryInterface(IID_IAccessible, (void **)&pAcc); varChild.pdispVal->Release(); } else if ((varChild.vt == VT_I4) || (varChild.vt == VT_EMPTY)) { // // accHitTest should ALWAYS return an object if the child is // an object. Unlike with accNavigate, where you usually // have to pick by-index or by_object only and intermixed means // get_accChild is needed. // *ppAcc = pAcc; VariantCopy(pvarChild, &varChild); return(S_OK); } else { // // Failure. Shouldn't have been returned. // VariantClear(&varChild); pAcc->Release(); hr = E_INVALIDARG; } } return(hr); } // -------------------------------------------------------------------------- // // WindowFromAccessibleObject() // // This walks UP the ancestor chain until we find something who responds to // IOleWindow(). Then we get the HWND from it. // // Returns E_INVALIDARG if object cannot be read or if HWND pointer is invalid // (CWO, 12/4/96) // -------------------------------------------------------------------------- STDAPI WindowFromAccessibleObject(IAccessible* pacc, HWND* phwnd) { IAccessible* paccT; IOleWindow* polewnd; IDispatch* pdispParent; HRESULT hr; //CWO: 12/4/96, Added check for NULL object //CWO: 12/13/96, Removed NULL check, replaced with IsBadReadPtr check (#10342) if (phwnd == NULL || IsBadWritePtr(phwnd,sizeof(HWND*)) || pacc == NULL || IsBadReadPtr(pacc, sizeof(void*))) return (E_INVALIDARG); *phwnd = NULL; paccT = pacc; hr = S_OK; while (paccT && SUCCEEDED(hr)) { polewnd = NULL; hr = paccT->QueryInterface(IID_IOleWindow, (void**)&polewnd); if (SUCCEEDED(hr) && polewnd) { hr = polewnd->GetWindow(phwnd); polewnd->Release(); // // Release an interface we obtained on our own, but not the one // passed in. // if (paccT != pacc) { paccT->Release(); paccT = NULL; } break; } // // Get our parent. // pdispParent = NULL; hr = paccT->get_accParent(&pdispParent); // // Release an interface we obtained on our own, but not the one // passed in. // if (paccT != pacc) { paccT->Release(); } paccT = NULL; if (SUCCEEDED(hr) && pdispParent) { hr = pdispParent->QueryInterface(IID_IAccessible, (void**)&paccT); pdispParent->Release(); } } return(hr); } // -------------------------------------------------------------------------- // // AccessibleChildren() // // This function fills in an array of VARIANTs that refer to all the chilren // of an IAccessible object. This should simplify many of the test // applications lives, as well as lots of other people as well. // // Parameters: // paccContainer This is a pointer to the IAccessible interface of the // container object - the one you want to get the // children of. // iChildStart The INDEX (NOT ID!!!) of the first child to get. // Usually the caller will use 0 to get all the children. // If the caller wants something else, they need to remember // that this expects an INDEX (0 to n-1) and not an ID // (1 to n, or some private ID). // cChildren Count of how many children to get. Usually the // caller will first call IAccessible::get_accChildCount // and use that value. // rgvarChildren The array of VARIANTs that will be filled in by the // function. Each VARIANT can be used to get info // about the child it references. The caller should be // careful if they didn't use 0 for iChildStart, because // then the index of the array and the index of the // children won't match up. // Each VARIANT will be of type either VT_I4 or // VT_DISPATCH. For a VT_I4, the caller will just ask the // container for info about the child, using the // VARIANT.lVal as a child id. For a VT_DISPATCH, the // caller should do a QueryInterface on VARIANT.pdispVal // to get an IAccessible interface and then talk to the // child object directly. // *** The caller must also do a Release on any IDispatch // Interfaces, and free this array of variants when done!! *** // pcObtained This value will be filled in by the function and // will indicate the number of VARIANTs in the array // that were successfully filled in. May not be NULL. // // Returns: // S_OK if the number of elements supplied is cChildren; S_FALSE if // it succeeded but fewer than the number of children requested was // returned, or if you try to skip more children than exist. // Error return values are E_INAVLIDARG if rgvarChildren is not as // big as cChildren, or if pcObtained is not a valid pointer. // // -------------------------------------------------------------------------- STDAPI AccessibleChildren (IAccessible* paccContainer, LONG iChildStart, LONG cChildren, VARIANT* rgvarChildren,LONG* pcObtained) { HRESULT hr; IEnumVARIANT* penum; IDispatch* pdisp; LONG ArrayIndex; LONG ChildIndex; LONG celtTotal; Assert(paccContainer); if ( IsBadWritePtr(paccContainer,sizeof(void*)) || IsBadWritePtr(rgvarChildren,sizeof(VARIANT)*cChildren) || IsBadWritePtr(pcObtained,sizeof(LONG))) { return E_INVALIDARG; } // start by initializing the VARIANT array for (ArrayIndex = 0; ArrayIndex < cChildren; ArrayIndex++) VariantInit (&(rgvarChildren[ArrayIndex])); // // Try querying for IEnumVARIANT. If that fails, use index+1 based IDs. // penum = NULL; hr = paccContainer->QueryInterface(IID_IEnumVARIANT, (void**)&penum); if (penum) { penum->Reset(); // SMD 4/27/98 - fix 689 regression // if we are doing the case of getting everything (skipping 0) // then don't bother calling it. Fixes a problem in CClient::Skip // where it returned S_FALSE when skipping 0 items. Since others // may accidentally do this too, we'll "fix" it here to localize // the change if (iChildStart > 0) { hr = penum->Skip(iChildStart); // hr should still be set to S_OK from QI call } if (hr == S_OK) hr = penum->Next(cChildren,rgvarChildren,(ULONG*)pcObtained); else *pcObtained = 0; penum->Release(); if (FAILED(hr)) return (hr); } else { // okay,so it doesn't support IEnumVARIANT. We'll just have to // create an array of variants with sequential Child Id's. celtTotal = 0; paccContainer->get_accChildCount((LONG*)&celtTotal); if (iChildStart < celtTotal) *pcObtained = celtTotal - iChildStart; else *pcObtained = 0; ChildIndex = iChildStart+1; for (ArrayIndex = 0;ArrayIndex < *pcObtained;ArrayIndex++) { rgvarChildren[ArrayIndex].vt = VT_I4; rgvarChildren[ArrayIndex].lVal = ChildIndex; ChildIndex++; } } // end else - doesn't support IEnumVARIANT // Now that we've filled in the array of variants, let's check each // item to see if it is a real object or not. for (ArrayIndex = 0;ArrayIndex < *pcObtained;ArrayIndex++) { // check to see if this child is an IAccessible object or not if (rgvarChildren[ArrayIndex].vt == VT_I4) { pdisp = NULL; hr = paccContainer->get_accChild(rgvarChildren[ArrayIndex], &pdisp); if (SUCCEEDED(hr) && pdisp) { rgvarChildren[ArrayIndex].vt = VT_DISPATCH; rgvarChildren[ArrayIndex].pdispVal = pdisp; } // end if child seems to be an object (has an IDispatch) } // end if child is VT_I4 } // end for loop through if (*pcObtained == cChildren) return(S_OK); else return (S_FALSE); } WORD g_VerInfo [ 4 ]= { BUILD_VERSION_INT }; STDAPI_(VOID) GetOleaccVersionInfo(DWORD* pVer, DWORD* pBuild) { if ( IsBadWritePtr(pVer,sizeof(DWORD)) || IsBadWritePtr(pBuild,sizeof(DWORD))) { return; } *pVer = MAKELONG( g_VerInfo[1], g_VerInfo[0] ); // MAKELONG(lo, hi) *pBuild = MAKELONG( g_VerInfo[3], g_VerInfo[2] ); // MAKELONG(lo, hi) }