Exposing managed events to COM using C++
up vote
1
down vote
favorite
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
add a comment |
up vote
1
down vote
favorite
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
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
add a comment |
up vote
1
down vote
favorite
up vote
1
down vote
favorite
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
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
c# c++ events com atl
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
add a comment |
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
add a comment |
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
ComSourceInterfacesattribute 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(orIDispEventImpl) - 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)
add a comment |
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);
2
It needs to be unmanged.
– Hans Passant
Sep 28 '12 at 23:02
add a comment |
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
- Right-click on Project and select Add > New Item...
- Select ATL Simple Object and click the Add button
- In the Other tab select "Single" for Threading model
- 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.
add a comment |
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
ComSourceInterfacesattribute 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(orIDispEventImpl) - 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)
add a comment |
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
ComSourceInterfacesattribute 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(orIDispEventImpl) - 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)
add a comment |
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
ComSourceInterfacesattribute 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(orIDispEventImpl) - 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)
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
ComSourceInterfacesattribute 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(orIDispEventImpl) - 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)
edited Oct 1 '12 at 9:49
answered Sep 29 '12 at 14:03
Zdeslav Vojkovic
12.5k1836
12.5k1836
add a comment |
add a comment |
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);
2
It needs to be unmanged.
– Hans Passant
Sep 28 '12 at 23:02
add a comment |
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);
2
It needs to be unmanged.
– Hans Passant
Sep 28 '12 at 23:02
add a comment |
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);
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);
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
add a comment |
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
add a comment |
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
- Right-click on Project and select Add > New Item...
- Select ATL Simple Object and click the Add button
- In the Other tab select "Single" for Threading model
- 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.
add a comment |
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
- Right-click on Project and select Add > New Item...
- Select ATL Simple Object and click the Add button
- In the Other tab select "Single" for Threading model
- 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.
add a comment |
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
- Right-click on Project and select Add > New Item...
- Select ATL Simple Object and click the Add button
- In the Other tab select "Single" for Threading model
- 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.
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
- Right-click on Project and select Add > New Item...
- Select ATL Simple Object and click the Add button
- In the Other tab select "Single" for Threading model
- 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.
answered Nov 11 at 13:13
garac
212
212
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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