从 C# 到 C++ 触发 COM 事件的正确方法是啥

Posted

技术标签:

【中文标题】从 C# 到 C++ 触发 COM 事件的正确方法是啥【英文标题】:What is the correct way to fire COM events from C# to C++从 C# 到 C++ 触发 COM 事件的正确方法是什么 【发布时间】:2018-10-12 10:35:35 【问题描述】:

我们有一个项目,其中包含大量 C++(使用 ATL)、VB6 以及最近的 C# 中的遗留代码。

我们最近将一个组件从 C++ 移植到 C#,它会触发由 C++、VB6 和 C# 中的组件处理的事件。 理论上一切正常,但触发事件会产生大量 System.NotImplementedExceptions。

我已经修改了触发事件的代码,以便它单独调用每个事件处理程序,这样一个对象的异常不会阻止它调用下一个对象中的事件处理程序。

if ( GeneratedChannelChange != null )

  // Invoke each handler separately, so that an exception in one invokation does not prevent us calling the next one.
  foreach ( GeneratedChannelChangeEventHandler del in GeneratedChannelChange.GetInvocationList() )
  
    try
    
      del.Invoke ( GenChan, ChangeMask ) ;
    
    catch  ( Exception ) 
  

实际上,ATL 向导生成的原始 C++ 代码也忽略了调用函数返回的错误。

这似乎工作正常,但我对它产生的大量异常并不满意。在调试器中运行时,这也会降低性能。

我认为只有在 C++ 类中处理事件时才会引发异常。这是使用 ATL 类 IDispEventSimpleImpl 实现的。我发现,IDispEventSimpleImpl 没有实现函数GetIDsOfNames。这个函数肯定被调用了,它返回 E_NOTIMPL。

会不会是新的 C# 代码调用了 GetIDsOfNames 函数,而旧的 C++ 代码却没有?

如果是这样,有没有简单的方法在使用IDispEventSimpleImpl的类中实现函数GetIDsOfNames?

如果我的分析是错误的,是否有一种“正确”的方式在 C#(作为事件源)和 C++(作为事件接收器)中实现事件逻辑?

【问题讨论】:

【参考方案1】:

我认为我已经解决了这个问题,将 IDispEventSimpleImpl 替换为 IDispEventImpl

这或多或少是一种直接替代,但它确实需要对类型库的引用。

ATL 类继承了多个基类,其中一个定义为 WorkspaceEventSink。

class ATL_NO_VTABLE CImportChannels :
  ...
  public WorkspaceEventSink,
  ...

   ...

这之前被定义为

typedef IDispEventSimpleImpl< 42, CImportChannels, &DIID_ICs3WorkspaceEvents>        WorkspaceEventSink ;

我已将定义更改为

typedef IDispEventImpl< 42, CImportChannels, &DIID_ICs3WorkspaceEvents, &LIBID_McAnalEventsNET, 1, 0 >        WorkspaceEventSink ;

除了将 IDispEventSimpleImpl 更改为 IDispEventImpl 之外,我还添加了类型库的 GUID 及其主要和次要版本号。

最初,我尝试保留主要和次要版本号,但这会导致类型库未注册异常。

没有必要进行其他更改 - 例如对接收器地图进行更改。

【讨论】:

以上是关于从 C# 到 C++ 触发 COM 事件的正确方法是啥的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 中触发事件并在 C# 中处理它们

WPF MVVM 从 ViewModel 触发事件的正确方法

C#事件触发机制

c++或者C 语言中有没有类似C#中事件委托的机制

C++ 到 C# 回调结构编组

C# SocketAsyncEventArgs 停止触发完成事件