Reputation: 129
I read this article on MSDN, How to: Handle Events Raised by a COM Source.
It briefly describes the inner-working of COM event handling by stating that an Event Handler is automatically generated for a COM event Source. I've been trying to find details on what exactly is being automatically generated.
Really, I'm wondering when IConnectionPoint::Advise method is called. For example, is it called when, in the first article, m_IExplorer.TitleChange += DTitleChangeE;
, is called? As I understand it, this is the method on the client that eventually calls the Advise() of the COM object.
Any COM experts out there that can clarify any confusion I have?
Edit: I read this article .NET Delegate Event Model vs COM Connection Points.
In it, the author states Effectively, what you are doing here is something that's analogous to the IConnectionPoint::Advise in the COM world
in reference to
m_pager.OnAirlineArrivedEvent +=
new _IAirlineArrivalPagerEvents_OnAirlineArrivedEventEventHandler(OnMyPagerNotify);
Shouldn't that be exactly what is happening? .Net generates the "+=" operation that calls IConnectionPoint::Advise
on the COM connection point for the COM interface?
Upvotes: 1
Views: 588
Reputation: 129
The suspicion that the +=
operator on the .Net COM event eventually leads to IConnectionPoint::Advise
being called is correct.
.NET uses tlbimp.exe (part of the Core Common Language Runtime) to generate "managed code equivalents" of each COM interface used in the library. See page 344 of The .NET and COM Interoperability Handbook. In the one-line example provided m_pager
is one of the managed classes generated. By default, Events in C# support the +=
operator and m_pager.OnAirlineArrivedEvent
is an event generated by tlbimp.exe.
In the generated managed class, += invokes the add method of the generated event (See about halfway down this page C# In Depth). The kicker is that EventProviderWriter (EPW) is used by .Net to generate the add and remove methods of that event (See Discussion Below for more info), and EventProviderWriter constructs the add method such that it uses IConnectionPointer::Advise on the IConnectionPointer
. The EPW creates and then passes a SinkHelper (see edit) to Advise. The SinkHelper will then forward events from the COM Server to the delegate. See Page 344 of The .NET and COM Interoperability Handbook. The exact implementation of how the SinkHandler is called by the COM server is a little trickier, see edit2.
Actually, the EventProviderWriter writes direct MSIL (Microsoft Intermediate Language). The EPW writes the add_[EventName]
and the remove_[EventName]
IL methods, which is what the add and remove accessors actually call at runtime. (and thus, what +=
and -=
call) More can be read about that here and referenced links therein.
The EventSinkHelperWriter class--which is also used to create managed code--creates the SinkHelper object dynamically. It too writes directly into MSIL. It looks as though the ESHW writes an implementation of the COM Server interface into the sink. That is, it writes an event method into the SinkHelper, which, when called, will invoke the delegate (combined or single) stored in it. See page 344 of The .NET and COM Interoperability Handbook.
Something I find interesting, is that it doesn't look like SinkHelper implements IUnknown (Which is required by IConnectionPointer.Advise()
). There is some inheritance based on reflection going on so maybe it is there.
Edit: Furthermore, it looks like ComEventsHelper is a class for the 'user' whereas the others are used under the hood. As far as I can tell, each time the +=
operator is called on the event, a new Sink object is created and Advised to the COM server (Page 354 of The .NET and COM Interoperability Handbook seems to indicate this). The ComEventsHelper offers a method, Combine
, to have more than one C# delegate instance per sink but I can't say for sure.
Another Note: Really, a EPW passes a "SinkHelper" into Advise(). The details of the "SinkHelper" are in the source here, SinkHelperWriter. It looks like the "SinkHelper" implements the COM Source Interface --I Hope my vocab is right-- so that whenever the COM Event is raised, the appropriate interface method is called on the SinkHelper; the SinkHelper then forwards that event to the .Net Client. But, this is all done through reflection so I had a hard time following it. I'm still unsure of the details of how the SinkHelper is related to ComEventsSink. Previously my answer stated
"In the Advise call, the EPW passes in a ComEventSink, which is now 'listening' for the COM server to fire events (This wasn't clear from the EPW class, but the comments in ComEventsHelper seem to indicate this is the case). The ComEventSink acts as a wrapper for the ComEventsMethod, which further wraps a plain delegate. When the COM server fires an event, the ComEventSink implements the IDispatch::Invoke() method which is called by the Com server, eventually invoking a delagate wrapped by ComEventsMethod."
Finally, I don't own the book, but from context clues, it would seem that Page 353 of The .NET and COM Interoperability Handbook has an implementation of the add_event method.
Edit2: In an effort to find whether or not SinkHelper implements unknown, I became distracted by How is the SinkHelper event method invoked by the COM Server
. This was a disappointing journey. I assume that the type library that .Net used to generate its managed code was compiled by the MIDL compiler (I don't know of any other way). If that's the case, then there should be a Fire_[EventName]
method in the type library which calls Invoke on a class that implements IDispatch and stores the ConnectionPointerContainer. Eventually (tracing the calls back through atlcom.h) DispCallFunc
is called. This is an undocumented Windows API call which apparently forms some type coercion and finally invokes the SinkHelper event method. Since it is undocumented, my sleuthing ends here, and the only answer to how it all works is MAGIC.
Upvotes: 2