Figure 1   Delegator Hook Interfaces

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