从 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 事件的正确方法是啥的主要内容,如果未能解决你的问题,请参考以下文章