1141 lines
36 KiB
C++
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)
|
|
}
|