【问题标题】:How does .Net Automatically Generate a COM Event Handler.Net 如何自动生成 COM 事件处理程序
【发布时间】:2016-10-08 12:01:38
【问题描述】:

我在 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 的“+=”操作?

【问题讨论】:

  • 您混淆了客户端和服务器角色。调用 Advise() 来订阅您公开的事件的是客户端。当您在 ComVisible 类中使用 [ComSourceInterfaces] 属性时,是 CLR 提供 IConnectionPoint 实现。如果您编写的代码使用 COM 服务器,那么您就是客户端,那么 += 会到达 CLR 以调用 Advise()。
  • 我看到我的第三段措辞相当奇怪。我知道客户调用了建议方法。我想知道的是 CLR 提供的用于与 COM 对象交互的 += 运算符的底层发生了什么。 .Net 客户端调用“+=”,我希望最终调用 Advise()。但是,我找不到描述 "+=" 实现的 MSDN 源
  • 参会代表就像是铅做的冰山,你只能看到很尖的顶部。 CLR 中有大量的 C++ 代码来处理它们的实现。您必须阅读 coreclr 源代码才能看到。

标签: .net com


【解决方案1】:

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

.NET 使用 tlbimp.exe(核心公共语言运行时的一部分)生成库中使用的每个 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 方法,以便它在 @ 上使用 IConnectionPointer::Advise 987654336@。 EPW 创建然后将 SinkHelper(见编辑)传递给 Advise。然后 SinkHelper 会将来自 COM 服务器的事件转发给委托。请参阅.NET 和 COM 互操作性手册的第 344 页。 COM 服务器如何调用 SinkHandler 的具体实现有点棘手,请参阅edit2。

实际上,EventProviderWriter 直接编写 MSIL(Microsoft Intermediate Language)。 EPW 编写add_[EventName]remove_[EventName] IL 方法,这是添加和删除访问器在运行时实际调用的内容。 (因此,+=-= 调用了什么)可以阅读更多关于 here 和其中引用的链接。

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

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

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

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

“在 Advise 调用中,EPW 传入 ComEventSink,它现在正在“侦听”COM 服务器以触发事件(这在 EPW 类中并不清楚,但 ComEventsHelper 中的 cmets 似乎表示这种情况)。ComEventSink 充当ComEventsMethod 的包装器,它进一步包装了一个普通委托。当 COM 服务器触发事件​​时,ComEventSink 实现 IDispatch::Invoke() 方法,该方法由Com 服务器,最终调用由 ComEventsMethod 包装的 delagate。”

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

Edit2:为了弄清楚 SinkHelper 是否实现了 unknown,我被 How is the SinkHelper event method invoked by the COM Server 分心了。这是一次令人失望的旅程。我假设 .Net 用于生成其托管代码的类型库是由 MIDL 编译器编译的(我不知道其他任何方式)。如果是这种情况,那么类型库中应该有一个Fire_[EventName] 方法,它在实现IDispatch 并存储ConnectionPointerContainer 的类上调用Invoke。最终(通过 atlcom.h 跟踪调用)DispCallFunc 被调用。这是一个undocumented Windows API call,它显然形成了某种类型的强制并最终调用了 SinkHelper 事件方法。由于它是无证的,所以我的侦查到此结束,关于它如何运作的唯一答案就是 MAGIC。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-08
    • 1970-01-01
    • 1970-01-01
    • 2011-09-05
    • 2011-08-14
    相关资源
    最近更新 更多