Why does ITypeInfo::Invoke return 0x80020003 (Member not found.)?

1

I am having trouble implementing an event sink for DShellWindowsEvents from SHDocVw.dll in Microsoft Visual C++.

The problem arises inside my implementation of IDispatch::Invoke. I implemented it by delegating its call to ITypeInfo::Invoke (as suggested by Microsoft on remarks section), but it always fails with error code 0x80020003 (Member not found).

It is interesting though that DShellWindowsEvents has only two methods (WindowRegistered and WindowRevoked) and ITypeInfo::GetIDsOfNames works successfully for both, returning the expected DISPID. But then how does it give "Member not found" error for Invoke?

The relevant part of the type library:

[
  uuid(FE4106E0-399A-11D0-A48C-00A0C90A8F39),
  helpstring("Event interface for IShellWindows")
]
dispinterface DShellWindowsEvents {
    properties:
    methods:
        [id(0x000000c8), helpstring("A new window was registered.")]
        void WindowRegistered([in] long lCookie);
        [id(0x000000c9), helpstring("A new window was revoked.")]
        void WindowRevoked([in] long lCookie);
};

[
  uuid(9BA05972-F6A8-11CF-A442-00A0C90A8F39),
  helpstring("ShellDispatch Load in Shell Context")
]
coclass ShellWindows {
    [default] interface IShellWindows;
    [default, source] dispinterface DShellWindowsEvents;
};

and my actual code where it all happens:

class CDShellWindowsEvents : public DShellWindowsEvents {

public:
    // IUnknown implementation
    virtual HRESULT __stdcall QueryInterface( REFIID riid, LPVOID *ppvObj )
    {
        if( ppvObj == NULL )  return E_INVALIDARG;
        *ppvObj = NULL;
        if(  riid == IID_IUnknown  ||  riid == IID_IDispatch  ||  riid == DIID_DShellWindowsEvents  )
        {
            *ppvObj = this;
            AddRef( );
            return NOERROR;
        }
        return E_NOINTERFACE;
    }

    virtual ULONG __stdcall AddRef( )
    {
        InterlockedIncrement( &m_cRef );
        return m_cRef;
    }

    virtual ULONG __stdcall Release( )
    {
        ULONG ulRefCount = InterlockedDecrement( &m_cRef );
        if( 0 == m_cRef )
            delete this;
        return ulRefCount;
    }


    // IDispatch implementation
    virtual HRESULT __stdcall GetTypeInfoCount( unsigned int * pctinfo )
    {
        if( pctinfo == NULL )  return E_INVALIDARG;
        *pctinfo = 1;
        return NOERROR;
    }

    virtual HRESULT __stdcall GetTypeInfo( unsigned int iTInfo, LCID lcid, ITypeInfo ** ppTInfo )
    {
        if( ppTInfo == NULL )  return E_INVALIDARG;
        *ppTInfo = NULL;

        if( iTInfo != 0 )  return DISP_E_BADINDEX;

        *ppTInfo = m_pTypeInfo;
        m_pTypeInfo->AddRef();
        return NOERROR;
    }

    virtual HRESULT __stdcall GetIDsOfNames(REFIID riid, OLECHAR ** rgszNames, unsigned int cNames, LCID lcid, DISPID * rgDispId )
    {
        return  m_pTypeInfo->GetIDsOfNames( rgszNames, cNames, rgDispId );
    }

    virtual HRESULT __stdcall Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pDispParams, VARIANT * pVarResult, EXCEPINFO * pExcepInfo, unsigned int * puArgErr )
    {
        // We could switch on dispIdMember here but we want to do this the flexible way, so we can easily implement it later for dispinterfaces with dozens of methods.

        return  m_pTypeInfo->Invoke( this, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr );

        /*HRESULT ret;
        switch( dispIdMember )
        {
        case 0x000000c8:    ret = WindowRegistered( pDispParams->rgvarg[0].intVal );  break;
        case 0x000000c9:    ret = WindowRevoked( pDispParams->rgvarg[0].intVal );  break;
        default:            ret = DISP_E_MEMBERNOTFOUND;
        }
        if( pVarResult )  pVarResult->lVal = ret;
        return ret;*/
    }


    // DShellWindowsEvents implementation
    virtual HRESULT __stdcall WindowRegistered( int nCookie )
    {
        LOG( "CDShellWindowsEvents::WindowRegistered( nCookie=0x%X ) ." , nCookie );
        return NOERROR;
    }

    virtual HRESULT __stdcall WindowRevoked( int nCookie )
    {
        LOG( "CDShellWindowsEvents::WindowRevoked( nCookie=0x%X ) ." , nCookie );
        return NOERROR;
    }


    // Constructor
    CDShellWindowsEvents( )
    {
        m_cRef = 1;

        LoadRegTypeLib( LIBID_SHDocVw, 1, 0, LANG_NEUTRAL, &m_pTypeLib );
        m_pTypeLib->GetTypeInfoOfGuid( DIID_DShellWindowsEvents, &m_pTypeInfo );
    }


    // Destructor
    ~CDShellWindowsEvents( )
    {
        m_pTypeInfo->Release( );
        m_pTypeLib->Release( );
    }


private:
    ULONG m_cRef;
    ITypeLib *m_pTypeLib;
    ITypeInfo *m_pTypeInfo;
};

Is this a problem with the type library not specifying dual for the dispinterface? If yes, is there a way to force ITypeInfo::Invoke to threat it as dual since I know its vtable is in order.

Thanks in advance.

c++
events
com
activex
ole
asked on Stack Overflow Jun 11, 2014 by user3574913

1 Answer

2

A dispinterface is an automation-only interface, not a dual interface. So, DShellWindowsEvents only natively has the 7 methods of IDispatch, not the extra 2 it declares. It's like declaring an IDispatch-only interface with a contract, such as predefined DISPIDs.

To use ITypeLib::Invoke in that fashion, you need to declare your own interface as [dual] with the extra methods.

[ object, dual, oleautomation, uuid(...) ]
interface IMyDualShellWindowsEvents : IDispatch
{
    [id(0x000000c8), helpstring("A new window was registered.")]
    HRESULT WindowRegistered([in] long lCookie);
    [id(0x000000c9), helpstring("A new window was revoked.")]
    HRESULT WindowRevoked([in] long lCookie);
}

You'll need provide or embed your own typelib and load it instead.

DShellWindowsEvents is a dispinterface, so your events object won't be QueryInterfaced for your internal interface, although you may handle that case anyway. As such, you don't need to register your typelib, just load it with LoadLibraryEx.

I think you may add hidden, restricted to the interface attributes, so it doesn't show up and can't be used in e.g. VBA, even by manually adding a reference to such a private typelib.

answered on Stack Overflow Jun 11, 2014 by acelent

User contributions licensed under CC BY-SA 3.0