从 C# 到 C++ 触发 COM 事件的正确方法是什么
What is the correct way to fire COM events from C# to C++
我们有一个项目包含大量 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++ 中处理事件时才会抛出异常 class。这是使用 ATL class IDispEventSimpleImpl 实现的。我发现 IDispEventSimpleImpl 没有实现函数 GetIDsOfNames。这个函数肯定被调用了,它 returns E_NOTIMPL.
难道新的 C# 代码正在调用函数 GetIDsOfNames,而旧的 C++ 代码没有?
如果是这样,有什么简单的方法可以在使用 IDispEventSimpleImpl 的classes 中实现函数 GetIDsOfNames 吗?
如果我的分析有误,是否有 'correct' 方法在 C#(作为事件源)和 C++(作为事件接收器)中实现事件逻辑?
我认为我已经通过将 IDispEventSimpleImpl 替换为 IDispEventImpl.
来解决问题
这或多或少是一种直接替换,但它确实需要引用类型库。
ATL class 继承了多个基础 class,包括一个定义为 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++(使用 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++ 中处理事件时才会抛出异常 class。这是使用 ATL class IDispEventSimpleImpl 实现的。我发现 IDispEventSimpleImpl 没有实现函数 GetIDsOfNames。这个函数肯定被调用了,它 returns E_NOTIMPL.
难道新的 C# 代码正在调用函数 GetIDsOfNames,而旧的 C++ 代码没有?
如果是这样,有什么简单的方法可以在使用 IDispEventSimpleImpl 的classes 中实现函数 GetIDsOfNames 吗?
如果我的分析有误,是否有 'correct' 方法在 C#(作为事件源)和 C++(作为事件接收器)中实现事件逻辑?
我认为我已经通过将 IDispEventSimpleImpl 替换为 IDispEventImpl.
来解决问题这或多或少是一种直接替换,但它确实需要引用类型库。
ATL class 继承了多个基础 class,包括一个定义为 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 及其主要和次要版本号。
最初,我尝试保留主要和次要版本号,但这导致了类型库未注册异常。
不需要其他更改 - 例如对接收器映射的更改。