为啥将托管 .NET 客户端设置为使用 STA 线程会导致本机 COM 服务器中出现异常问题?

Posted

技术标签:

【中文标题】为啥将托管 .NET 客户端设置为使用 STA 线程会导致本机 COM 服务器中出现异常问题?【英文标题】:Is there any reason why setting a managed .NET client to use STA threads would cause problems with exceptions in a native COM server?为什么将托管 .NET 客户端设置为使用 STA 线程会导致本机 COM 服务器中出现异常问题? 【发布时间】:2012-03-26 18:16:02 【问题描述】:

我有一个标榜为 STA(公寓线程模型)的本机 (Delphi) COM 服务器。

它包含一些在某些情况下会引发溢出异常的算法。这些异常在代码中处理,如果我从主线程上的客户端访问 COM 服务器,一切都会正常工作。

如果客户端是本地的 (Delphi),我可以从多个线程访问服务器,只要我遵守在线程上创建的对象从同一个线程进行所有方法调用的规则。

但是,如果客户端是托管客户端(经过 Vb.NET 和 C# 测试),如果我将客户端线程的 ApartmentState 设置为 MTA,一切正常,但性能受到影响。

这是我所预料的,因为我猜 COM 一定是在做一些诡计多端的操作(即编组)以确保每个人都开心。

但是,如果我将 ApartmentState 更改为 STA,从而确保客户端和服务器之间的直接连接,客户端将因故障错误而崩溃,通常是 CustomMarshallers.dll 中的 System.***exception。

如果我消除导致这些溢出的数字,那么我没有问题。

我可以通过调整算法使其不依赖于异常(可能一开始应该如何编写它们)来解决这个问题,但我想了解发生这种情况的原因。

【问题讨论】:

您的代码在 STA 中必须是线程安全的。 代码是线程安全的。所有实例数据都是安全的,因为它保证从单个线程调用。所有全球数据都受到保护。 不简单,但在调试器下运行 COM 服务器会有所帮助 【参考方案1】:

鉴于您没有逐字引用错误消息并且没有提及 HRESULT 值,因此此答案涉及一些猜测。

理论上,STA 和 MTA 案例之间不同的可用堆栈空间可能会对 Delphi 应用程序的堆栈占用量产生影响。但以下可能性更大。

***Exception 很可能是由 Delphi 端本身引起的(假设错误消息称其为“未处理”)。仔细查看 Delphi 端的所有错误处理机制,并注意任何可能导致递归的机制,特别是错误。例如,在特定事件上执行的代码可能包含一个受保护的块,其中 catch 子句会触发相同的事件。

我认为,如果您从客户端代码中删除 PreserveSigAttribute,您在 STA 与 MTA 中看到的差异会有所平衡,因此堆栈溢出异常将停止转换为您的客户端可能会忽略的 HRESULT现在。 (如果你使用tlbimp.exe,这个属性是隐式指定的。这意味着你想以COM方式处理异常作为返回值,而不是.NET方式作为异常处理。)

【讨论】:

所有有效点毫无疑问,但是为什么本地客户端可以毫无问题地处理这个问题。我的一个算法中有一个特定的行: : 如果 a[icol,icol] = 0 然后引发 EMatrixError.Create('Matrix Error');我正在尝试处理此错误,但随着异常的展开,我得到了错误。所以是的,Delphi 代码中的错误,但 .NET 客户端中的某些东西似乎导致了问题 @Steve - 关于“客户端因故障错误而崩溃”,您可以复制并粘贴您看到的消息或堆栈跟踪吗?您是否尝试从 Delphi(非托管)客户端打印出 HRESULT,客户端现在可能会忽略它?如果我的回答是正确的,那 HRESULT 将是一个错误代码,PreserveSigAttribute 将是两个客户端之间的唯一区别。 我什至没有达到 HResult。当异常开始展开时发生错误。事实上,它甚至没有达到我的处理程序。就好像 .NET 客户端以某种方式干扰了非托管服务器的堆栈。 @Steve - 但在你写的问题中,当客户端是 Delphi 时,你可以从多个线程调用服务器而不会崩溃。我要求您修改您的 Delphi 客户端以打印它当前忽略的 HRESULT(或通过调试器显示它们)。

以上是关于为啥将托管 .NET 客户端设置为使用 STA 线程会导致本机 COM 服务器中出现异常问题?的主要内容,如果未能解决你的问题,请参考以下文章

C++/CLI 为啥对托管不可见

如何使用 net core / net5+ 为 wpf 设置 STA 公寓状态? [复制]

为啥要使用托管(C# 和 .NET)或本机代码进行 Windows API 开发?

检测到在集成的托管管道模式下不适用的ASP.NET设置的解决方法(非简单设置为经典模式)。 - CatcherX

为啥我将tableview的分隔符设置为None时,分隔线仍然存在?

Authorize.net 接受托管和客户资料