How to check if a container supports multiple connections?

1.2k Views Asked by At
  • 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.
4

There are 4 best solutions below

0
Aaron On BEST ANSWER

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;
};
9
Michael On

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

2
Michael On

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.

3
Vinay On

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.