views:

445

answers:

4
  • 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.
+1  A: 

Hi Atkin. 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
Hi Michael, thank you for pointing this out. Would it be safe to ignore this and continue as normal?
Aaron
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
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 modeli.e) APARTMENTTHREADED /vs MULTITHREADEDWho wrote the server Connectionpoint or event class ?--Michael
Michael
@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
@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
Is marshalling code (proxy/stub) contained in the TLB file - generated from MIDL compiler?
Aaron
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
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
http://www.simul8.com/products/features/external.htm#comIt 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
A: 
Michael
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
I'll attached the full Sink implementation and the IS8SimulationEvents from S8.tlh
Aaron
A: 

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
How can I trace the Advise API using VC++ tools?
Aaron
I'll attempt the above to find the issue
Aaron
Just put a breakpoint at hresult = pConnection->Advise( pSinkUnk, and press F11, when it breaks.
Vinay
A: 

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