WindowsXP-SP1/windows/oleacc/oleacc/api.cpp
2020-09-30 16:53:49 +02:00

1141 lines
36 KiB
C++

// 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)
}