1

我对使用解决方案中引用的 VB6 COM dll 的 Windows 服务有疑问。

该服务是执行任务的调度程序。每个任务都在一个线程中执行,因此它允许用户同时执行一些任务。

当一个线程启动时,一个对象(在名为 C_AUTO 的 vb6 dll 中)在 c# 中使用“New”语法创建。该对象的构造函数创建其他对象来执行任务。任务完成后,C_AUTO 的析构函数用 set ... = nothing 销毁所有其他对象。对象 C_AUTO 被销毁,线程也被销毁。

我的问题是当服务执行另一个任务时,另一个线程创建了另一个 C_AUTO 对象。我添加了一个片段,它在文件中写入内存指针的值并且值相同,因此 C_AUTO 创建的所有对象都不会被破坏。

是否有另一种方法可以在没有“新”语法的情况下加载 VB6 dll,这允许我在任务完成时卸载所有对象?因为几天后,服务消耗了大量内存,任务崩溃了。

谢谢你的帮助

4

1 回答 1

7

There are several unpleasant implementation details about a VB6 object that makes them difficult to use correctly in a C# service. VB6 objects are COM objects that are apartment threaded. Which is an expensive word that means that they are not thread-safe and COM ensures that they are used in a thread-safe way.

A C# service almost always creates threads that are not hospitable to apartment threaded objects. COM will create a new thread to give such an object a safe home. That's expensive, a new thread costs a megabyte of virtual memory.

Furthermore, such an object will only be unloaded (and the thread will only stop running) when the garbage collector runs. You can easily run into a problem if you make a lot of calls on the VB6 object but don't yourself allocate a lot of .NET objects. Which stops the GC from running often enough to keep you out of trouble with all of these threads getting created and swallowing a bunch of virtual memory. You'll get the OOM kaboom when you've created about 1800 of them, give or take.

Specific workarounds are:

  • Use the Thread.SetApartmentState() method to switch the thread to STA before you start it. That stops COM from creating that helper thread. Do note that common service techniques like using a Timer to get the service running are not suitable, you have to create a Thread in the OnStart() method.

  • You may have to call Application.Run() to start a message loop, a requirement for STA threads. That tends to be a bit tricky to do in a service, you get little help from the project template to get the plumbing in place. You may get away without pumping but you have to be sure to make all of the calls on the VB6 object on the same thread that created it. The diagnostic for getting this wrong is deadlock.

  • If you've determined that the GC doesn't run frequently enough (use Perfmon.exe to look at the .NET counters) then you may have to help and call GC.Collect() to get the VB6 object unloaded.

Not that easy to get everything right here. If you've been contemplating getting that VB6 code updated then now would be a good time.

于 2013-09-06T09:36:53.270 回答