1
  • Implemented the Sink Class - to receive event notifications from COM Server
  • Event Interface derives from IDispatch

I have an issue whereby an IConnectionPoint::Advise call returns E_NOTIMPL. This could be because the connection point only allows one connection - MSDN.

Note:

  • COM Server is out-of-process
  • Pure C++ implementation

EDIT:

S8.tlh: C++ source equivalent of Win32 type library S8.tlb:

struct __declspec(uuid("090910c3-28c3-45fe-861d-edcf11aa9788"))
IS8SimulationEvents : IDispatch
{

    // Methods:
    HRESULT S8SimulationReset ( );
    HRESULT S8SimulationEndRun ( );
    HRESULT S8SimulationCustomEvent (
        BSTR * TextInfo );
    HRESULT S8SimulationOpened ( );
    HRESULT S8SimulationEndTrial ( );
    HRESULT S8SimulationOEMEvent (
        BSTR * TextInfo );
    HRESULT S8SimulationReadyToClose ( );
    HRESULT S8SimulationUserMessage (
        long * Answer,
        BSTR * TextMsg,
        long ValidAnswers );
};

Implementation of Class Sink - to handle event notifications:

class Sink : public IS8SimulationEvents
{
public:
Sink(){
    m_dwRefCount = 0;
};
~Sink(){};
/*
* IS8SimulationEvent interface functions
*/
HRESULT S8SimulationEndTrial()
{
    cout << "Simulation complete." << endl;
    return S_OK;;
};

HRESULT S8SimulationOpened()
{
    cout << "Simulation open." << endl;
    return S_OK;
};

HRESULT S8SimulationReadyToClose()
{
    cout << "Simulation ready to close" << endl;
    return S_OK;
};

ULONG STDMETHODCALLTYPE AddRef()
{
    m_dwRefCount++;
    return m_dwRefCount;
};

ULONG STDMETHODCALLTYPE Release()
{
    ULONG l;
    l = m_dwRefCount--;

    if (0 == m_dwRefCount)
    {
        delete this;
    }

    return m_dwRefCount;
};

HRESULT STDMETHODCALLTYPE QueryInterface(
                                        REFIID iid ,
                                        void **ppvObject)
{
    m_dwRefCount++;
    *ppvObject = (void *)this;
    return S_OK;
};

HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT *pctinfo)
{
    return E_NOTIMPL;
};

HRESULT STDMETHODCALLTYPE GetIDsOfNames( 
                                        REFIID riid,
                                        LPOLESTR *rgszNames,
                                        UINT cNames,
                                        LCID lcid,
                                        DISPID *rgDispId)
{
    return E_NOTIMPL;
};

HRESULT STDMETHODCALLTYPE GetTypeInfo(
                                    unsigned int iTInfo,
                                    LCID lcid,
                                    ITypeInfo FAR* FAR* ppTInfo)
{
    return E_NOTIMPL;
};

HRESULT STDMETHODCALLTYPE Invoke(
                                DISPID dispIdMember,
                                REFIID riid,
                                LCID lcid,
                                WORD wFlags,
                                DISPPARAMS FAR* pDispParams,
                                VARIANT FAR* pVarResult,
                                EXCEPINFO FAR* pExcepInfo,
                                unsigned int FAR* puArgErr)
{
    HRESULT hresult = S_OK;
    if (pDispParams)
    {
        switch (dispIdMember) {
        case 1:
            return S8SimulationEndTrial();
        case 2:
            return S8SimulationOpened();
        case 3:
            return S8SimulationReadyToClose();
        default:
            return E_NOTIMPL;
        }

    }
    return E_NOTIMPL;
}
private:
    DWORD m_dwRefCount;
public:
void SetupConnectionPoint (IS8Simulation *pis8)
{

    HRESULT hresult;
    IConnectionPointContainer *pContainer = NULL;
    IConnectionPoint *pConnection = NULL;
    IUnknown *pSinkUnk = NULL;
    Sink *pSink = NULL;
    DWORD dwAdvise;

    dwAdvise = 0;

    hresult = pis8->QueryInterface(
                        __uuidof(IConnectionPointContainer),
                        (void **) &pContainer);

    if (SUCCEEDED(hresult))
    {
        cout << "IConnectionPointContainer inteface supported." << endl;
    } else {
        cerr << "Error: No such interface supported." << endl;
        exit (hresult);
    }

                                    __uuidof(IS8SimulationEvents),
                                    &pConnection); 

    switch (HRESULT_CODE(hresult)) {
        case NOERROR:
            cout << "Obtained valid interface pointer." << endl;
            break;
        case E_POINTER:
            cerr << "Invalid pointer: the address is not valid." << endl;
            exit (hresult);
            break;
        case CONNECT_E_NOCONNECTION:
            cerr << "This connectable object not support the "
                    "outgoing interface specified." << endl;
            exit (hresult);
            break;
        case E_UNEXPECTED:
        default:
            cerr << "Catastrophic failure." << endl;
            exit (hresult);
            break;
    }

    pContainer->Release();


    hresult = pSink->QueryInterface(
                            __uuidof(IUnknown),
                            (void **)&pSinkUnk);

    if (FAILED(hresult))
    {
        exit (EXIT_FAILURE);
    }

    hresult = pConnection->Advise(
                            pSinkUnk,
                            &dwAdvise);

    switch (HRESULT_CODE(hresult)) {
        case NOERROR:
            cout << "The connection has been established and "
                    "*dwAdvise has the connection token." << endl;
            break;
        case E_POINTER:
            cerr << "Invalid pointer: "
                    "the value pSinkUnk or dwAdvise is not valid." << endl;
            exit (hresult);
            break;
        case CONNECT_E_ADVISELIMIT:
            cerr << "The connection point has already reached "
                    "its limit of connections and cannot accept "
                    "any more." << endl;
            exit (hresult);
            break;
        case CONNECT_E_CANNOTCONNECT:
            cerr << "The sink does not support the interface "
                    "required by this connection point." << endl;
            exit (hresult);
            break;
        case E_NOTIMPL:
            break;
            case E_UNEXPECTED:
            default:
        cerr << "Catastrophic failure." << endl;
        exit (hresult);
        break;
    }
    return;
}
};

EDIT:

Implementation of IUnknown Interface in Sink Class

ULONG STDMETHODCALLTYPE AddRef()
{
    m_dwRefCount++;
    return m_dwRefCount;
};

ULONG STDMETHODCALLTYPE Release()
{
    ULONG l;
    l = m_dwRefCount--;

    if (0 == m_dwRefCount)
    {
        delete this;
    }

    return m_dwRefCount;
};

HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject)
{
    m_dwRefCount++;
    *ppvObject = (void *)this;
    return S_OK;
};

Question:

  • How to check if a container supports multiple connections?
  • If more information is required, please comment accordingly.
Vinay
  • 4,743
  • 7
  • 33
  • 43
Aaron
  • 2,823
  • 9
  • 44
  • 57
  • This can also occur if your sink object that you send to advise is not implementing the IUnknown – Vinay Jan 27 '09 at 01:12
  • How to check this? And if so, how to change? – Aaron Jan 27 '09 at 01:18
  • @Vinay: Do you think you could help a bit more please? – Aaron Jan 27 '09 at 10:09
  • Try to trace inside advise api. You can also try to QI for the IUnknown object before calling advise. As Michael pointed about the MSDN article, I suspect QI is failing for IUnknown. – Vinay Jan 29 '09 at 16:20

4 Answers4

1

Please read the MSDN article again.

 A connection point that allows only one interface
 can return E_NOTIMPL FROM the IConnectionPoint::EnumConnections method
EnumConnections : E_NOTIMPL 
The connection point does not support enumeration.

IConnectionPoint::Advise is required to reply

CONNECT_E_ADVISELIMIT

when he connection point has already reached its limit of connections and cannot accept any more.

--

Michael

Michael
  • 106
  • 3
  • Hi Michael, thank you for pointing this out. Would it be safe to ignore this and continue as normal? – Aaron Jan 27 '09 at 08:45
  • If you'd like to see my implementation of the Sink Class or more information please let me know. I'm new to COM Programming - I'm stuck at this particular point – Aaron Jan 27 '09 at 09:53
  • The simplest thing may cause advise to return E_NOTIMPL, 1 )for example, calls to coinitializeex do not match. 2 )the client and server are not the same model i.e) APARTMENTTHREADED /vs MULTITHREADED Who wrote the server Connectionpoint or event class ? -- Michael – Michael Jan 27 '09 at 17:43
  • @Michael: Firstly, THANK YOU for your response! With regards to: 1) I've only made a single call to coinitialize() 2) With regards to this suggestion - I'll investigate 3) The server CP was implemented by Simul8 and the event class - do you mean the Sink class? Otherwise Simul8 – Aaron Jan 27 '09 at 23:58
  • @Michael: If need be I'll attach the full implementation (of the COM Client thus far and Sink Class). Just let me know - regards :) – Aaron Jan 27 '09 at 23:58
  • Is marshalling code (proxy/stub) contained in the TLB file - generated from MIDL compiler? – Aaron Jan 28 '09 at 02:25
  • Atklin. Somewhere in the Simul8 TLB file should be the event inferface. Your sink class should implement that class methods. unless Simul8 does not provide events. – Michael Jan 28 '09 at 21:16
  • Thanks for the response Michael :). I can confirm that the event interface is present. How to confirm/establish if the client and server are in the same model? I'm research... – Aaron Jan 29 '09 at 00:38
  • http://www.simul8.com/products/features/external.htm#com It may be be that the events source you implemented is not the correct interface for a partcular com server. I posted another answer because of comment length limits. – Michael Jan 29 '09 at 20:24
0

AtlAdvise should cause the connectionpoint to call QI on the event sink. You cannot be sure WHAT or how many times it might call QI.

Suggestion: Refer to something like this in the eventsink to determine the correct event interface is queried.

void GuidToString(PTCHAR s, LPGUID piid )
{
_stprintf(s, _T("{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}"),
    piid->Data1, piid->Data2,
    piid->Data3,
    piid->Data4[0], piid->Data4[1],
    piid->Data4[2], piid->Data4[3],
    piid->Data4[4], piid->Data4[5],
    piid->Data4[6], piid->Data4[7]);
    // Outputdebugstr (s)
}

ALSO the event sink QueryInterface should do something like

if (iid == IID_IUnknown || iid == IID_AppEvents)
     *ppObj = (AppEvents*)this;

So it returns only the correct interface.

Jason Plank
  • 2,336
  • 5
  • 31
  • 40
Michael
  • 106
  • 3
  • Obtained Exception from HRESULT 0xc000005, post making changes to QI in the Event Sink: m_dwRefCount++; if (iid == IID_IUnknown || iid == __uuidof(IS8SimulationEvents)) { ppvObject = (void *)this; } return S_OK; – Aaron Jan 30 '09 at 09:22
  • I'll attached the full Sink implementation and the IS8SimulationEvents from S8.tlh – Aaron Jan 30 '09 at 09:23
0

COM Library calls QI for so many interfaces like IUnknown, IMarshall etc when Advise call in invoked.

As Michael pointed about the MSDN article, it says that it returns E_NOTIMPL only for EnumConnections API and not for Advise. I suspect QI for returning the E_NOTIMPL. So try to trace the Advise API. And you will come to know about the failure of QI.

Vinay
  • 4,743
  • 7
  • 33
  • 43
0

Solved:

  • Using pure C++
  • No ATL

With the following implementation of QI:

HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject)
{
    if (iid == __uuidof(IUnknown) || iid == __uuidof(IS8SimulationEvents))
    {
        *ppvObject = (IS8SimulationEvents*)this;
    } else {
        *ppvObject = NULL;
        return E_NOINTERFACE;
    }
    m_dwRefCount++;
    return S_OK;
};
Aaron
  • 2,823
  • 9
  • 44
  • 57