481 lines
11 KiB
C++
481 lines
11 KiB
C++
|
// Copyright (c) 2000-2000 Microsoft Corporation
|
||
|
|
||
|
// --------------------------------------------------------------------------
|
||
|
//
|
||
|
// wrap_base
|
||
|
//
|
||
|
// Base class for IAccessible wrapping.
|
||
|
// Derived classes implement annotation, caching, and other cool features.
|
||
|
//
|
||
|
// --------------------------------------------------------------------------
|
||
|
|
||
|
#include "oleacc_p.h"
|
||
|
|
||
|
#include <initguid.h> // used for IIS_AccWrapBase_GetIUnknown in wrap_base.h
|
||
|
#include "wrap_base.h"
|
||
|
|
||
|
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
QIINDEX_IAccessible,
|
||
|
QIINDEX_IEnumVARIANT,
|
||
|
QIINDEX_IOleWindow,
|
||
|
QIINDEX_IAccIdentity,
|
||
|
QIINDEX_IServiceProvider,
|
||
|
};
|
||
|
|
||
|
|
||
|
AccWrap_Base::AccWrap_Base( IUnknown * pUnknown )
|
||
|
: m_ref( 1 ),
|
||
|
m_QIMask( 0 ),
|
||
|
m_pUnknown( pUnknown ),
|
||
|
m_pAcc( NULL ),
|
||
|
m_pEnumVar( NULL ),
|
||
|
m_pOleWin( NULL ),
|
||
|
m_pAccID( NULL ),
|
||
|
m_pSvcPdr( NULL ),
|
||
|
m_pTypeInfo( NULL )
|
||
|
{
|
||
|
m_pUnknown->AddRef();
|
||
|
}
|
||
|
|
||
|
AccWrap_Base::~AccWrap_Base()
|
||
|
{
|
||
|
m_pUnknown->Release();
|
||
|
if( m_pAcc )
|
||
|
m_pAcc->Release();
|
||
|
if( m_pEnumVar )
|
||
|
m_pEnumVar->Release();
|
||
|
if( m_pOleWin )
|
||
|
m_pOleWin->Release();
|
||
|
if( m_pAccID )
|
||
|
m_pAccID->Release();
|
||
|
if( m_pSvcPdr )
|
||
|
m_pSvcPdr->Release();
|
||
|
if( m_pTypeInfo )
|
||
|
m_pTypeInfo->Release();
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// Helper used by QI...
|
||
|
HRESULT AccWrap_Base::CheckForInterface( IUnknown * punk, REFIID riid, int QIIndex, void ** pDst )
|
||
|
{
|
||
|
// Do we already have a cached interface ptr? If so, don't need to QI again...
|
||
|
if( *pDst )
|
||
|
return S_OK;
|
||
|
|
||
|
// Did we try QI'ing for this before? If so, don't try again, just return E_NOINTERFACE...
|
||
|
if( IsBitSet( m_QIMask, QIIndex ) )
|
||
|
return E_NOINTERFACE;
|
||
|
|
||
|
SetBit( & m_QIMask, QIIndex );
|
||
|
|
||
|
HRESULT hr = punk->QueryInterface( riid, pDst );
|
||
|
if( FAILED( hr ) )
|
||
|
{
|
||
|
*pDst = NULL;
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
// Paranoia ( in case QI returns S_OK with NULL... )
|
||
|
if( ! *pDst )
|
||
|
return E_FAIL;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
BOOL AccWrap_Base::AlreadyWrapped( IUnknown * punk )
|
||
|
{
|
||
|
// Try QueryService'ing to IIS_AccWrapBase_GetIUnknown - if it succeeds, then
|
||
|
// we are talking to something that's already wrapped.
|
||
|
IServiceProvider * psvc = NULL;
|
||
|
HRESULT hr = punk->QueryInterface( IID_IServiceProvider, (void **) & psvc );
|
||
|
if( hr != S_OK || psvc == NULL )
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
IUnknown * pout = NULL;
|
||
|
hr = psvc->QueryService( IIS_AccWrapBase_GetIUnknown, IID_IUnknown, (void **) & pout );
|
||
|
psvc->Release();
|
||
|
if( hr != S_OK || pout == NULL )
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
pout->Release();
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
IUnknown * AccWrap_Base::Wrap( IUnknown * pUnk )
|
||
|
{
|
||
|
if( AlreadyWrapped( pUnk ) )
|
||
|
{
|
||
|
pUnk->AddRef();
|
||
|
return pUnk;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return WrapFactory( pUnk );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// IUnknown
|
||
|
// Implement refcounting ourselves
|
||
|
// Also implement QI ourselves, so that we return a ptr back to the wrapper.
|
||
|
HRESULT STDMETHODCALLTYPE AccWrap_Base::QueryInterface( REFIID riid, void ** ppv )
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
*ppv = NULL;
|
||
|
|
||
|
if ( riid == IID_IUnknown )
|
||
|
{
|
||
|
*ppv = static_cast< IAccessible * >( this );
|
||
|
}
|
||
|
else if( riid == IID_IAccessible || riid == IID_IDispatch )
|
||
|
{
|
||
|
hr = CheckForInterface( m_pUnknown, IID_IAccessible, QIINDEX_IAccessible, (void **) & m_pAcc );
|
||
|
if( hr != S_OK )
|
||
|
return hr;
|
||
|
*ppv = static_cast< IAccessible * >( this );
|
||
|
}
|
||
|
else if( riid == IID_IEnumVARIANT )
|
||
|
{
|
||
|
hr = CheckForInterface( m_pUnknown, IID_IEnumVARIANT, QIINDEX_IEnumVARIANT, (void **) & m_pEnumVar );
|
||
|
if( hr != S_OK )
|
||
|
return hr;
|
||
|
*ppv = static_cast< IEnumVARIANT * >( this );
|
||
|
}
|
||
|
else if( riid == IID_IOleWindow )
|
||
|
{
|
||
|
hr = CheckForInterface( m_pUnknown, IID_IOleWindow, QIINDEX_IOleWindow, (void **) & m_pOleWin );
|
||
|
if( hr != S_OK )
|
||
|
return hr;
|
||
|
*ppv = static_cast< IOleWindow * >( this );
|
||
|
}
|
||
|
else if( riid == IID_IAccIdentity )
|
||
|
{
|
||
|
hr = CheckForInterface( m_pUnknown, IID_IAccIdentity, QIINDEX_IAccIdentity, (void **) & m_pAccID );
|
||
|
if( hr != S_OK )
|
||
|
return hr;
|
||
|
*ppv = static_cast< IAccIdentity * >( this );
|
||
|
}
|
||
|
else if( riid == IID_IServiceProvider )
|
||
|
{
|
||
|
*ppv = static_cast< IServiceProvider * >( this );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return E_NOINTERFACE ;
|
||
|
}
|
||
|
|
||
|
AddRef( );
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
ULONG STDMETHODCALLTYPE AccWrap_Base::AddRef( )
|
||
|
{
|
||
|
return ++m_ref;
|
||
|
}
|
||
|
|
||
|
|
||
|
ULONG STDMETHODCALLTYPE AccWrap_Base::Release( )
|
||
|
{
|
||
|
if( --m_ref == 0 )
|
||
|
{
|
||
|
delete this;
|
||
|
return 0;
|
||
|
}
|
||
|
return m_ref;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// IServiceProvider
|
||
|
HRESULT STDMETHODCALLTYPE AccWrap_Base::QueryService( REFGUID guidService, REFIID riid, void **ppv )
|
||
|
{
|
||
|
// For the moment, just handle locally. Later, also forward others through to the base,
|
||
|
// if it supports IServiceProvider.
|
||
|
|
||
|
if( guidService == IIS_AccWrapBase_GetIUnknown )
|
||
|
{
|
||
|
return m_pUnknown->QueryInterface( riid, ppv );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Pass through to base, if it handles IServiceProvider..
|
||
|
CheckForInterface( m_pUnknown, IID_IServiceProvider, QIINDEX_IServiceProvider, (void **) & m_pSvcPdr );
|
||
|
if( m_pSvcPdr )
|
||
|
{
|
||
|
return m_pSvcPdr->QueryService( guidService, riid, ppv );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// MSDN mentions SVC_E_UNKNOWNSERVICE as the return code, but that's not in any of the headers.
|
||
|
// Returning E_INVALIDARG instead. (Don't want to use E_NOINTERFACE, since that clashes with
|
||
|
// QI's return value, making it hard to distinguish between valid service+invalid interface vs
|
||
|
// invalid service.
|
||
|
return E_INVALIDARG;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// IDispatch
|
||
|
// implemented locally, to avoid IDispatch short-circuiting...
|
||
|
|
||
|
HRESULT AccWrap_Base::InitTypeInfo()
|
||
|
{
|
||
|
if( m_pTypeInfo )
|
||
|
return S_OK;
|
||
|
|
||
|
// Try getting the typelib from the registry
|
||
|
ITypeLib * piTypeLib = NULL;
|
||
|
HRESULT hr = LoadRegTypeLib( LIBID_Accessibility, 1, 0, 0, &piTypeLib );
|
||
|
|
||
|
if( FAILED( hr ) || piTypeLib == NULL )
|
||
|
{
|
||
|
OLECHAR wszPath[ MAX_PATH ];
|
||
|
|
||
|
// Try loading directly.
|
||
|
#ifdef UNICODE
|
||
|
MyGetModuleFileName( NULL, wszPath, ARRAYSIZE( wszPath ) );
|
||
|
#else
|
||
|
TCHAR szPath[ MAX_PATH ];
|
||
|
|
||
|
MyGetModuleFileName( NULL, szPath, ARRAYSIZE( szPath ) );
|
||
|
MultiByteToWideChar( CP_ACP, 0, szPath, -1, wszPath, ARRAYSIZE( wszPath ) );
|
||
|
#endif
|
||
|
hr = LoadTypeLib(wszPath, &piTypeLib);
|
||
|
}
|
||
|
|
||
|
if( SUCCEEDED( hr ) )
|
||
|
{
|
||
|
hr = piTypeLib->GetTypeInfoOfGuid( IID_IAccessible, & m_pTypeInfo );
|
||
|
piTypeLib->Release();
|
||
|
|
||
|
if( ! SUCCEEDED( hr ) )
|
||
|
{
|
||
|
m_pTypeInfo = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE AccWrap_Base::GetTypeInfoCount( UINT * pctInfo )
|
||
|
{
|
||
|
HRESULT hr = InitTypeInfo();
|
||
|
if( SUCCEEDED( hr ) )
|
||
|
{
|
||
|
*pctInfo = 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*pctInfo = 0;
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE AccWrap_Base::GetTypeInfo(
|
||
|
UINT itInfo,
|
||
|
LCID unused( lcid ),
|
||
|
ITypeInfo ** ppITypeInfo )
|
||
|
{
|
||
|
if( ppITypeInfo == NULL )
|
||
|
{
|
||
|
return E_POINTER;
|
||
|
}
|
||
|
|
||
|
*ppITypeInfo = NULL;
|
||
|
|
||
|
if( itInfo != 0 )
|
||
|
{
|
||
|
return TYPE_E_ELEMENTNOTFOUND;
|
||
|
}
|
||
|
|
||
|
HRESULT hr = InitTypeInfo();
|
||
|
if( SUCCEEDED( hr ) )
|
||
|
{
|
||
|
m_pTypeInfo->AddRef();
|
||
|
*ppITypeInfo = m_pTypeInfo;
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE AccWrap_Base::GetIDsOfNames(
|
||
|
REFIID unused( riid ),
|
||
|
OLECHAR ** rgszNames,
|
||
|
UINT cNames,
|
||
|
LCID unused( lcid ),
|
||
|
DISPID * rgDispID )
|
||
|
{
|
||
|
HRESULT hr = InitTypeInfo();
|
||
|
if( ! SUCCEEDED( hr ) )
|
||
|
{
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
return m_pTypeInfo->GetIDsOfNames( rgszNames, cNames, rgDispID );
|
||
|
}
|
||
|
|
||
|
HRESULT STDMETHODCALLTYPE AccWrap_Base::Invoke(
|
||
|
DISPID dispID,
|
||
|
REFIID unused( riid ),
|
||
|
LCID unused( lcid ),
|
||
|
WORD wFlags,
|
||
|
DISPPARAMS * pDispParams,
|
||
|
VARIANT * pvarResult,
|
||
|
EXCEPINFO * pExcepInfo,
|
||
|
UINT * puArgErr )
|
||
|
{
|
||
|
HRESULT hr = InitTypeInfo();
|
||
|
if( ! SUCCEEDED( hr ) )
|
||
|
return hr;
|
||
|
|
||
|
return m_pTypeInfo->Invoke( (IAccessible *)this, dispID, wFlags,
|
||
|
pDispParams, pvarResult, pExcepInfo, puArgErr );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// Post-method fixup methods
|
||
|
//
|
||
|
// These wrap any outgoing params. All of these are passed a pointer
|
||
|
// to the prospective outgoing value, which is wrapped and modified in-place.
|
||
|
//
|
||
|
|
||
|
|
||
|
HRESULT AccWrap_Base::ProcessIUnknown( IUnknown ** ppUnk )
|
||
|
{
|
||
|
if( ! ppUnk || ! *ppUnk )
|
||
|
return S_OK;
|
||
|
|
||
|
IUnknown * punkWrap = Wrap( *ppUnk );
|
||
|
|
||
|
(*ppUnk)->Release();
|
||
|
*ppUnk = punkWrap;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT AccWrap_Base::ProcessIDispatch( IDispatch ** ppdisp )
|
||
|
{
|
||
|
if( ! ppdisp || ! *ppdisp )
|
||
|
return S_OK;
|
||
|
|
||
|
IUnknown * punkWrap = Wrap( *ppdisp );
|
||
|
|
||
|
IDispatch * pdispWrap = NULL;
|
||
|
HRESULT hr = punkWrap->QueryInterface( IID_IDispatch, (void **) & pdispWrap );
|
||
|
punkWrap->Release();
|
||
|
|
||
|
if( hr != S_OK )
|
||
|
{
|
||
|
// clean up...
|
||
|
(*ppdisp)->Release();
|
||
|
*ppdisp = pdispWrap;
|
||
|
return FAILED( hr ) ? hr : S_OK;
|
||
|
}
|
||
|
|
||
|
(*ppdisp)->Release();
|
||
|
*ppdisp = pdispWrap;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT AccWrap_Base::ProcessIEnumVARIANT( IEnumVARIANT ** ppEnum )
|
||
|
{
|
||
|
if( ! ppEnum || ! *ppEnum )
|
||
|
return S_OK;
|
||
|
|
||
|
IUnknown * punkWrap = Wrap( *ppEnum );
|
||
|
|
||
|
IEnumVARIANT * penumWrap = NULL;
|
||
|
HRESULT hr = punkWrap->QueryInterface( IID_IEnumVARIANT, (void **) & penumWrap );
|
||
|
punkWrap->Release();
|
||
|
|
||
|
if( hr != S_OK )
|
||
|
{
|
||
|
// clean up...
|
||
|
(*ppEnum)->Release();
|
||
|
*ppEnum = penumWrap;
|
||
|
return FAILED( hr ) ? hr : S_OK;
|
||
|
}
|
||
|
|
||
|
(*ppEnum)->Release();
|
||
|
*ppEnum = penumWrap;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
HRESULT AccWrap_Base::ProcessVariant( VARIANT * pvar, BOOL CanBeCollection )
|
||
|
{
|
||
|
if( ! pvar )
|
||
|
return S_OK;
|
||
|
|
||
|
/*
|
||
|
if( pvar->vt == VT_I4 || pvar->vt == VT_EMPTY )
|
||
|
return S_OK; // Is VT_EMPTY an allowable output value? could do some validation here...
|
||
|
*/
|
||
|
if( CanBeCollection && pvar->vt == VT_UNKNOWN )
|
||
|
{
|
||
|
// Is an IEnumVARIANT (as an IUnknown)
|
||
|
return ProcessIUnknown( & pvar->punkVal );
|
||
|
}
|
||
|
else if( pvar->vt == VT_DISPATCH )
|
||
|
{
|
||
|
// Is an IAccessible (as an IDispatch)
|
||
|
return ProcessIDispatch( & pvar->pdispVal );
|
||
|
}
|
||
|
|
||
|
// TODO - check for other types?
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT AccWrap_Base::ProcessEnum( VARIANT * pvar, ULONG * pceltFetched )
|
||
|
{
|
||
|
if( ! pvar || ! pceltFetched || ! *pceltFetched )
|
||
|
return S_OK;
|
||
|
|
||
|
for( ULONG count = 0 ; count > *pceltFetched ; count++ )
|
||
|
{
|
||
|
HRESULT hr = ProcessVariant( & pvar[ count ] );
|
||
|
if( hr != S_OK )
|
||
|
{
|
||
|
// Clean up - clear all variants, return error...
|
||
|
for( ULONG c = 0 ; c < *pceltFetched ; c++ )
|
||
|
{
|
||
|
VariantClear( & pvar[ c ] );
|
||
|
}
|
||
|
|
||
|
*pceltFetched = 0;
|
||
|
return hr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return S_OK;
|
||
|
}
|