如何从多线程的C# Windows服务应用程序中调用VB6 DLL?

8
我正在运行一个多线程的Windows服务,需要调用一个VB6 dll。没有关于这个VB6 dll的文档,并且这个遗留系统支持非常关键的业务流程。
第一次(第一个线程),这个dll表现良好。当其他线程需要访问时,它开始提供错误的结果。
我读到有人说:
“如果您使用的是VB6,请注意一件事情。如果您正在运行多线程服务,则线程模型将必须更改以支持公寓。VB仅支持多个单线程公寓,但.NET通常运行完全自由线程。调用VB6 DLL的线程需要与DLL兼容。”
团队中的另一个人给了我将此ddl放入一个分离的应用程序域的想法。但我不确定。
我们如何处理从多线程C# Windows服务应用程序调用的VB6 dll?

为了给您一个明确的答案,我们需要更多信息:它是否在COM+下运行,在前一种情况下是执行内部或外部进程?您如何在系统中安装它:通过regsvr32还是其他方式? - Giulio Vian
感谢大家,我已经找到了问题所在。这个dll不应该与除VB 6之外的任何其他东西一起使用。它内部有一个错误。它不接受服务器上的英语区域设置,我们不得不改为葡萄牙语;客户端语言。 到目前为止,我正在使用单例模式和代理模式来处理这个遗留组件,现在它运行得很好。 - Eduardo Xavier
4个回答

2
当线程进入时,您是否保存对象并在新线程上重用它们?如果可以的话,请为每个线程创建新的对象。我们有一个使用数据层dll的情况。如果在一个线程上创建连接,则无法从另一个线程中使用它。如果在每个线程上创建一个新连接,则可以正常工作。
如果创建对象速度较慢,请查看ThreadPool类和ThreadStatic属性。线程池反复回收同一组线程来执行工作,而ThreadStatic允许您创建仅存在于一个线程中的对象。例如:
[ThreadStatic]
public static LegacyComObject myObject;

当请求进来时,将其转化为一个任务并将其排队到线程池中。当任务开始时,检查静态对象是否已初始化;

void DoWork()
{ 
    if (myObject == null)
    { 
        // slow intialisation process
        myObject = New ...
    }

    // now do the work against myObject
    myObject.DoGreatStuff();
}

1

你说:

我正在运行一个多线程的Windows服务,需要调用一个VB6 dll。关于这个VB6 dll没有任何文档,而这个遗留系统支持非常关键的业务流程。

同时你又说:

在第一次(1º线程)时,这个dll表现得很好。但是当其他线程需要访问时,它开始提供错误的结果。

我会确保管理层知道你所看到的故障,因为支持关键业务流程的代码已经过时且未经记录,并且正在以其从未预期或测试的方式使用。我敢打赌,它以前也从未被测试过从.NET中使用,对吧?

这是我的建议,这类似于我实际实现的某些内容:

VB6 DLL希望在单个线程上调用。 不要让它失望! 当您的服务启动时,请启动适当类型的线程(我不能说,因为我已经故意忘记了所有STA / MTA的东西)。将请求排队到该线程以访问VB6 DLL。所有此类访问都通过单个线程进行。

这样,就从VB6 DLL的角度来看,它运行的方式与测试时完全一致。


顺便说一下,这与我实现的略有不同。我使用的是 Web 服务,而不是 Windows 服务。我使用的是 C DLL,而不是 VB6,并且它不是 COM。我只是将对该对象的所有访问重构为单个类,然后在每个公共方法周围放置锁语句。


我完全同意John的观点。由于VB6 DLL对你来说实际上是一个黑盒子,所以最好选择保证正确的方法。在最好的情况下,该DLL将支持单线程公寓(STA)线程模型。请参阅MSDN上的此链接,了解调用STA COM对象时需要考虑的事项:http://msdn.microsoft.com/en-us/library/ms680112(VS.85).aspx。为了获得最大的性价比,您可能希望简单地利用适配器或单例将对象访问调节到一次一个线程(正如John建议的那样)。还要注意您的.NET中的COM互操作和封送。 - Sean P. McDonough

0

这篇关于多线程Visual Basic 6 DLL的文章提供了一些见解。它说:

要使一个ActiveX DLL项目成为多线程的,可以在“项目属性”对话框的“常规”选项卡上选择所需的线程选项。

这篇文章说有三种可能的模型可供选择:

One thread of execution 
Thread pool with round-robin thread assignment 
Every externally created object is on its own thread 

我假设默认情况下是一个执行线程,而另外两个选项中必须选择一个。


0

你可能想看一下这个:linky

这里有一段引起我注意的代码片段:

VB6 COM对象是STA对象,这意味着它们必须在STA线程上运行。您确实从两个MTA线程创建了该对象的两个实例,但对象本身将在单个(COM (OLE)创建的) STA线程上运行,并且来自两个MTA线程的访问将被编组和同步。因此,您应该将线程初始化为STA,以便每个对象在自己的STA线程上运行而不需要编组,这样就可以解决问题了。
无论如何,VB风格的COM对象始终是STA。现在,为了防止公寓编组和线程切换,您需要在STA初始化的公寓中创建实例。还要注意,当您在Main上设置[MTAThread]属性时,您实际上将主线程初始化为MTA,当您从MTA线程创建STA对象的实例时,COM将创建一个单独的(非托管)线程并将其初始化为STA(这称为默认STA),所有对STA对象的调用都将被编组(并产生线程切换),在某些情况下,IDispatch调用将由于IP编组失败而失败。因此,建议仅从兼容的公寓使用STA(因此是VB6)对象。

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