简体   繁体   English

从多线程c#Windows服务应用程序中调用VB6 DLL?

[英]Call VB6 DLL from a multithreaded c# windows service application?

I'm running a multithreaded windows service that need to call a VB6 dll. 我正在运行需要调用VB6 dll的多线程Windows服务。 There's no documentation about this VB6 dll and this legacy system supports a very critical business process. 没有有关此VB6 dll的文档,并且该旧系统支持非常关键的业务流程。

At first time (1st thread), this dll performs well. 第一次(第一个线程),该dll表现良好。 As other threads need access, it start provide wrong results. 由于其他线程需要访问,因此它开始提供错误的结果。

I read one guys saying: 我读过一个人说:

"Just be careful of one thing if you are using VB6. Your threading model is going to have to change to support apartments if you are running a multithreaded service. VB only supports multiple single-threaded apartments, but .NET runs fully free threaded normally. The thread that calls into the VB6 DLL needs to be compatible with the DLL." “如果要使用VB6,请小心一件事。如果要运行多线程服务,则必须更改线程模型以支持单元。VB仅支持多个单线程单元,但是.NET通常会运行完全免费的线程。调用VB6 DLL的线程必须与DLL兼容。”

Another guy from team gave me the idea to put this ddl in a separated application domain. 团队中的另一个人给了我这个将DDL放在单独的应用程序域中的想法。 But I'm not sure. 但是我不确定。

How can we work with VB6 dll called from a multithreaded c# windows service application? 我们如何使用从多线程C#Windows Service应用程序调用的VB6 dll?

When the threads come in, are you saving objects and reusing them later on new threads? 当线程进入时,您是否要保存对象并稍后在新线程上重用它们? If you can, create the objects fresh for every thread. 如果可以,请为每个线程新鲜创建对象。 We have a situation like this with a data layer dll we use. 我们使用的数据层dll存在这种情况。 If you create a connection on one thread, it can't be used from another. 如果在一个线程上创建连接,则无法从另一个线程使用该连接。 If you create a new connection on each thread, it works fine. 如果在每个线程上创建一个新的连接,它将正常工作。

If it's slow to create your objects, look at the ThreadPool class and the ThreadStatic attribute. 如果创建对象的速度很慢,请查看ThreadPool类和ThreadStatic属性。 Threadpools recycle the same set of threads over and over to do work, and ThreadStatic lets you create an object that exists for one thread only. 线程池反复循环使用同一组线程来完成工作,而ThreadStatic允许您创建一个仅存在于一个线程中的对象。 eg 例如

[ThreadStatic]
public static LegacyComObject myObject;

As a request comes in, turn it into a job and queue it in your thread pool. 当有请求进入时,将其转换为作业并将其放入线程池中。 When the job starts, check if the static object is initialised; 作业开始时,检查静态对象是否已初始化;

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

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

You say 你说

I'm running a multithreaded windows service that need to call a VB6 dll. 我正在运行需要调用VB6 dll的多线程Windows服务。 There's no documentation about this VB6 dll and this legacy system supports a very critical business process. 没有有关此VB6 dll的文档,并且该旧系统支持非常关键的业务流程。

and at the same time you say 同时你说

At first time (1º thread), this dll performs well. 第一次(1º线程)时,此dll性能良好。 As other threads need access, it start provide wrong results. 由于其他线程需要访问,因此它开始提供错误的结果。

I'd make very certain that Management is aware of the failure you're seeing because the code supporting the critical business process is old and undocumented, and is being used in a way it was never intended to be used, and was never tested to be used. 我非常确定管理层会意识到您所遇到的失败,因为支持关键业务流程的代码是旧的且没有文档记录,并且正在以从未打算使用且未经测试的方式使用使用。 I bet it's also never been tested to be used from .NET before, has it? 我敢打赌,它也从未经过过.NET的使用测试,对吗?

Here's my suggestion, and this is similar to something I've actually implemented: 这是我的建议,这与我实际执行的类似:

The VB6 DLL expects to be called on a single thread. VB6 DLL期望在单个线程上被调用。 Do not disappoint it! 不要失望! When your service starts, have it start up a thread of the appropriate type (I can't say, since I've deliberately forgotten all that STA/MTA stuff). 当您的服务启动时,让它启动适当类型的线程(我不能说,因为我故意忘记了所有STA / MTA东西)。 Queue up requests to that thread for access to the VB6 DLL. 将对该线程的请求排队,以访问VB6 DLL。 Have all such access go through the single thread. 使所有此类访问都通过单线程。

That way, as far as the VB6 DLL is concerned, it's running exactly as it was tested to run. 这样,就VB6 DLL而言,它的运行与经过测试可运行的完全相同。


BTW, this is slightly different from what I've implemented. 顺便说一句,这与我已经实现的略有不同。 I had a web service, not a Windows Service. 我有一个Web服务,而不是Windows服务。 I had a C DLL, not VB6, and it wasn't COM. 我有一个C DLL,而不是VB6,它不是COM。 I just refactored all access to the thing into a single class, then put lock statements around each of the public methods. 我只是将对事物的所有访问重构为一个类,然后在每个公共方法周围放置锁语句。

This article on multithreading Visual Basic 6 DLL's provides some insight. 这篇有关多线程Visual Basic 6 DLL的文章提供了一些见解。 It says: 它说:

To make an ActiveX DLL project multithreaded, select the desired threading options on the General tab of the Project Properties dialog box. 若要使ActiveX DLL项目成为多线程,请在“项目属性”对话框的“常规”选项卡上选择所需的线程选项。

This article says there are three possible models to choose from: 本文说,有三种可能的模型可供选择:

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

I assume that the default is one thread of execution , and that one of the other two options needs to be selected. 我假设默认值是one thread of execution ,并且需要选择其他两个选项之一。

You might want to take a look at this: linky 您可能需要看一下: linky

And here is a snippet that caught my attention: 以下是引起我注意的摘要:

VB6 COM objects are STA objects, that means they must run on an STA thread. VB6 COM对象是STA对象,这意味着它们必须在STA线程上运行。 You did create two instances of the object from two MTA threads, but the object itself will run on a single (COM (OLE) created) STA thread, and access from the two MTA threads will be marshaled and synchronized. 您确实从两个MTA线程创建了对象的两个实例,但是对象本身将在单个(COM(OLE)创建的)STA线程上运行,并且将对来自两个MTA线程的访问进行封送和同步。 So what you should do is, initialize the threads as STA so that each objects runs on his own STA thread without marshaling and you will be fine. 因此,您应该做的是,将线程初始化为STA,以便每个对象都在自己的STA线程上运行,而不会封送处理,这样就可以了。

Anyway, VB style COM objects are always STA. 无论如何,VB样式的COM对象始终是STA。 Now in order to prevent apartment marshaling and thread switching you need to create instances in STA initialized apartments. 现在,为了防止单元封送和线程切换,您需要在STA初始化的单元中创建实例。 Note also that when you set the [MTAThread] attribute on Main, you effectively initialize the main thread as MTA, when you create instances of STA objects from MTA threads COM will create a separate (unmanaged) thread and initialize it as STA (this is called the default STA), all calls to STA objects from MTA threads will be marshaled (and incur thread switches), in some cases Idispatch calls will fail due to IP marshaling failures. 还要注意,在Main上设置[MTAThread]属性时,可以将主线程有效地初始化为MTA,当您从MTA线程创建STA对象的实例时,COM将创建一个单独的(非托管)线程并将其初始化为STA(这是称为默认STA),则将从MTA线程对STA对象的所有调用进行封送处理(并引发线程切换),在某些情况下,由于IP封送处理故障,Idispatch调用将失败。 So the advise is use STA (and therefore VB6) objects from compatible apartments only. 因此,建议仅使用兼容公寓中的STA(因此使用VB6)对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM