使用C# .NET调用OCX控件时如何保证线程安全性

4

作为开发人员,我已经有一段时间没有使用OLE/COM了,但我目前需要在C#程序中使用一些第三方OCX代码库。

C#程序使用线程(它是一个TCP套接字服务器)。这些OCX被标记为公寓线程模型。根据我的阅读,我得出结论,如果我小心地为每个线程创建一个OCX实例,并且只从创建它的线程使用该实例,那么我应该没问题。

我也做了以下操作:

myThread.SetApartmentState(ApartmentState.STA);

在每个线程开始之前,需要进行以下操作。

这样是否足以确保OCX的安全使用?

我看到的症状是,线程都可以创建OCX,但似乎是随机的,对于准备和初始化OCX的调用会失败。它们似乎没有返回任何有用的信息来解释原因。

有人能解释我所看到的情况,或者给我一个指南,告诉我如何从多线程代码中安全地使用这些OCX吗?

或者,我应该放弃,在一个线程中创建每个和所有OCX的单个实例,并通过线程安全队列或类似方法将所有调用发送到它们吗?


如果有用的话,实际错误是RPC_E_SERVERFAULT 0x80010105,“服务器引发异常。” 我怀疑这不会有什么帮助,因为这个错误太通用了。 - JohnCC
3个回答

1

你正在做一些控件用户可能从未尝试过的事情。像VB6这样的运行时环境,是.ocx最常用的地方,不允许创建多个STA线程。很有可能控件内部的代码包含全局变量,而没有任何必要的锁定来确保安全。随机故障就是结果。

放弃它吧,.NET对System.Net命名空间的TCP支持非常出色。Socket、TcpClient和TcpServer类。


OCX库可能不是套接字服务器。 - dvhh
1
我不介意被踩一下,只要有理由就行。当然,这个理由也得有点道理。 - Hans Passant
不是我给你的投票,但你的建议本质上是使用.NET技术重写OCX,如果你不知道OCX确切的功能,这可能有点轻率。不幸的是,我不得不处理第三方组件,它们在TCP/IP上实现了未记录的协议,重写并不总是一个容易的选择。 - Joe
我认为你关于问题是正确的,但也许不是解决方案。这些OCX来自MapFactor的映射产品,用于反向地理编码。这是我工作中的一些遗留问题,我没有文档。它们可能已经不再受支持了。它们与TCP套接字服务器没有任何关系,只是被用于处理进入其中的数据。最终我们会转移到另一个系统,但现在我必须使用它。 - JohnCC
很抱歉听到这个消息,但请避免在多个线程中使用它。这些传统控件根本不是线程安全的,它们从来不需要是线程安全的。 - Hans Passant

1

将组件标记为公寓线程只是组件开发人员的断言。周围有很多组件开发人员不理解线程,即使那些理解线程的人有时也会犯错,因此信任这个声明是一种信仰。

就我个人而言,在多线程环境中使用第三方组件时非常谨慎,原因如上。除非我知道该组件已在多线程应用程序中广泛使用,并且/或者我知道我可以获得足够的支持。

如果您无法从组件开发人员那里获得支持或获取组件源代码,但绝对需要使用它,则创建单个实例的选项可能是一个很好的实用解决方案。


我同意你的分析。COM线程很难正确处理,而添加.NET互操作性又增加了一层复杂性。 我没有支持或源代码,所以我同意,我认为单个受保护的实例可能是可行的方法,特别是在其他遗留代码中运行良好,每个进程只创建一个实例。 - JohnCC

0

公寓状态应该由COM管理,因此设置公寓状态只涉及您的.NET代码公寓状态。 但是,您的COM对象的公寓只能从创建COM对象的线程(调用CoInitialize的线程)访问。 至于对COM对象的调用失败,可能来自底层代码。您的OCX库是什么?


OCX是MapFactor的一种映射产品,用于反向地理编码。它们在遗留代码中被广泛使用,单线程,通常每个进程只有一个静态实例。它们没有出现任何问题,这告诉我可能是我使用它们的方式不是预期的。 - JohnCC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接