Exposing managed events to COM using C++









up vote
1
down vote

favorite
2












It is possible to exposed managed events written in C# to be exposed and used in a COM object written using c++. not being that familiar with com and atl. Can you please show what would the C++ side of things would look like for the example shown in the MSDN article



http://msdn.microsoft.com/en-us/library/dd8bf0x3.aspx



The VB6 code shown proves that it is doable.










share|improve this question























  • What technology are you using on unmanaged side, ATL, MFC or something else?
    – Zdeslav Vojkovic
    Sep 29 '12 at 8:30










  • I guess any will do as long as the job gets done. I would prefer towards the solution that is simple and concise.
    – TrustyCoder
    Sep 29 '12 at 13:45














up vote
1
down vote

favorite
2












It is possible to exposed managed events written in C# to be exposed and used in a COM object written using c++. not being that familiar with com and atl. Can you please show what would the C++ side of things would look like for the example shown in the MSDN article



http://msdn.microsoft.com/en-us/library/dd8bf0x3.aspx



The VB6 code shown proves that it is doable.










share|improve this question























  • What technology are you using on unmanaged side, ATL, MFC or something else?
    – Zdeslav Vojkovic
    Sep 29 '12 at 8:30










  • I guess any will do as long as the job gets done. I would prefer towards the solution that is simple and concise.
    – TrustyCoder
    Sep 29 '12 at 13:45












up vote
1
down vote

favorite
2









up vote
1
down vote

favorite
2






2





It is possible to exposed managed events written in C# to be exposed and used in a COM object written using c++. not being that familiar with com and atl. Can you please show what would the C++ side of things would look like for the example shown in the MSDN article



http://msdn.microsoft.com/en-us/library/dd8bf0x3.aspx



The VB6 code shown proves that it is doable.










share|improve this question















It is possible to exposed managed events written in C# to be exposed and used in a COM object written using c++. not being that familiar with com and atl. Can you please show what would the C++ side of things would look like for the example shown in the MSDN article



http://msdn.microsoft.com/en-us/library/dd8bf0x3.aspx



The VB6 code shown proves that it is doable.







c# c++ events com atl






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Dec 17 '17 at 3:24









Cœur

17.2k9102140




17.2k9102140










asked Sep 28 '12 at 22:33









TrustyCoder

2,45284396




2,45284396











  • What technology are you using on unmanaged side, ATL, MFC or something else?
    – Zdeslav Vojkovic
    Sep 29 '12 at 8:30










  • I guess any will do as long as the job gets done. I would prefer towards the solution that is simple and concise.
    – TrustyCoder
    Sep 29 '12 at 13:45
















  • What technology are you using on unmanaged side, ATL, MFC or something else?
    – Zdeslav Vojkovic
    Sep 29 '12 at 8:30










  • I guess any will do as long as the job gets done. I would prefer towards the solution that is simple and concise.
    – TrustyCoder
    Sep 29 '12 at 13:45















What technology are you using on unmanaged side, ATL, MFC or something else?
– Zdeslav Vojkovic
Sep 29 '12 at 8:30




What technology are you using on unmanaged side, ATL, MFC or something else?
– Zdeslav Vojkovic
Sep 29 '12 at 8:30












I guess any will do as long as the job gets done. I would prefer towards the solution that is simple and concise.
– TrustyCoder
Sep 29 '12 at 13:45




I guess any will do as long as the job gets done. I would prefer towards the solution that is simple and concise.
– TrustyCoder
Sep 29 '12 at 13:45












3 Answers
3






active

oldest

votes

















up vote
7
down vote













The easiest way in C++ would be, IMO, to implement an event sink with help of ATL's IDispEventImpl and IDispEventSimpleImpl templates. An explanation with sample project can be found here.



There are many online resources about how to do this, e.g. this or this, but here is the list of required steps:



First let's take a look at managed side.



In order to provide events, we must do the following:



  • declare an event interface (IDispatch-based)

  • mark the coclass with ComSourceInterfaces attribute to bind the event interface to coclass

  • implement matching events in the coclass

Here are is the managed code:



[ComVisible(true), 
Guid("D6D3565F-..."),
InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] //! must be IDispatch
public interface IMyEvents

[DispId(1)] // the dispid is used to correctly map the events
void SomethingHappened(DateTime timestamp, string message);


[ComVisible(true)]
[Guid("E22E64F7-...")]
[ProgId("...")]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(IMyEvents))] // binding the event interface
public class MyComServer : IMyComServer

// here we declare the delegate for the event
[ComVisible(false)]
public delegate void MyEventHandler(DateTime timestamp, string message);

// and a public event which matches the method in IMyEvents
// your code will raise this event when needed
public event MyEventHandler SomethingHappened;
...



Now, back to unmanaged side. I will use ATL, as I find it the most effective way to write COM clients, but you can try MFC or do it 'manually'.



The following steps are required:



  • the sink will inherit IDispEventSimpleImpl (or IDispEventImpl)

  • sink map with all the needed methods is declared

  • handler methods are written for each event

  • the sink is registered with event source

  • eventually, when not needed anymore, the sink is disconnected

Here's the code in an ATL C++ client:



// import the typelib of your COM server
// 'named_guids' ensures friendly ID of event interface
#import "myserver.tlb" named_guids
const UINT SINK_ID = 234231341; // we need some sink id

class MyClient : public IDispEventSimpleImpl<SINK_ID, MyClient, &MyServer::DIID_IMyEvents >


Now, we need to define the type info members in the cpp file (i.e. the someEvent instance from example above):



_ATL_FUNC_INFO MyClient::traceEvent = CC_STDCALL, VT_EMPTY, 2 , VT_DECIMAL, VT_BSTR ; // dispid = 1
^ ^ ^ ^
// calling convention (always stdcall) --------+ | | |
// type of return value (only VT_EMPTY makes sense) ----+ | |
// number of parameters to the event -------------------------+ |
// Variant types of event arguments -----------------------------------------+


This can be tricky as type mappings are not always obvious (e.g. a it might be clear that managed int maps to VT_I4, but it is less obvious that DateTime maps to VT_DECIMAL).
You need to declare each event you plan to use in the sink map - if you don't need all of them, don't map them.



Now you need to connect your sink to the event source:



// IUnknown* pUnk = interface to you COM server instance
pMyClient->DispEventAdvise(pUnk);
// .. from this point, events will be caught by the client
// when you are done, disconnect:
pMyClient->DispEventUnadvise(pUnk);


This is it, more or less. Using IDispEventImpl instead of IDispEventSimpleImpl results in a bit less code, as you don't need to supply the type info objects which might be the ugliest part. However, it has two drawbacks:



  • requires access to the typelib (as it needs to read the interface metadata in order to provide the type info itself)

  • is a bit slower (but I would guess not significantly)





share|improve this answer





























    up vote
    0
    down vote













    If you can use C++/CLI you can just do (source):



    // class that defines methods that will called when event occurs
    ref class EventReceiver
    public:
    void OnMyClick(int i, double d)
    Console::WriteLine("OnClick: 0, 1", i, d);


    void OnMyDblClick(String^ str)
    Console::WriteLine("OnDblClick: 0", str);

    ;

    int main()
    EventSource ^ MyEventSource = gcnew EventSource();
    EventReceiver^ MyEventReceiver = gcnew EventReceiver();

    // hook handler to event
    MyEventSource->OnClick += gcnew ClickEventHandler(MyEventReceiver, &EventReceiver::OnMyClick);






    share|improve this answer


















    • 2




      It needs to be unmanged.
      – Hans Passant
      Sep 28 '12 at 23:02

















    up vote
    0
    down vote













    The solution proposed by Zdeslav Vojkovic was almost the complete answer for me, however I ran into stability issues, when implementing the sink in an Outlook add-in. After the first unadvise the system became unstable and could crash at the next advise. The solution for me was to make the sink as a COM object.



    For brevity I have only added the ATL side of the solution, as the managed code can be left exactly as Zdeslav Vojkovic proposed.



    I followed these steps in Visual Studio 2017:



    Create ATL Simple Object



    1. Right-click on Project and select Add > New Item...

    2. Select ATL Simple Object and click the Add button

    3. In the Other tab select "Single" for Threading model

    4. Click Finish

    Fill in the sink interface details



    In the generated idl add handlers for initialization and shutdown:



    [
    object,
    uuid(a5211fba-...),
    dual,
    nonextensible,
    pointer_default(unique)
    ]
    interface IMyClient : IDispatch

    [id(1), helpstring("method InitHandler"), local] HRESULT InitHandler(IUnknown* myserver);
    [id(2), helpstring("method ShutdownHandler"), local] HRESULT ShutdownHandler(void);
    ;


    Fill in the header file (MyClient.h)



    const UINT SINK_ID = 234231341;
    extern _ATL_FUNC_INFO SomethingHappenedInfo;

    // LIBID_MyATLComLib should point to the LIBID of the type library
    class ATL_NO_VTABLE CMyClient :
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<CMyClient, &CLSID_MyClient>,
    public IDispatchImpl<IMyClient, &IID_IMyClient, &LIBID_MyATLComLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
    public IDispEventSimpleImpl<SINK_ID, CMyClient, &MyServer::DIID_IMyEvents>

    public:

    typedef IDispEventSimpleImpl</*nID =*/ SINK_ID, CMyClient, &MyServer::DIID_IMyEvents> SomethingHappenedEvent;

    ...

    BEGIN_SINK_MAP(CMyClient)
    SINK_ENTRY_INFO(SINK_ID, MyServer::DIID_IMyEvents, 0x1, OnSomethingHappened, &SomethingHappenedInfo)
    END_SINK_MAP()

    ...

    private:
    CComQIPtr<MyServer::IMyComServer> mMyComServer;

    public:
    STDMETHOD(InitHandler)(IUnknown* myserver);
    STDMETHOD(ShutdownHandler)(void);

    void __stdcall OnSomethingHappened(DateTime timestamp, string message);
    ;


    Some of the generated code has been left as "..."



    Fill in the C++ code (MyClient.cpp)



    _ATL_FUNC_INFO SomethingHappenedInfo = CC_STDCALL, VT_EMPTY, 2 , VT_DECIMAL, VT_BSTR ;


    STDMETHODIMP CMyClient::InitHandler(IUnknown* myserver)

    this->mMyComServer = myserver;
    SomethingHappenedEvent::DispEventAdvise((IDispatch*)this->mMyComServer);

    return S_OK;



    STDMETHODIMP CMyClient::ShutdownHandler(void)

    SomethingHappenedEvent::DispEventUnadvise(this->mMyComServer);

    return S_OK;


    void __stdcall CMyClient::OnSomethingHappened(DateTime timestamp, string message)

    ...



    Notice that the Advise/Unadvise calls are done differently here.






    share|improve this answer




















      Your Answer






      StackExchange.ifUsing("editor", function ()
      StackExchange.using("externalEditor", function ()
      StackExchange.using("snippets", function ()
      StackExchange.snippets.init();
      );
      );
      , "code-snippets");

      StackExchange.ready(function()
      var channelOptions =
      tags: "".split(" "),
      id: "1"
      ;
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function()
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled)
      StackExchange.using("snippets", function()
      createEditor();
      );

      else
      createEditor();

      );

      function createEditor()
      StackExchange.prepareEditor(
      heartbeatType: 'answer',
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader:
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      ,
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      );



      );













      draft saved

      draft discarded


















      StackExchange.ready(
      function ()
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f12648212%2fexposing-managed-events-to-com-using-c%23new-answer', 'question_page');

      );

      Post as a guest















      Required, but never shown

























      3 Answers
      3






      active

      oldest

      votes








      3 Answers
      3






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes








      up vote
      7
      down vote













      The easiest way in C++ would be, IMO, to implement an event sink with help of ATL's IDispEventImpl and IDispEventSimpleImpl templates. An explanation with sample project can be found here.



      There are many online resources about how to do this, e.g. this or this, but here is the list of required steps:



      First let's take a look at managed side.



      In order to provide events, we must do the following:



      • declare an event interface (IDispatch-based)

      • mark the coclass with ComSourceInterfaces attribute to bind the event interface to coclass

      • implement matching events in the coclass

      Here are is the managed code:



      [ComVisible(true), 
      Guid("D6D3565F-..."),
      InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] //! must be IDispatch
      public interface IMyEvents

      [DispId(1)] // the dispid is used to correctly map the events
      void SomethingHappened(DateTime timestamp, string message);


      [ComVisible(true)]
      [Guid("E22E64F7-...")]
      [ProgId("...")]
      [ClassInterface(ClassInterfaceType.None)]
      [ComSourceInterfaces(typeof(IMyEvents))] // binding the event interface
      public class MyComServer : IMyComServer

      // here we declare the delegate for the event
      [ComVisible(false)]
      public delegate void MyEventHandler(DateTime timestamp, string message);

      // and a public event which matches the method in IMyEvents
      // your code will raise this event when needed
      public event MyEventHandler SomethingHappened;
      ...



      Now, back to unmanaged side. I will use ATL, as I find it the most effective way to write COM clients, but you can try MFC or do it 'manually'.



      The following steps are required:



      • the sink will inherit IDispEventSimpleImpl (or IDispEventImpl)

      • sink map with all the needed methods is declared

      • handler methods are written for each event

      • the sink is registered with event source

      • eventually, when not needed anymore, the sink is disconnected

      Here's the code in an ATL C++ client:



      // import the typelib of your COM server
      // 'named_guids' ensures friendly ID of event interface
      #import "myserver.tlb" named_guids
      const UINT SINK_ID = 234231341; // we need some sink id

      class MyClient : public IDispEventSimpleImpl<SINK_ID, MyClient, &MyServer::DIID_IMyEvents >


      Now, we need to define the type info members in the cpp file (i.e. the someEvent instance from example above):



      _ATL_FUNC_INFO MyClient::traceEvent = CC_STDCALL, VT_EMPTY, 2 , VT_DECIMAL, VT_BSTR ; // dispid = 1
      ^ ^ ^ ^
      // calling convention (always stdcall) --------+ | | |
      // type of return value (only VT_EMPTY makes sense) ----+ | |
      // number of parameters to the event -------------------------+ |
      // Variant types of event arguments -----------------------------------------+


      This can be tricky as type mappings are not always obvious (e.g. a it might be clear that managed int maps to VT_I4, but it is less obvious that DateTime maps to VT_DECIMAL).
      You need to declare each event you plan to use in the sink map - if you don't need all of them, don't map them.



      Now you need to connect your sink to the event source:



      // IUnknown* pUnk = interface to you COM server instance
      pMyClient->DispEventAdvise(pUnk);
      // .. from this point, events will be caught by the client
      // when you are done, disconnect:
      pMyClient->DispEventUnadvise(pUnk);


      This is it, more or less. Using IDispEventImpl instead of IDispEventSimpleImpl results in a bit less code, as you don't need to supply the type info objects which might be the ugliest part. However, it has two drawbacks:



      • requires access to the typelib (as it needs to read the interface metadata in order to provide the type info itself)

      • is a bit slower (but I would guess not significantly)





      share|improve this answer


























        up vote
        7
        down vote













        The easiest way in C++ would be, IMO, to implement an event sink with help of ATL's IDispEventImpl and IDispEventSimpleImpl templates. An explanation with sample project can be found here.



        There are many online resources about how to do this, e.g. this or this, but here is the list of required steps:



        First let's take a look at managed side.



        In order to provide events, we must do the following:



        • declare an event interface (IDispatch-based)

        • mark the coclass with ComSourceInterfaces attribute to bind the event interface to coclass

        • implement matching events in the coclass

        Here are is the managed code:



        [ComVisible(true), 
        Guid("D6D3565F-..."),
        InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] //! must be IDispatch
        public interface IMyEvents

        [DispId(1)] // the dispid is used to correctly map the events
        void SomethingHappened(DateTime timestamp, string message);


        [ComVisible(true)]
        [Guid("E22E64F7-...")]
        [ProgId("...")]
        [ClassInterface(ClassInterfaceType.None)]
        [ComSourceInterfaces(typeof(IMyEvents))] // binding the event interface
        public class MyComServer : IMyComServer

        // here we declare the delegate for the event
        [ComVisible(false)]
        public delegate void MyEventHandler(DateTime timestamp, string message);

        // and a public event which matches the method in IMyEvents
        // your code will raise this event when needed
        public event MyEventHandler SomethingHappened;
        ...



        Now, back to unmanaged side. I will use ATL, as I find it the most effective way to write COM clients, but you can try MFC or do it 'manually'.



        The following steps are required:



        • the sink will inherit IDispEventSimpleImpl (or IDispEventImpl)

        • sink map with all the needed methods is declared

        • handler methods are written for each event

        • the sink is registered with event source

        • eventually, when not needed anymore, the sink is disconnected

        Here's the code in an ATL C++ client:



        // import the typelib of your COM server
        // 'named_guids' ensures friendly ID of event interface
        #import "myserver.tlb" named_guids
        const UINT SINK_ID = 234231341; // we need some sink id

        class MyClient : public IDispEventSimpleImpl<SINK_ID, MyClient, &MyServer::DIID_IMyEvents >


        Now, we need to define the type info members in the cpp file (i.e. the someEvent instance from example above):



        _ATL_FUNC_INFO MyClient::traceEvent = CC_STDCALL, VT_EMPTY, 2 , VT_DECIMAL, VT_BSTR ; // dispid = 1
        ^ ^ ^ ^
        // calling convention (always stdcall) --------+ | | |
        // type of return value (only VT_EMPTY makes sense) ----+ | |
        // number of parameters to the event -------------------------+ |
        // Variant types of event arguments -----------------------------------------+


        This can be tricky as type mappings are not always obvious (e.g. a it might be clear that managed int maps to VT_I4, but it is less obvious that DateTime maps to VT_DECIMAL).
        You need to declare each event you plan to use in the sink map - if you don't need all of them, don't map them.



        Now you need to connect your sink to the event source:



        // IUnknown* pUnk = interface to you COM server instance
        pMyClient->DispEventAdvise(pUnk);
        // .. from this point, events will be caught by the client
        // when you are done, disconnect:
        pMyClient->DispEventUnadvise(pUnk);


        This is it, more or less. Using IDispEventImpl instead of IDispEventSimpleImpl results in a bit less code, as you don't need to supply the type info objects which might be the ugliest part. However, it has two drawbacks:



        • requires access to the typelib (as it needs to read the interface metadata in order to provide the type info itself)

        • is a bit slower (but I would guess not significantly)





        share|improve this answer
























          up vote
          7
          down vote










          up vote
          7
          down vote









          The easiest way in C++ would be, IMO, to implement an event sink with help of ATL's IDispEventImpl and IDispEventSimpleImpl templates. An explanation with sample project can be found here.



          There are many online resources about how to do this, e.g. this or this, but here is the list of required steps:



          First let's take a look at managed side.



          In order to provide events, we must do the following:



          • declare an event interface (IDispatch-based)

          • mark the coclass with ComSourceInterfaces attribute to bind the event interface to coclass

          • implement matching events in the coclass

          Here are is the managed code:



          [ComVisible(true), 
          Guid("D6D3565F-..."),
          InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] //! must be IDispatch
          public interface IMyEvents

          [DispId(1)] // the dispid is used to correctly map the events
          void SomethingHappened(DateTime timestamp, string message);


          [ComVisible(true)]
          [Guid("E22E64F7-...")]
          [ProgId("...")]
          [ClassInterface(ClassInterfaceType.None)]
          [ComSourceInterfaces(typeof(IMyEvents))] // binding the event interface
          public class MyComServer : IMyComServer

          // here we declare the delegate for the event
          [ComVisible(false)]
          public delegate void MyEventHandler(DateTime timestamp, string message);

          // and a public event which matches the method in IMyEvents
          // your code will raise this event when needed
          public event MyEventHandler SomethingHappened;
          ...



          Now, back to unmanaged side. I will use ATL, as I find it the most effective way to write COM clients, but you can try MFC or do it 'manually'.



          The following steps are required:



          • the sink will inherit IDispEventSimpleImpl (or IDispEventImpl)

          • sink map with all the needed methods is declared

          • handler methods are written for each event

          • the sink is registered with event source

          • eventually, when not needed anymore, the sink is disconnected

          Here's the code in an ATL C++ client:



          // import the typelib of your COM server
          // 'named_guids' ensures friendly ID of event interface
          #import "myserver.tlb" named_guids
          const UINT SINK_ID = 234231341; // we need some sink id

          class MyClient : public IDispEventSimpleImpl<SINK_ID, MyClient, &MyServer::DIID_IMyEvents >


          Now, we need to define the type info members in the cpp file (i.e. the someEvent instance from example above):



          _ATL_FUNC_INFO MyClient::traceEvent = CC_STDCALL, VT_EMPTY, 2 , VT_DECIMAL, VT_BSTR ; // dispid = 1
          ^ ^ ^ ^
          // calling convention (always stdcall) --------+ | | |
          // type of return value (only VT_EMPTY makes sense) ----+ | |
          // number of parameters to the event -------------------------+ |
          // Variant types of event arguments -----------------------------------------+


          This can be tricky as type mappings are not always obvious (e.g. a it might be clear that managed int maps to VT_I4, but it is less obvious that DateTime maps to VT_DECIMAL).
          You need to declare each event you plan to use in the sink map - if you don't need all of them, don't map them.



          Now you need to connect your sink to the event source:



          // IUnknown* pUnk = interface to you COM server instance
          pMyClient->DispEventAdvise(pUnk);
          // .. from this point, events will be caught by the client
          // when you are done, disconnect:
          pMyClient->DispEventUnadvise(pUnk);


          This is it, more or less. Using IDispEventImpl instead of IDispEventSimpleImpl results in a bit less code, as you don't need to supply the type info objects which might be the ugliest part. However, it has two drawbacks:



          • requires access to the typelib (as it needs to read the interface metadata in order to provide the type info itself)

          • is a bit slower (but I would guess not significantly)





          share|improve this answer














          The easiest way in C++ would be, IMO, to implement an event sink with help of ATL's IDispEventImpl and IDispEventSimpleImpl templates. An explanation with sample project can be found here.



          There are many online resources about how to do this, e.g. this or this, but here is the list of required steps:



          First let's take a look at managed side.



          In order to provide events, we must do the following:



          • declare an event interface (IDispatch-based)

          • mark the coclass with ComSourceInterfaces attribute to bind the event interface to coclass

          • implement matching events in the coclass

          Here are is the managed code:



          [ComVisible(true), 
          Guid("D6D3565F-..."),
          InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] //! must be IDispatch
          public interface IMyEvents

          [DispId(1)] // the dispid is used to correctly map the events
          void SomethingHappened(DateTime timestamp, string message);


          [ComVisible(true)]
          [Guid("E22E64F7-...")]
          [ProgId("...")]
          [ClassInterface(ClassInterfaceType.None)]
          [ComSourceInterfaces(typeof(IMyEvents))] // binding the event interface
          public class MyComServer : IMyComServer

          // here we declare the delegate for the event
          [ComVisible(false)]
          public delegate void MyEventHandler(DateTime timestamp, string message);

          // and a public event which matches the method in IMyEvents
          // your code will raise this event when needed
          public event MyEventHandler SomethingHappened;
          ...



          Now, back to unmanaged side. I will use ATL, as I find it the most effective way to write COM clients, but you can try MFC or do it 'manually'.



          The following steps are required:



          • the sink will inherit IDispEventSimpleImpl (or IDispEventImpl)

          • sink map with all the needed methods is declared

          • handler methods are written for each event

          • the sink is registered with event source

          • eventually, when not needed anymore, the sink is disconnected

          Here's the code in an ATL C++ client:



          // import the typelib of your COM server
          // 'named_guids' ensures friendly ID of event interface
          #import "myserver.tlb" named_guids
          const UINT SINK_ID = 234231341; // we need some sink id

          class MyClient : public IDispEventSimpleImpl<SINK_ID, MyClient, &MyServer::DIID_IMyEvents >


          Now, we need to define the type info members in the cpp file (i.e. the someEvent instance from example above):



          _ATL_FUNC_INFO MyClient::traceEvent = CC_STDCALL, VT_EMPTY, 2 , VT_DECIMAL, VT_BSTR ; // dispid = 1
          ^ ^ ^ ^
          // calling convention (always stdcall) --------+ | | |
          // type of return value (only VT_EMPTY makes sense) ----+ | |
          // number of parameters to the event -------------------------+ |
          // Variant types of event arguments -----------------------------------------+


          This can be tricky as type mappings are not always obvious (e.g. a it might be clear that managed int maps to VT_I4, but it is less obvious that DateTime maps to VT_DECIMAL).
          You need to declare each event you plan to use in the sink map - if you don't need all of them, don't map them.



          Now you need to connect your sink to the event source:



          // IUnknown* pUnk = interface to you COM server instance
          pMyClient->DispEventAdvise(pUnk);
          // .. from this point, events will be caught by the client
          // when you are done, disconnect:
          pMyClient->DispEventUnadvise(pUnk);


          This is it, more or less. Using IDispEventImpl instead of IDispEventSimpleImpl results in a bit less code, as you don't need to supply the type info objects which might be the ugliest part. However, it has two drawbacks:



          • requires access to the typelib (as it needs to read the interface metadata in order to provide the type info itself)

          • is a bit slower (but I would guess not significantly)






          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Oct 1 '12 at 9:49

























          answered Sep 29 '12 at 14:03









          Zdeslav Vojkovic

          12.5k1836




          12.5k1836






















              up vote
              0
              down vote













              If you can use C++/CLI you can just do (source):



              // class that defines methods that will called when event occurs
              ref class EventReceiver
              public:
              void OnMyClick(int i, double d)
              Console::WriteLine("OnClick: 0, 1", i, d);


              void OnMyDblClick(String^ str)
              Console::WriteLine("OnDblClick: 0", str);

              ;

              int main()
              EventSource ^ MyEventSource = gcnew EventSource();
              EventReceiver^ MyEventReceiver = gcnew EventReceiver();

              // hook handler to event
              MyEventSource->OnClick += gcnew ClickEventHandler(MyEventReceiver, &EventReceiver::OnMyClick);






              share|improve this answer


















              • 2




                It needs to be unmanged.
                – Hans Passant
                Sep 28 '12 at 23:02














              up vote
              0
              down vote













              If you can use C++/CLI you can just do (source):



              // class that defines methods that will called when event occurs
              ref class EventReceiver
              public:
              void OnMyClick(int i, double d)
              Console::WriteLine("OnClick: 0, 1", i, d);


              void OnMyDblClick(String^ str)
              Console::WriteLine("OnDblClick: 0", str);

              ;

              int main()
              EventSource ^ MyEventSource = gcnew EventSource();
              EventReceiver^ MyEventReceiver = gcnew EventReceiver();

              // hook handler to event
              MyEventSource->OnClick += gcnew ClickEventHandler(MyEventReceiver, &EventReceiver::OnMyClick);






              share|improve this answer


















              • 2




                It needs to be unmanged.
                – Hans Passant
                Sep 28 '12 at 23:02












              up vote
              0
              down vote










              up vote
              0
              down vote









              If you can use C++/CLI you can just do (source):



              // class that defines methods that will called when event occurs
              ref class EventReceiver
              public:
              void OnMyClick(int i, double d)
              Console::WriteLine("OnClick: 0, 1", i, d);


              void OnMyDblClick(String^ str)
              Console::WriteLine("OnDblClick: 0", str);

              ;

              int main()
              EventSource ^ MyEventSource = gcnew EventSource();
              EventReceiver^ MyEventReceiver = gcnew EventReceiver();

              // hook handler to event
              MyEventSource->OnClick += gcnew ClickEventHandler(MyEventReceiver, &EventReceiver::OnMyClick);






              share|improve this answer














              If you can use C++/CLI you can just do (source):



              // class that defines methods that will called when event occurs
              ref class EventReceiver
              public:
              void OnMyClick(int i, double d)
              Console::WriteLine("OnClick: 0, 1", i, d);


              void OnMyDblClick(String^ str)
              Console::WriteLine("OnDblClick: 0", str);

              ;

              int main()
              EventSource ^ MyEventSource = gcnew EventSource();
              EventReceiver^ MyEventReceiver = gcnew EventReceiver();

              // hook handler to event
              MyEventSource->OnClick += gcnew ClickEventHandler(MyEventReceiver, &EventReceiver::OnMyClick);







              share|improve this answer














              share|improve this answer



              share|improve this answer








              edited Sep 29 '12 at 0:32









              Tamás Szelei

              13.9k1377146




              13.9k1377146










              answered Sep 28 '12 at 22:38









              CrazyCasta

              16.8k33259




              16.8k33259







              • 2




                It needs to be unmanged.
                – Hans Passant
                Sep 28 '12 at 23:02












              • 2




                It needs to be unmanged.
                – Hans Passant
                Sep 28 '12 at 23:02







              2




              2




              It needs to be unmanged.
              – Hans Passant
              Sep 28 '12 at 23:02




              It needs to be unmanged.
              – Hans Passant
              Sep 28 '12 at 23:02










              up vote
              0
              down vote













              The solution proposed by Zdeslav Vojkovic was almost the complete answer for me, however I ran into stability issues, when implementing the sink in an Outlook add-in. After the first unadvise the system became unstable and could crash at the next advise. The solution for me was to make the sink as a COM object.



              For brevity I have only added the ATL side of the solution, as the managed code can be left exactly as Zdeslav Vojkovic proposed.



              I followed these steps in Visual Studio 2017:



              Create ATL Simple Object



              1. Right-click on Project and select Add > New Item...

              2. Select ATL Simple Object and click the Add button

              3. In the Other tab select "Single" for Threading model

              4. Click Finish

              Fill in the sink interface details



              In the generated idl add handlers for initialization and shutdown:



              [
              object,
              uuid(a5211fba-...),
              dual,
              nonextensible,
              pointer_default(unique)
              ]
              interface IMyClient : IDispatch

              [id(1), helpstring("method InitHandler"), local] HRESULT InitHandler(IUnknown* myserver);
              [id(2), helpstring("method ShutdownHandler"), local] HRESULT ShutdownHandler(void);
              ;


              Fill in the header file (MyClient.h)



              const UINT SINK_ID = 234231341;
              extern _ATL_FUNC_INFO SomethingHappenedInfo;

              // LIBID_MyATLComLib should point to the LIBID of the type library
              class ATL_NO_VTABLE CMyClient :
              public CComObjectRootEx<CComSingleThreadModel>,
              public CComCoClass<CMyClient, &CLSID_MyClient>,
              public IDispatchImpl<IMyClient, &IID_IMyClient, &LIBID_MyATLComLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
              public IDispEventSimpleImpl<SINK_ID, CMyClient, &MyServer::DIID_IMyEvents>

              public:

              typedef IDispEventSimpleImpl</*nID =*/ SINK_ID, CMyClient, &MyServer::DIID_IMyEvents> SomethingHappenedEvent;

              ...

              BEGIN_SINK_MAP(CMyClient)
              SINK_ENTRY_INFO(SINK_ID, MyServer::DIID_IMyEvents, 0x1, OnSomethingHappened, &SomethingHappenedInfo)
              END_SINK_MAP()

              ...

              private:
              CComQIPtr<MyServer::IMyComServer> mMyComServer;

              public:
              STDMETHOD(InitHandler)(IUnknown* myserver);
              STDMETHOD(ShutdownHandler)(void);

              void __stdcall OnSomethingHappened(DateTime timestamp, string message);
              ;


              Some of the generated code has been left as "..."



              Fill in the C++ code (MyClient.cpp)



              _ATL_FUNC_INFO SomethingHappenedInfo = CC_STDCALL, VT_EMPTY, 2 , VT_DECIMAL, VT_BSTR ;


              STDMETHODIMP CMyClient::InitHandler(IUnknown* myserver)

              this->mMyComServer = myserver;
              SomethingHappenedEvent::DispEventAdvise((IDispatch*)this->mMyComServer);

              return S_OK;



              STDMETHODIMP CMyClient::ShutdownHandler(void)

              SomethingHappenedEvent::DispEventUnadvise(this->mMyComServer);

              return S_OK;


              void __stdcall CMyClient::OnSomethingHappened(DateTime timestamp, string message)

              ...



              Notice that the Advise/Unadvise calls are done differently here.






              share|improve this answer
























                up vote
                0
                down vote













                The solution proposed by Zdeslav Vojkovic was almost the complete answer for me, however I ran into stability issues, when implementing the sink in an Outlook add-in. After the first unadvise the system became unstable and could crash at the next advise. The solution for me was to make the sink as a COM object.



                For brevity I have only added the ATL side of the solution, as the managed code can be left exactly as Zdeslav Vojkovic proposed.



                I followed these steps in Visual Studio 2017:



                Create ATL Simple Object



                1. Right-click on Project and select Add > New Item...

                2. Select ATL Simple Object and click the Add button

                3. In the Other tab select "Single" for Threading model

                4. Click Finish

                Fill in the sink interface details



                In the generated idl add handlers for initialization and shutdown:



                [
                object,
                uuid(a5211fba-...),
                dual,
                nonextensible,
                pointer_default(unique)
                ]
                interface IMyClient : IDispatch

                [id(1), helpstring("method InitHandler"), local] HRESULT InitHandler(IUnknown* myserver);
                [id(2), helpstring("method ShutdownHandler"), local] HRESULT ShutdownHandler(void);
                ;


                Fill in the header file (MyClient.h)



                const UINT SINK_ID = 234231341;
                extern _ATL_FUNC_INFO SomethingHappenedInfo;

                // LIBID_MyATLComLib should point to the LIBID of the type library
                class ATL_NO_VTABLE CMyClient :
                public CComObjectRootEx<CComSingleThreadModel>,
                public CComCoClass<CMyClient, &CLSID_MyClient>,
                public IDispatchImpl<IMyClient, &IID_IMyClient, &LIBID_MyATLComLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
                public IDispEventSimpleImpl<SINK_ID, CMyClient, &MyServer::DIID_IMyEvents>

                public:

                typedef IDispEventSimpleImpl</*nID =*/ SINK_ID, CMyClient, &MyServer::DIID_IMyEvents> SomethingHappenedEvent;

                ...

                BEGIN_SINK_MAP(CMyClient)
                SINK_ENTRY_INFO(SINK_ID, MyServer::DIID_IMyEvents, 0x1, OnSomethingHappened, &SomethingHappenedInfo)
                END_SINK_MAP()

                ...

                private:
                CComQIPtr<MyServer::IMyComServer> mMyComServer;

                public:
                STDMETHOD(InitHandler)(IUnknown* myserver);
                STDMETHOD(ShutdownHandler)(void);

                void __stdcall OnSomethingHappened(DateTime timestamp, string message);
                ;


                Some of the generated code has been left as "..."



                Fill in the C++ code (MyClient.cpp)



                _ATL_FUNC_INFO SomethingHappenedInfo = CC_STDCALL, VT_EMPTY, 2 , VT_DECIMAL, VT_BSTR ;


                STDMETHODIMP CMyClient::InitHandler(IUnknown* myserver)

                this->mMyComServer = myserver;
                SomethingHappenedEvent::DispEventAdvise((IDispatch*)this->mMyComServer);

                return S_OK;



                STDMETHODIMP CMyClient::ShutdownHandler(void)

                SomethingHappenedEvent::DispEventUnadvise(this->mMyComServer);

                return S_OK;


                void __stdcall CMyClient::OnSomethingHappened(DateTime timestamp, string message)

                ...



                Notice that the Advise/Unadvise calls are done differently here.






                share|improve this answer






















                  up vote
                  0
                  down vote










                  up vote
                  0
                  down vote









                  The solution proposed by Zdeslav Vojkovic was almost the complete answer for me, however I ran into stability issues, when implementing the sink in an Outlook add-in. After the first unadvise the system became unstable and could crash at the next advise. The solution for me was to make the sink as a COM object.



                  For brevity I have only added the ATL side of the solution, as the managed code can be left exactly as Zdeslav Vojkovic proposed.



                  I followed these steps in Visual Studio 2017:



                  Create ATL Simple Object



                  1. Right-click on Project and select Add > New Item...

                  2. Select ATL Simple Object and click the Add button

                  3. In the Other tab select "Single" for Threading model

                  4. Click Finish

                  Fill in the sink interface details



                  In the generated idl add handlers for initialization and shutdown:



                  [
                  object,
                  uuid(a5211fba-...),
                  dual,
                  nonextensible,
                  pointer_default(unique)
                  ]
                  interface IMyClient : IDispatch

                  [id(1), helpstring("method InitHandler"), local] HRESULT InitHandler(IUnknown* myserver);
                  [id(2), helpstring("method ShutdownHandler"), local] HRESULT ShutdownHandler(void);
                  ;


                  Fill in the header file (MyClient.h)



                  const UINT SINK_ID = 234231341;
                  extern _ATL_FUNC_INFO SomethingHappenedInfo;

                  // LIBID_MyATLComLib should point to the LIBID of the type library
                  class ATL_NO_VTABLE CMyClient :
                  public CComObjectRootEx<CComSingleThreadModel>,
                  public CComCoClass<CMyClient, &CLSID_MyClient>,
                  public IDispatchImpl<IMyClient, &IID_IMyClient, &LIBID_MyATLComLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
                  public IDispEventSimpleImpl<SINK_ID, CMyClient, &MyServer::DIID_IMyEvents>

                  public:

                  typedef IDispEventSimpleImpl</*nID =*/ SINK_ID, CMyClient, &MyServer::DIID_IMyEvents> SomethingHappenedEvent;

                  ...

                  BEGIN_SINK_MAP(CMyClient)
                  SINK_ENTRY_INFO(SINK_ID, MyServer::DIID_IMyEvents, 0x1, OnSomethingHappened, &SomethingHappenedInfo)
                  END_SINK_MAP()

                  ...

                  private:
                  CComQIPtr<MyServer::IMyComServer> mMyComServer;

                  public:
                  STDMETHOD(InitHandler)(IUnknown* myserver);
                  STDMETHOD(ShutdownHandler)(void);

                  void __stdcall OnSomethingHappened(DateTime timestamp, string message);
                  ;


                  Some of the generated code has been left as "..."



                  Fill in the C++ code (MyClient.cpp)



                  _ATL_FUNC_INFO SomethingHappenedInfo = CC_STDCALL, VT_EMPTY, 2 , VT_DECIMAL, VT_BSTR ;


                  STDMETHODIMP CMyClient::InitHandler(IUnknown* myserver)

                  this->mMyComServer = myserver;
                  SomethingHappenedEvent::DispEventAdvise((IDispatch*)this->mMyComServer);

                  return S_OK;



                  STDMETHODIMP CMyClient::ShutdownHandler(void)

                  SomethingHappenedEvent::DispEventUnadvise(this->mMyComServer);

                  return S_OK;


                  void __stdcall CMyClient::OnSomethingHappened(DateTime timestamp, string message)

                  ...



                  Notice that the Advise/Unadvise calls are done differently here.






                  share|improve this answer












                  The solution proposed by Zdeslav Vojkovic was almost the complete answer for me, however I ran into stability issues, when implementing the sink in an Outlook add-in. After the first unadvise the system became unstable and could crash at the next advise. The solution for me was to make the sink as a COM object.



                  For brevity I have only added the ATL side of the solution, as the managed code can be left exactly as Zdeslav Vojkovic proposed.



                  I followed these steps in Visual Studio 2017:



                  Create ATL Simple Object



                  1. Right-click on Project and select Add > New Item...

                  2. Select ATL Simple Object and click the Add button

                  3. In the Other tab select "Single" for Threading model

                  4. Click Finish

                  Fill in the sink interface details



                  In the generated idl add handlers for initialization and shutdown:



                  [
                  object,
                  uuid(a5211fba-...),
                  dual,
                  nonextensible,
                  pointer_default(unique)
                  ]
                  interface IMyClient : IDispatch

                  [id(1), helpstring("method InitHandler"), local] HRESULT InitHandler(IUnknown* myserver);
                  [id(2), helpstring("method ShutdownHandler"), local] HRESULT ShutdownHandler(void);
                  ;


                  Fill in the header file (MyClient.h)



                  const UINT SINK_ID = 234231341;
                  extern _ATL_FUNC_INFO SomethingHappenedInfo;

                  // LIBID_MyATLComLib should point to the LIBID of the type library
                  class ATL_NO_VTABLE CMyClient :
                  public CComObjectRootEx<CComSingleThreadModel>,
                  public CComCoClass<CMyClient, &CLSID_MyClient>,
                  public IDispatchImpl<IMyClient, &IID_IMyClient, &LIBID_MyATLComLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
                  public IDispEventSimpleImpl<SINK_ID, CMyClient, &MyServer::DIID_IMyEvents>

                  public:

                  typedef IDispEventSimpleImpl</*nID =*/ SINK_ID, CMyClient, &MyServer::DIID_IMyEvents> SomethingHappenedEvent;

                  ...

                  BEGIN_SINK_MAP(CMyClient)
                  SINK_ENTRY_INFO(SINK_ID, MyServer::DIID_IMyEvents, 0x1, OnSomethingHappened, &SomethingHappenedInfo)
                  END_SINK_MAP()

                  ...

                  private:
                  CComQIPtr<MyServer::IMyComServer> mMyComServer;

                  public:
                  STDMETHOD(InitHandler)(IUnknown* myserver);
                  STDMETHOD(ShutdownHandler)(void);

                  void __stdcall OnSomethingHappened(DateTime timestamp, string message);
                  ;


                  Some of the generated code has been left as "..."



                  Fill in the C++ code (MyClient.cpp)



                  _ATL_FUNC_INFO SomethingHappenedInfo = CC_STDCALL, VT_EMPTY, 2 , VT_DECIMAL, VT_BSTR ;


                  STDMETHODIMP CMyClient::InitHandler(IUnknown* myserver)

                  this->mMyComServer = myserver;
                  SomethingHappenedEvent::DispEventAdvise((IDispatch*)this->mMyComServer);

                  return S_OK;



                  STDMETHODIMP CMyClient::ShutdownHandler(void)

                  SomethingHappenedEvent::DispEventUnadvise(this->mMyComServer);

                  return S_OK;


                  void __stdcall CMyClient::OnSomethingHappened(DateTime timestamp, string message)

                  ...



                  Notice that the Advise/Unadvise calls are done differently here.







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Nov 11 at 13:13









                  garac

                  212




                  212



























                      draft saved

                      draft discarded
















































                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid


                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.

                      To learn more, see our tips on writing great answers.





                      Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                      Please pay close attention to the following guidance:


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid


                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.

                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function ()
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f12648212%2fexposing-managed-events-to-com-using-c%23new-answer', 'question_page');

                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      27

                      Top Tejano songwriter Luis Silva dead of heart attack at 64

                      Category:Rhetoric