C# COM 服务器互操作 - System.InvalidCastException.
C# COM server interop - System.InvalidCastException
我在 C# 中创建了一个 COM 服务器,它为特定设备提供服务,并由C++ COM 客户端(客户应用程序,没有可用的源代码)使用。 客户应用程序需要定期更新,为此使用称为Notify()
的功能。该函数在非托管 COM 客户端代码中实现。
当从创建此通知侦听器对象的同一线程中调用Notify()
时,一切都很好。但这只能发生一次C++因为 COM 客户端最初只请求一次数据。
即使它只要求一次数据,它似乎也不能容忍超过一秒钟左右的响应。而且我的设备反应有点慢,尤其是在网络上,超过一秒钟才能获得某种读数。好的,我明白了,它想要快速响应。
因此,不仅要实现定期更新,还要减轻主线程等待数据的负担,我创建了一个单独的线程,该线程每分钟探测一次设备,然后通过调用Notify()
将结果报告回非托管代码。
当从不同的线程调用此 Notify() 时,我得到了一个非常讨厌System.InvalidCastException
。异常是从 CLR 内部深处引发的。
这是在生产系统上。在我自己的开发系统上,我有自己的C++ COM客户端模型,此异常在调试会话期间被ContextSwitchDeadlock
屏蔽。我可以关闭这个,但不会改变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);
我希望这可以避免System.InvalidCastException,但还没有运气。
我们的 C# COM 服务器面向 .NET 4.0,而系统上安装的 .NET 版本是 4.7.1。这会引发这样的问题吗?
我也尝试使用 RegAsm.exe 重新注册 C# dll,但这似乎没有任何效果。
您需要将对象的接口从主线程编送到工作线程,并在此接口上使用调用 Notify 方法。
在类中定义以下两个成员变量:
IStream* m_StreamThis ;
<your interface>* m_pMarshalledThis ;
在创建线程之前,请调用函数 CoMarshalInterThreadInterfaceInStream()。
CoMarshalInterThreadInterfaceInStream ( <your interface>, this, &m_StreamThis ) ;
第二个参数是指向 IUnknown 的指针。在你的类中,必须实现IUnknown,我认为指定"this"是可以的。
线程入口点不能是类成员,因此常见的做法是将对象指针作为参数传递给线程,然后使用它来调用成员函数。我假设你已经在这样做了。
在线程中,调用成员函数后,调用函数 CoGetInterfaceAndReleaseStream()。
CoGetInterfaceAndReleaseStream ( m_StreamThis, <your interface>, (void**)&m_pMarshalThis ) ;
当您需要调用 Notify 函数时,请使用封送指针调用它:
m_pMarshalThis->Notify() ;
这将导致在主线程上调用函数。
当线程终止时,当然应该释放指针m_pMarshalThis。您可能希望将其附加到智能指针类。
当然,您应该真正检查两个函数调用返回的 HRESULT,但这取决于您。
您当然可以在Stack Overflow或其他站点上找到有关如何使用这些函数的其他(和更好的)示例。
- 如何在c++中使用system()来运行包含空格的python脚本
- System.InvalidCastException - SQL to C++ - safe_cast<float>
- C++/CLI System.AccessViolation在托管类中调用非托管函数时出现异常
- 来自 Android 应用程序内部的 boost 类型的 boost::wrapexcept<boost::system::system_error> 的未捕获异常
- 程序在使用 system() 启动另一个可执行文件时停止
- 为什么我的 DeviceInformation 对象没有 System.Devices.InterfaceClassGuid 属性?
- 为什么"using System;"不被视为不良做法?
- C++ 合并字符串以'system'函数错误
- System.AccessViolationException:shared_ptr C# .NET 和 C++ 应用程
- 将 Vcl::Controls::TCaption aka (System::UnicodeString) 转换为 co
- 对 boost::system::d etail::system_category_instance 的未定义引用,从
- Python os.system() 返回错误值
- 使用 system() 函数在C++程序中运行 cmd 命令
- 如何使用 C/C++ 和 system() 系统调用以外的其他方法在 Linux 中获取文件功能?
- 在C++中,如何在第一个"system()"结束后执行第二个"system()"?
- system() 无法运行我的程序,因为空间
- How to recover system gcc compiler on centos 6
- 导出 c++ 函数并使用 c# System.AccessViolationException 中的函数
- g++ 中的 system() 函数
- System Verilog DPI - 在 cpp 中运行并行线程,在 SV 中运行另一个并行线程