.Net如何自动生成COM事件处理程序

时间:2016-10-06 19:23:12

标签: .net com

我在MSDN上阅读了这篇文章, How to: Handle Events Raised by a COM Source

通过声明为COM事件源自动生成事件处理程序,简要描述了COM事件处理的内部工作。我一直在努力寻找有关自动生成的详细信息。

真的,我想知道何时调用IConnectionPoint::Advise method。例如,在第一篇文章m_IExplorer.TitleChange += DTitleChangeE;中调用时,是否会调用它?据我了解,这是客户端上最终调用COM对象的Advise()的方法。

那里的任何COM专家可以澄清我的困惑吗?

编辑:我读过这篇文章.NET Delegate Event Model vs COM Connection Points

其中,作者在引用

时指出Effectively, what you are doing here is something that's analogous to the IConnectionPoint::Advise in the COM world
 m_pager.OnAirlineArrivedEvent += 
      new _IAirlineArrivalPagerEvents_OnAirlineArrivedEventEventHandler(OnMyPagerNotify);

这不应该是正在发生的事情吗? .Net生成“+ =”操作,在COM接口的COM连接点上调用IConnectionPoint::Advise

1 个答案:

答案 0 :(得分:1)

怀疑.Net COM事件上的+=运算符最终导致IConnectionPoint::Advise被调用是正确的。

.NET使用tlbimp.exe(Core Common Language Runtime的一部分)生成库中使用的每个COM接口的“托管代码等价物”。参见The .NET and COM Interoperability Handbook的第344页。在单行示例中,m_pager是生成的托管类之一。默认情况下,C#中的事件支持+=运算符,m_pager.OnAirlineArrivedEvent是tlbimp.exe生成的事件。

在生成的托管类中,+ =调用生成的事件的add方法(请参阅本页的中间部分C# In Depth)。踢球者是.Net使用EventProviderWriter(EPW)来生成该事件的添加和删除方法(请参阅下面的讨论以获取更多信息),并且EventProviderWriter构造add方法,使其使用{{3}在IConnectionPointer上。 EPW创建然后将SinkHelper(参见编辑)传递给Advise。然后,SinkHelper将事件从COM服务器转发给委托。请参阅 .NET和COM互操作性手册的第344页。 COM服务器如何调用SinkHandler的确切实现有点棘手,请参阅edit2。

实际上,EventProviderWriter直接编写MSIL(Microsoft中间语言)。 EPW编写add_[EventName]remove_[EventName] IL方法,这是添加和删除访问器在运行时实际调用的方法。 (以及+=-=调用的内容)可以阅读更多有关IConnectionPointer::Advise及其中引用链接的内容。

EventSinkHelperWriter类 - 也用于创建托管代码 - 动态创建SinkHelper对象。它也直接写入MSIL。看起来好像ESHW将COM服务器接口的实现写入接收器。也就是说,它将一个事件方法写入SinkHelper,当调用它时,它将调用存储在其中的委托(组合或单个)。请参阅 .NET和COM互操作性手册的第344页。

我觉得有趣的是,它看起来不像SinkHelper实现IUnknown(IConnectionPointer.Advise()需要)。有一些基于反射的继承,所以也许它就在那里。

编辑此外,看起来ComEventsHelper是“用户”的类,而其他人则在幕后使用。据我所知,每次在事件上调用+=运算符时,都会创建一个新的Sink对象并向COM服务器提供建议( .NET和COM互操作性手册的第354页似乎表明了这一点)。 ComEventsHelper提供了一个方法Combine,每个接收器有多个C#委托实例,但我不能肯定地说。

另一个注意事项:真的,EPW将“SinkHelper”传递给Advise()。 “SinkHelper”的详细信息来源于here。看起来“SinkHelper”实现了COM源接口 - 我希望我的词汇是正确的 - 这样每当引发COM事件时,都会在SinkHelper上调用相应的接口方法;然后,SinkHelper将该事件转发给.Net客户端。但是,这一切都是通过反思完成的,所以我很难跟进它。我仍然不确定SinkHelper如何与ComEventsSink相关的细节。以前我的答案说明了

“在Advise调用中,EPW传入SinkHelperWriter,现在正在”侦听“COM服务器触发事件​​(这在EPW类中并不清楚,但{{3}中的注释3}}似乎表明情况就是这样。)ComEventSink充当ComEventSink的包装器,它进一步包装普通委托。当COM服务器触发事件​​时,ComEventSink实现IDispatch :: Invoke( )由Com服务器调用的方法,最终调用由ComEventsMethod包装的delagate。“

最后,我不拥有这本书,但从上下文线索来看,似乎 .NET和COM互操作性手册的实现具有add_event方法的实现。

Edit2:为了找出SinkHelper是否实现了未知,我被How is the SinkHelper event method invoked by the COM Server分散了注意力。这是一段令人失望的旅程。我假设.Net用于生成其托管代码的类型库是由MIDL编译器编译的(我不知道其他任何方式)。如果是这种情况,那么类型库中应该有一个Fire_[EventName]方法,它在实现IDispatch的类上调用Invoke并存储ConnectionPointerContainer。最终(通过atlcom.h追溯回调)DispCallFunc被调用。这是ComEventsHelper,它显然形成了一些类型强制,最后调用了SinkHelper事件方法。由于它没有文件记录,我的调查结束于此,唯一的答案就是MAGIC。