486 lines
16 KiB
C++
486 lines
16 KiB
C++
|
// Copyright (c) 2000-2000 Microsoft Corporation
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// wrap_annotation
|
||
|
//
|
||
|
// Wrapper class to implement annotation for IAccessibles
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
|
||
|
#include "oleacc_p.h"
|
||
|
|
||
|
#include "PropMgr_Client.h"
|
||
|
#include "PropMgr_Util.h"
|
||
|
|
||
|
#include "wrap_base.h"
|
||
|
|
||
|
|
||
|
|
||
|
class AccWrap_AddIAccProp: public AccWrap_Base
|
||
|
{
|
||
|
HWND m_FakeIAccPropHwnd;
|
||
|
BOOL m_fCheckedForFakeIAccProp;
|
||
|
|
||
|
|
||
|
BOOL CanFakeIAccIdentity( HWND * phwnd )
|
||
|
{
|
||
|
// Can we fale IAccIdentity for this object?
|
||
|
// Yes, if:
|
||
|
// * It supports IAccessible
|
||
|
// * It has a parent
|
||
|
// * The parent supports IAccIdentity
|
||
|
// * Parent's identity is HWND-base,d and is OBJID_WINDOW.
|
||
|
// We can 99.9% assume that the child is an OBJID_CLIENT object.
|
||
|
//
|
||
|
// (TODO - also check that its HWND matches that of its parent?)
|
||
|
//
|
||
|
// This is useful where native IAccessible is implementing IAccessible
|
||
|
// for a client (ie. where it's not something complicated like Trident
|
||
|
// with multiple levels). Since the native IAccessible won't implement
|
||
|
// IAccIdentity (since that was spec'd only recently), annotation
|
||
|
// won't work for it.
|
||
|
// However, if we can determine that it is a simple IAccessible - and
|
||
|
// we do so by checking that it's parent is a OBJID_WINDOW object -
|
||
|
// we can supply the identity for it.
|
||
|
// This works because noone has ever wanted to supply an IAccessible
|
||
|
// for anything other than the OBJID_CLIENT child of an OBJID_WINDOW.
|
||
|
|
||
|
|
||
|
// Note that we use the AccWrap_Base::get_accParent here instead of
|
||
|
// using get_accParent - if we used the latter, we'd end up trying
|
||
|
// to use annotation (to see if the parent was annotated), but we're
|
||
|
// in the middle ot QI'ing for an annotation interface, so that would
|
||
|
// be freaky. So we explicitly call the base class to short-circuit-out
|
||
|
// the annotated version of get_accParent.
|
||
|
IDispatch * pdispParent = NULL;
|
||
|
HRESULT hr = AccWrap_Base::get_accParent( & pdispParent );
|
||
|
if( hr != S_OK || pdispParent == NULL )
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// This is very important - we want to talk to the real parent IAccessible -
|
||
|
// not its wrapper, so that when we QI it for IAccIdentity, we will know
|
||
|
// for sure if the parent actually supports it or not. (If we didn't do this,
|
||
|
// the parent's wrapper would try to implement IAccIdentity for it, by
|
||
|
// calling back into this method on the parent, which would in turn do the same
|
||
|
// for the parent's parent, and so on up the tree. Eventually, that would all
|
||
|
// correctly return 'FALSE', but it's a particularly expensive way to calculate
|
||
|
// FALSE, especially where deep trees are used, eg. in a trident doc.)
|
||
|
//
|
||
|
// We get the real parent by QI'ing for IServiceProvider, and then QS'ing for
|
||
|
// IIS_AccWrapBase_GetIUnknown. (These are implemented in wrap_base.cpp).
|
||
|
|
||
|
IServiceProvider * psvc = NULL;
|
||
|
hr = pdispParent->QueryInterface( IID_IServiceProvider, (void **) & psvc );
|
||
|
pdispParent->Release();
|
||
|
if( hr != S_OK || psvc == NULL )
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// QS allows us to both get the real parent, and QI it (for IAccIdentity) in one go...
|
||
|
IAccIdentity * pParentID = NULL;
|
||
|
hr = psvc->QueryService( IIS_AccWrapBase_GetIUnknown, IID_IAccIdentity, (void **) & pParentID );
|
||
|
psvc->Release();
|
||
|
if( hr != S_OK || pParentID == NULL )
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Got the parent's identity interface - now get its identity string...
|
||
|
BYTE * pIDString = NULL;
|
||
|
DWORD dwStringLen = 0;
|
||
|
hr = pParentID->GetIdentityString( CHILDID_SELF, & pIDString, & dwStringLen );
|
||
|
pParentID->Release();
|
||
|
if( hr != S_OK || pIDString == NULL )
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Finally check if it is a OBJID_WINDOW thing...
|
||
|
HWND hwnd;
|
||
|
DWORD idObject;
|
||
|
DWORD idChild;
|
||
|
BOOL fGotIt = DecodeHwndKey( pIDString, dwStringLen, & hwnd, & idObject, & idChild );
|
||
|
CoTaskMemFree( pIDString );
|
||
|
|
||
|
if( ! fGotIt || hwnd == NULL || idObject != OBJID_WINDOW || idChild != CHILDID_SELF )
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
* phwnd = hwnd;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
|
||
|
AccWrap_AddIAccProp( IUnknown * punk )
|
||
|
: AccWrap_Base( punk ),
|
||
|
m_FakeIAccPropHwnd( NULL ),
|
||
|
m_fCheckedForFakeIAccProp( FALSE )
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
~AccWrap_AddIAccProp()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void ** ppv )
|
||
|
{
|
||
|
HRESULT hr = AccWrap_Base::QueryInterface( riid, ppv );
|
||
|
|
||
|
if( hr == E_NOINTERFACE && riid == IID_IAccIdentity )
|
||
|
{
|
||
|
if( ! m_fCheckedForFakeIAccProp )
|
||
|
{
|
||
|
// Check if we can fake an IAccIdentity, if we haven't
|
||
|
// checked already...
|
||
|
HWND hwnd;
|
||
|
m_fCheckedForFakeIAccProp = TRUE;
|
||
|
if( CanFakeIAccIdentity( & hwnd ) )
|
||
|
{
|
||
|
m_FakeIAccPropHwnd = hwnd;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( m_FakeIAccPropHwnd )
|
||
|
{
|
||
|
// Yes, we can fake it...
|
||
|
*ppv = (IAccIdentity *) this;
|
||
|
AddRef();
|
||
|
hr = S_OK;
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE GetIdentityString (
|
||
|
DWORD dwIDChild,
|
||
|
BYTE ** ppIDString,
|
||
|
DWORD * pdwIDStringLen
|
||
|
)
|
||
|
{
|
||
|
*ppIDString = NULL;
|
||
|
*pdwIDStringLen = 0;
|
||
|
|
||
|
if( ! m_fCheckedForFakeIAccProp )
|
||
|
{
|
||
|
// Object supports this interface natively - call through...
|
||
|
return AccWrap_Base::GetIdentityString( dwIDChild, ppIDString, pdwIDStringLen );
|
||
|
}
|
||
|
|
||
|
if( ! m_FakeIAccPropHwnd )
|
||
|
{
|
||
|
// This shouldn't happen - if we've checked for the interface, but didn't
|
||
|
// get a valid HWND to use, then we'd have returned E_NOINTERFACE in QI, so
|
||
|
// the caller shouldn't have got an interface to call us on.
|
||
|
Assert( FALSE );
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
// Ok, we need to fake a key for an object that doesn't itself support IAccIdentity,
|
||
|
// but which is fakeable (see CanFakeIAccIdentity for more details
|
||
|
// on what that means.) Basically assume it's OBJID_CLIENT, and construct
|
||
|
// a win32/hwnd key.
|
||
|
BYTE * pKeyData = (BYTE *) CoTaskMemAlloc( HWNDKEYSIZE );
|
||
|
if( ! pKeyData )
|
||
|
{
|
||
|
return E_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
MakeHwndKey( pKeyData, m_FakeIAccPropHwnd, OBJID_CLIENT, dwIDChild );
|
||
|
|
||
|
*ppIDString = pKeyData;
|
||
|
*pdwIDStringLen = HWNDKEYSIZE;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// Note on member m_pAccPropID:
|
||
|
//
|
||
|
// This is a pointer to an interface on *this* object - so we don't need
|
||
|
// to AddRef it. It is actually important that we don't AddRef() it,
|
||
|
// otherwise we'd be keeping a ref to ourself - so we'd never get
|
||
|
// destroyed (circular ref problem).
|
||
|
// We get this interface pointer from QI on this object - the base
|
||
|
// class AccWrap_Base will only return a pointer if the object we're
|
||
|
// wrapping supports it too.
|
||
|
// As soon as we get the ptr from QI, we Release() to undo the effect
|
||
|
// of the AddRef in QI. It is still valid to use this pointer, however,
|
||
|
// since it is a pointer to ourself.
|
||
|
// This weirdness applies only because this is a pointer back to
|
||
|
// ourself. If the pointer pointed to any other object, we'd have to
|
||
|
// do the usual Release-only-when-done-with-ptr-eg.-in-the-dtor stuff.
|
||
|
//
|
||
|
// (Why not just cast instead of using QI? Well, the base class only
|
||
|
// returns a pointer via QI if the object we're wrapping also supports
|
||
|
// this inteface. If we did a cast, we'd always succeed, even if the
|
||
|
// object we're wrapping doesn't support this interface.)
|
||
|
//
|
||
|
|
||
|
class AccWrap_Annotate: public AccWrap_AddIAccProp
|
||
|
{
|
||
|
BOOL m_fInited;
|
||
|
IAccIdentity * m_pAccPropID; // See note above
|
||
|
|
||
|
// This function calls our ctor...
|
||
|
friend HRESULT WrapObject( IUnknown * punk, REFIID riid, void ** ppv );
|
||
|
|
||
|
|
||
|
AccWrap_Annotate( IUnknown * punk )
|
||
|
: AccWrap_AddIAccProp( punk ),
|
||
|
m_fInited( FALSE ),
|
||
|
m_pAccPropID( NULL )
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
~AccWrap_Annotate()
|
||
|
{
|
||
|
// We *don't* release m_pAccPropID, since it points to this object.
|
||
|
// See note at top of class for more details.
|
||
|
}
|
||
|
|
||
|
void Init()
|
||
|
{
|
||
|
if( ! m_fInited )
|
||
|
{
|
||
|
// Get the identity interface for this IAccessible - if it has one...
|
||
|
IAccIdentity * pAccPropID = NULL;
|
||
|
HRESULT hr = this->QueryInterface( IID_IAccIdentity, (void **) & pAccPropID );
|
||
|
if( hr == S_OK && pAccPropID )
|
||
|
{
|
||
|
m_pAccPropID = pAccPropID;
|
||
|
|
||
|
// We *must* release m_pAccPropID now, even though we're going to use it later.
|
||
|
// This is only because it points to this object.
|
||
|
// See note at top of class for more details.
|
||
|
m_pAccPropID->Release();
|
||
|
}
|
||
|
}
|
||
|
m_fInited = TRUE;
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
|
||
|
// Factory method - the AccWrap_Base calls this when it needs to wrap outgoing
|
||
|
// params to the IAccessible methods.
|
||
|
IUnknown * WrapFactory( IUnknown * punk )
|
||
|
{
|
||
|
return static_cast<IAccessible *>( new AccWrap_Annotate( punk ) );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// Forwarding methods...
|
||
|
|
||
|
|
||
|
BOOL GetGenericProp( VARIANT varChild, PROPINDEX idxProp, short vt, VARIANT * pvar )
|
||
|
{
|
||
|
// We do the 'check if annotation is active' check before calculating the key (in Init()).
|
||
|
// This saves calc'ing the key - which can be slightly expensive for out-of-proc objects
|
||
|
// (cross proc calls involved - at least QI.) - if nothing is using annotation anyhow.
|
||
|
if( varChild.vt != VT_I4 || ! PropMgrClient_CheckAlive() )
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
Init();
|
||
|
|
||
|
if( ! m_pAccPropID )
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
BYTE * pIDString = NULL;
|
||
|
DWORD dwIDStringLen = 0;
|
||
|
HRESULT hr = m_pAccPropID->GetIdentityString( varChild.lVal, & pIDString, & dwIDStringLen );
|
||
|
if( hr != S_OK || pIDString == NULL )
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
BOOL fLookup = PropMgrClient_LookupProp( pIDString, dwIDStringLen, idxProp, pvar );
|
||
|
|
||
|
CoTaskMemFree( pIDString );
|
||
|
|
||
|
if( ! fLookup )
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// If vt is not VT_EMPTY, then check the type is what we expect...
|
||
|
if( vt != VT_EMPTY && pvar->vt != vt )
|
||
|
{
|
||
|
VariantClear( pvar );
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
#define FORWARD_BSTR( name, idProp ) /**/\
|
||
|
HRESULT STDMETHODCALLTYPE get_acc ## name ( VARIANT varChild, BSTR * pbstr )\
|
||
|
{\
|
||
|
VARIANT var;\
|
||
|
if( GetGenericProp( varChild, idProp, VT_BSTR, & var ) )\
|
||
|
{\
|
||
|
*pbstr = var.bstrVal;\
|
||
|
return S_OK;\
|
||
|
}\
|
||
|
return AccWrap_Base::get_acc ## name( varChild, pbstr );\
|
||
|
}
|
||
|
|
||
|
#define FORWARD_VTI4( name, idProp ) /**/\
|
||
|
HRESULT STDMETHODCALLTYPE get_acc ## name ( VARIANT varChild, VARIANT * pvar )\
|
||
|
{\
|
||
|
if( GetGenericProp( varChild, idProp, VT_I4, pvar ) )\
|
||
|
{\
|
||
|
return S_OK;\
|
||
|
}\
|
||
|
return AccWrap_Base::get_acc ## name( varChild, pvar );\
|
||
|
}
|
||
|
|
||
|
|
||
|
FORWARD_BSTR( Name, PROPINDEX_NAME )
|
||
|
FORWARD_BSTR( Value, PROPINDEX_VALUE )
|
||
|
FORWARD_BSTR( Description, PROPINDEX_DESCRIPTION )
|
||
|
FORWARD_BSTR( Help, PROPINDEX_HELP )
|
||
|
FORWARD_BSTR( KeyboardShortcut, PROPINDEX_KEYBOARDSHORTCUT )
|
||
|
FORWARD_BSTR( DefaultAction, PROPINDEX_DEFAULTACTION )
|
||
|
|
||
|
FORWARD_VTI4( Role, PROPINDEX_ROLE )
|
||
|
FORWARD_VTI4( State, PROPINDEX_STATE )
|
||
|
|
||
|
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE get_accParent( IDispatch ** ppdispParent )
|
||
|
{
|
||
|
VARIANT varChild;
|
||
|
varChild.vt = VT_I4;
|
||
|
varChild.lVal = CHILDID_SELF;
|
||
|
VARIANT var;
|
||
|
if( GetGenericProp( varChild, PROPINDEX_PARENT, VT_DISPATCH, & var ) )
|
||
|
{
|
||
|
*ppdispParent = var.pdispVal;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
return AccWrap_Base::get_accParent( ppdispParent );
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE get_accFocus( VARIANT * pvar )
|
||
|
{
|
||
|
VARIANT varChild;
|
||
|
varChild.vt = VT_I4;
|
||
|
varChild.lVal = CHILDID_SELF;
|
||
|
if( GetGenericProp( varChild, PROPINDEX_FOCUS, VT_EMPTY, pvar ) )
|
||
|
{
|
||
|
return S_OK;
|
||
|
}
|
||
|
return AccWrap_Base::get_accFocus( pvar );
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE get_accSelection( VARIANT * pvar )
|
||
|
{
|
||
|
VARIANT varChild;
|
||
|
varChild.vt = VT_I4;
|
||
|
varChild.lVal = CHILDID_SELF;
|
||
|
if( GetGenericProp( varChild, PROPINDEX_SELECTION, VT_EMPTY, pvar ) )
|
||
|
{
|
||
|
return S_OK;
|
||
|
}
|
||
|
return AccWrap_Base::get_accSelection( pvar );
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE accNavigate( long NavDir, VARIANT varStart, VARIANT * pvar )
|
||
|
{
|
||
|
PROPINDEX idxProp;
|
||
|
|
||
|
switch( NavDir )
|
||
|
{
|
||
|
case NAVDIR_UP: idxProp = PROPINDEX_NAV_UP; break;
|
||
|
case NAVDIR_DOWN: idxProp = PROPINDEX_NAV_DOWN; break;
|
||
|
case NAVDIR_LEFT: idxProp = PROPINDEX_NAV_LEFT; break;
|
||
|
case NAVDIR_RIGHT: idxProp = PROPINDEX_NAV_RIGHT; break;
|
||
|
case NAVDIR_NEXT: idxProp = PROPINDEX_NAV_NEXT; break;
|
||
|
case NAVDIR_PREVIOUS: idxProp = PROPINDEX_NAV_RIGHT; break;
|
||
|
case NAVDIR_LASTCHILD: idxProp = PROPINDEX_NAV_LASTCHILD; break;
|
||
|
case NAVDIR_FIRSTCHILD: idxProp = PROPINDEX_NAV_FIRSTCHILD; break;
|
||
|
|
||
|
default:
|
||
|
return AccWrap_Base::accNavigate( NavDir, varStart, pvar );
|
||
|
}
|
||
|
|
||
|
if( GetGenericProp( varStart, idxProp, VT_EMPTY, pvar ) )
|
||
|
{
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
return AccWrap_Base::accNavigate( NavDir, varStart, pvar );
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE accDoDefaultAction( VARIANT varChild )
|
||
|
{
|
||
|
VARIANT varResult;
|
||
|
if( GetGenericProp( varChild, PROPINDEX_DODEFAULTACTION, VT_I4, & varResult ) )
|
||
|
{
|
||
|
return varResult.lVal;
|
||
|
}
|
||
|
return AccWrap_Base::accDoDefaultAction( varChild );
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// AccessibleObjectFromWindow calls this to wrap outgoing objects...
|
||
|
//
|
||
|
|
||
|
|
||
|
HRESULT WrapObject( IUnknown * punk, REFIID riid, void ** ppv )
|
||
|
{
|
||
|
if( AccWrap_Base::AlreadyWrapped( punk ) )
|
||
|
{
|
||
|
return punk->QueryInterface( riid, ppv );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
IUnknown * punkWrap = (IAccessible *) new AccWrap_Annotate( punk );
|
||
|
// TODO - error check if NULL...
|
||
|
if( ! punkWrap )
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
HRESULT hr = punkWrap->QueryInterface( riid, ppv );
|
||
|
punkWrap->Release();
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
}
|