////////////////////////////////////////////////////////
// excerpt from delegate.idl
//
[
uuid(9b8c32f1-249a-11d2-a7bb-006008d25ccf),
object,
local
]
interface IDelegatorHookMethods : IUnknown
{
void DelegatorPreprocess( [in] DWORD nVtblIndex,
[in] void* pArgs,
[in, out] DWORD* pnCookie );
HRESULT DelegatorPostprocess( [in] DWORD nVtblIndex,
[in] HRESULT hrFromInner,
[in] DWORD nCookie );
}
[
uuid(9b8c32f9-249a-11d2-a7bb-006008d25ccf),
object,
local
]
interface IDelegatorHookQI : IUnknown
{
typedef enum _DelegatorHookOptions
{
DHO_PREPROCESS_METHODS = 0x00000001,
DHO_POSTPROCESS_METHODS = 0x00000002
} DelegatorHookOptions;
HRESULT Init( [in] IUnknown* pUnkInner );
HRESULT OnFirstDelegatorQIFor(
[in] REFIID iid,
[in, iid_is(iid)] IUnknown* pItfInner,
[out] DWORD* pgrfDelegatorHookOptions,
[in] REFIID iidMethodHook,
[out, iid_is(iidMethodHook)] void** ppMethodHook );
}
Figure 2 CoDelegator::QueryInterface Implementation
////////////////////////////////////////////////////////
// excerpt from CoDelegator.cpp
//
STDMETHODIMP CoDelegator::QueryInterface( REFIID iid, void** ppv )
{
// we subsume the identity of the inner
if ( IID_IUnknown == iid )
{
((IUnknown*)(*ppv = static_cast<IUnknown*>(this)))->AddRef();
return S_OK;
}
else if ( ( IID_IMarshal == iid ) && ( DO_MBV_ALL & m_grf ) )
{
((IUnknown*)(*ppv = static_cast<IMarshal*>(this)))->AddRef();
return S_OK;
}
*ppv = 0;
Lock lock( *this );
// see if we've already assimilated the requested interface
HRESULT hr = S_OK;
{
Delegator* pDelegator = 0;
if ( _findDelegator( iid, pDelegator, hr ) )
{
if ( SUCCEEDED( hr ) )
reinterpret_cast<IUnknown*>( *ppv =
pDelegator )->AddRef();
return hr;
}
}
// go get the requested interface from the inner and assimilate it.
IUnknown* pUnkInner = 0;
hr = m_pUnkInner->QueryInterface( iid, (void**)&pUnkInner );
if ( SUCCEEDED( hr ) )
{
DWORD grfOptions = 0;
IDelegatorHookMethods* pHookMethods = 0;
if ( m_pHook )
{
hr = m_pHook->OnFirstDelegatorQIFor( iid, pUnkInner,
&grfOptions, IID_IDelegatorHookMethods,
(void**)&pHookMethods );
if ( SUCCEEDED( hr ) )
{
// watch for invalid results from QI hook
grfOptions = grfOptions & 0x00000007;
if ( ( 0 == grfOptions ) && pHookMethods )
{
pHookMethods->Release();
pHookMethods = 0;
}
}
else if ( E_NOINTERFACE == hr )
{
// we only give the QI hook *one* chance to say
// this to a particular interface so that we
// help maintain a correct implementation of QI.
grfOptions = Delegator::DONT_EXPOSE_FROM_QI;
hr = S_OK;
}
// if postprocessing is required,
// make sure we've acquired a TLS slot
if ( SUCCEEDED( hr )
&& ( DHO_POSTPROCESS_METHODS & grfOptions )
&& !_lazyAllocTLSIndex() )
{
hr = E_OUTOFMEMORY; // sort of :-)
if ( pHookMethods )
{
pHookMethods->Release();
pHookMethods = 0;
}
}
}
if ( SUCCEEDED( hr ) )
{
// grow the array if necessary
if ( m_last == m_end )
hr = _growArray();
if ( SUCCEEDED( hr ) )
{
Delegator* pDelegator =
new Delegator( *this, pUnkInner, iid,
grfOptions, pHookMethods );
if ( pDelegator )
{
*m_last++ = pDelegator;
if ( Delegator::DONT_EXPOSE_FROM_QI &
grfOptions )
hr = E_NOINTERFACE;
else reinterpret_cast<IUnknown*>
(*ppv = pDelegator)->AddRef();
}
else hr = E_OUTOFMEMORY;
}
if ( pHookMethods )
pHookMethods->Release();
}
pUnkInner->Release();
}
return hr;
}
Figure 4 IDelegatorHook Methods
struct MethodHook : IDelegatorHookMethods
{
const IID m_iid;
MethodHook( REFIID iid ) : m_iid( iid ) {}
// this could be some smart method that maps
// method indices to names via type information
// and dumps them to debug output or a logfile
void LogMessage( const TCHAR* psz,
DWORD nMethod )
{
// implementation omitted for brevity
}
// IUnknown implementation omitted for brevity
// IDelegatorHookMethods implementation
void DelegatorPreprocess( DWORD nIndex,
void*, DWORD* )
{
LogMessage( __TEXT( "entering" ), nIndex );
}
HRESULT DelegatorPostprocess( DWORD nVtblIndex,
HRESULT, DWORD )
{
LogMessage( __TEXT( "leaving" ), nIndex );
return S_OK;
}
};
HRESULT CoAuditorHookQI::OnFirstDelegatorQIFor(
REFIID iid, IUnknown*, DWORD* pgrfOptions,
REFIID iidMethodHook, void** ppvMethodHook )
{
HRESULT hr = S_OK;
MethodHook* pHook = new MethodHook( iid );
pHook->AddRef();
hr = pHook->QueryInterface( iidMethodHook,
ppvMethodHook );
pHook->Release();
return hr;
}