C#COM服务器互操作-System.InvalidCastException 我在C++中创建了一个COM服务器,它服务于特定的设备,并被C++ COM客户机(客户应用程序,没有可用的源)消耗。 客户应用程序需要定期更新,为此,使用了名为Notify()的函数。该函数在非托管COM客户端代码中实现

C#COM服务器互操作-System.InvalidCastException 我在C++中创建了一个COM服务器,它服务于特定的设备,并被C++ COM客户机(客户应用程序,没有可用的源)消耗。 客户应用程序需要定期更新,为此,使用了名为Notify()的函数。该函数在非托管COM客户端代码中实现,c#,c++,com,com-interop,C#,C++,Com,Com Interop,当从创建此通知侦听器对象的同一线程中调用Notify()时,一切正常。但这只能发生一次,因为C++客户端最初只要求一次数据。 即使它只需要一次数据,它似乎也不能容忍一秒钟左右的反应。我的设备反应有点慢,尤其是在网络上,一秒钟之内就能得到某种读数。好的,我明白了,它需要迅速的反应 因此,不仅为了实现定期更新,而且为了减轻主线程等待数据的负担,我创建了一个单独的线程,它每分钟探测一个设备,然后通过调用Notify()将结果报告给非托管代码 当从另一个线程调用Notify()时,我得到一个非常讨厌的S

当从创建此通知侦听器对象的同一线程中调用
Notify()
时,一切正常。但这只能发生一次,因为C++客户端最初只要求一次数据。 即使它只需要一次数据,它似乎也不能容忍一秒钟左右的反应。我的设备反应有点慢,尤其是在网络上,一秒钟之内就能得到某种读数。好的,我明白了,它需要迅速的反应

因此,不仅为了实现定期更新,而且为了减轻主线程等待数据的负担,我创建了一个单独的线程,它每分钟探测一个设备,然后通过调用
Notify()
将结果报告给非托管代码

当从另一个线程调用Notify()时,我得到一个非常讨厌的
System.InvalidCastException
。异常是从CLR中的深层引发的

这是一个生产系统。在我自己开发的系统中,我有自己的C++ COM客户端模型,这个异常在调试会话期间被“代码> CONTXETCHECH死锁< /代码>屏蔽。我可以关闭此选项,但不会改变
Notify()
在跨线程时表现不好的事实。事实上,根据日志,函数永远不会返回

下一步怎么办

从注册表中可以看出,COM线程模型设置为“两者”

我试图通过创建一个小的C++ DLL来解决这个问题,它将作为NoTIFY()的包装器。因此,不要使用以下命令直接调用非托管COM客户端代码:

[MethodImpl(MethodImplOptions.InternalCall)]
    void Notify([In] uint dwCount, [In] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.BStr, SizeParamIndex = 0)] string[] psAddresses, [In] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Struct, SizeParamIndex = 0)] object[] pvarValues, [In] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.R8, SizeParamIndex = 0)] double[] pdtTimestamps);
我使用以下函数创建了小型dll:

extern "C" __declspec(dllexport)
void CppNotify(INotificationListener *pNotifier, unsigned long dwCount, BSTR *psAddresses, VARIANT *pvarValues, DATE *pdtTimestamps)
{
    pNotifier->Notify(dwCount, psAddresses, pvarValues, pdtTimestamps);
}
使用此定义调用的:

[DllImport("CppNotifier.dll", CallingConvention = CallingConvention.Cdecl)]
    extern public static void NotifyCpp([In] [MarshalAs(UnmanagedType.Interface)] INotificationListener pNotifier, uint dwCount, [In] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.BStr, SizeParamIndex = 0)] string[] psAddresses, [In] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Struct, SizeParamIndex = 0)] object[] pvarValues, [In] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.R8, SizeParamIndex = 0)] double[] pdtTimestamps);
我希望这能避免系统失效的例外,但还没有运气

我们的C#COM服务器的目标是.NET 4.0,而系统上安装的.NET版本是4.7.1。这会引发这样的问题吗


我还尝试使用RegAsm.exe重新注册C#dll,但似乎没有任何效果。

您需要将对象的接口从主线程打包到工作线程,并在此接口上使用调用Notify方法

在类中定义以下两个成员变量:

IStream*            m_StreamThis ;
<your interface>*   m_pMarshalledThis ;
这将导致在主线程上调用函数

当线程终止时,当然应该释放指针m_pMarshalThis。您可能希望将其附加到智能指针类

当然,您确实应该检查两个函数调用返回的HRESULT,但这取决于您自己


<>你可以找到其他(更好的)例子来说明如何在堆栈溢出或其他站点上使用这些函数。< /P>你如何在创建COM对象的线程中调用C++客户端中的CoInitialize(EX)?所有的COM对象都是用C语言编码的吗?在我的模拟C++客户端中,我这样称呼:<代码> HESRe= HyrOrrimalIt(null);<代码>。它是如何在客户的生产系统上调用的我不知道,因为我无法访问他们的COM客户端的源。是的,我的COM服务器的所有COM对象都是用C#编码的。这一点非常重要,您需要深入研究COM线程问题,并理解“单元”的概念。CoInitialize意味着创建一个STA分区。如果使用CoInitializeEx(NULL,Conit\u多线程),它的行为是否相同?在没有复制样本的情况下,很难提供更多帮助。我已更改为
HRESULT hr=coinitializex(NULL,COINITBASE\u多线程)实际上,我的C++ COM客户机现在确实从不同的线程得到了C*com服务器的通知。但是客户的C++ COM客户端我不能更改。我在注册表中注册的C#COM服务器dll的线程模型设置为
Both
。如果客户的C++ COM客户机初始化为STA,这是否意味着如果我从一个不同的线程调用接口函数,它仍然应该被COM子系统重新编成线程,你可能在好的轨道上,但是就像我说的,它很难帮助更多。根据MSDN,
CoMarshalInterThreadInterfaceInStream
返回值可以是
S_OK
E_OUTOFMEMORY
。但是当我调用它时,函数返回
E\u NOINTERFACE
。不确定这意味着什么,因为我的类接口已注册,guid已签出。对于第一个参数,我使用
NotificationRequest
类的guid,该类随后为我提供
NotificationListener
指针。第二个参数可以是
this
pIUnknown
,无所谓,值是相同的。是什么使这个函数返回
E_NOINTERFACE
?我猜这意味着在第二个参数中传递的对象不支持在第一个参数中传递的接口。您可能必须将指针强制转换到接口。我发现了一些示例,如CoMarshalInterThreadInterfaceInStream(IMyInterface,(IMyInterface*)this和m_pStream);感谢@Phil如此迅速的回复。嗯,这真的应该行得通<代码>IStream*m_StreamThis=NULL;HRESULT hr=CoMarshalInterThreadInterfaceInStream(this->GetObjectCLSID()、this->GetUnknown()、m_StreamThis)。很难看出第二个参数中的对象如何不支持第一个参数传递的接口。当然,我仍然得到
E\u-NOINTERFACE
CoMarshalInterThreadInterfaceInStream ( <your interface>, this, &m_StreamThis ) ;
CoGetInterfaceAndReleaseStream ( m_StreamThis, <your interface>, (void**)&m_pMarshalThis ) ;
m_pMarshalThis->Notify() ;