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

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;
}
}