多线程环境下COM的处理

发布于 2024-11-19 22:33:02 字数 878 浏览 4 评论 0 原文

我正在尝试正确处理包含在 Visual Studio 生成的 RCW 中的旧版 VFP (FoxPro) COM 控件。该控件公开了一个我应该调用的 Destroy 方法,以允许该控件正确地自行拆除。当发出释放 COM 实例的请求时,控件上的方法很有可能正在后台线程上执行。 VFP 是单线程单元模型,因此当调用 Destroy 时,只需将其添加到 VFP 执行堆栈中即可。

理想情况下,调用 Destroy 是正确的做法,因为它允许 COM 实例清理一些资源。我担心的是,实例化 VFP COM 控件实际上会启动该控件所在的 VFP 语言运行时实例,并且该实例可能会被锁定(无响应)。该 COM 组件公开了大型企业级 20 年旧版应用程序中的功能,并且我见过这样的情况:尝试调用此控件上的方法的 .NET 线程只是阻塞而不会抛出错误(总是由旧版中的错误引起) VFP 代码)。这种情况并不经常发生,但它经常促使我构建一个实例管理器,在后台线程中运行 VFP COM 实例上的方法,并定期检查该线程是否被阻塞,如果是,则销毁该线程COM实例和线程并重新启动一个新实例来监视。

这是处理可能正在执行后台方法的线程的正确方法吗?

我是否应该尝试通过尝试调用 Destroy 方法来让 COM 控件正确拆除?

if (_vfpThread != null)
{
  try
  {
    if (_vfpThread.IsAlive)
      _vfpThread.Abort();
  }
  catch (ThreadAbortException)
  { }
  finally
  {
    _vfpThread = null;
  }
}

if (_vfpInstance != null)
{
  Marshal.ReleaseComObject(_vfpInstance);
  _vfpInstance = null;
}

I'm trying to properly dispose of a legacy VFP (FoxPro) COM control wrapped in a RCW generated by Visual Studio. The control exposes a Destroy method I should call to allow the control to properly tear itself down. There is a very good chance a method on the control may be executing on a background thread when a request is made to dispose of the COM instance. VFP is a single-threaded apartment model, so when calling Destroy it should just be added to the VFP execution stack.

Calling Destroy would ideally be the right thing to do as it allows the COM instance to clean up some resources. My concern is that instantiating a VFP COM control actually starts up a VFP language runtime instance that the control is hosted in and that instance may be locked up (non-responsive). This COM component exposes functionality in a large enterprise-scale 20-year-old legacy app and I have seen situations where a .NET thread attempting to call a method on this control simply blocks without throwing an error (always caused by bugs in the legacy VFP code). This doesn't happen often, but it is often enough that it prompted me to build an instance manager that runs methods on the VFP COM instance in a background thread and periodically checks to see if that thread is blocked, and if so, destroys the COM instance and thread and restarts a new instance to monitor.

Is this the right way to dispose of the thread that a background method may be executing on?

Should I attempt to get fancier by trying to call the Destroy method to allow the COM control to properly tear down?

if (_vfpThread != null)
{
  try
  {
    if (_vfpThread.IsAlive)
      _vfpThread.Abort();
  }
  catch (ThreadAbortException)
  { }
  finally
  {
    _vfpThread = null;
  }
}

if (_vfpInstance != null)
{
  Marshal.ReleaseComObject(_vfpInstance);
  _vfpInstance = null;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

裂开嘴轻声笑有多痛 2024-11-26 22:33:02

当基于 VFP 的 COM 对象(始终在 STA 单元中运行)上挂起方法调用时,从另一个线程调用同一 COM 对象上的任何方法将阻塞,直到前一个调用返回(退出)公寓)。
这意味着,任何尝试同时调用 Destroy() 的线程都将受到第一个线程的支配。如果该线程不知道自动退出,理论上它可以无限期地阻止处理线程。因此,换句话说,没有直接的方法可以通过从另一个线程中调用 COM 对象上的另一个方法来要求第一个线程立即退出该方法。调用 _vfpThread.Abort() 应该可以,但这种方法的安全性很大程度上取决于 VFP 类的内部结构。
在许多情况下,由于它是遗留代码,它不会有像 try/catch/finally 部分这样允许正常退出的东西,因此资源可能最终未被释放。 - 坏的!

另一种方法是在某处(注册表、文件等)设置一个外部标志,第一个线程可以从它正在执行的方法中读取该标志。当然,这要求 VFP 类意识到必须从每个 COM 发布的方法中读取标志,并迅速采取相应行动。

另外,关于您的代码片段。
仅当执行此代码的线程自行中止时,在中止线程的代码中捕获 ThreadAbortException 才有意义。这会很尴尬,因为它可能只是从方法返回。 (或者,调用 _vfpThread.Abort() 的这个线程是否也可能从另一个线程中中止?)
在正常情况下,您需要包装在 ThreadAbortException 捕获器中的是第一个线程的主代码,该代码对 COM 对象上的所有这些业务方法执行调用。
理想情况下,您应该将其置于与 VFP 方法本身一样深的堆栈中,其中代码能够在重新抛出异常之前正常关闭所有资源/表等。
然后在传递给 ThreadStart 的 main 方法中,您将有一个类似的捕获器,只不过它会从该方法和平地返回,从而终止线程或将其释放到线程池。

When a method call is pending on a VFP-based COM object (which always runs in an STA apartment), invoking any method on that same COM object from another thread will block until the former call returns (exits the apartment).
That means, any thread attempting to call Destroy() concurrently will be at the mercy of that first thread. And if that thread doesn't know to exit voluntarily, it could in theory keep the disposing thread blocked indefinitely. So, in other words, there's no direct way to ask the 1st thread to exit the method immediately by calling another method on the COM object from within another thread. Calling _vfpThread.Abort() should work, but the safety of this approach largely depends on the internals of the VFP class.
In many cases, due to it being legacy code, it won't have anything like a try/catch/finally section that would allow for a graceful exit, therefore resources may wind up being left unreleased. - BAD!

Another approach would be to set an external flag somewhere (registry, file, whatever), which would be available for reading by that 1st thread from within the method it is executing. That of course requires that the VFP class be aware of having to read the flag from each of its COM-published methods, and act accordingly and quickly.

Also, regarding your code snippet.
Catching ThreadAbortException in the code that is aborting a thread only makes sense if the thread executing this code is aborting itself. Which would be pretty awkward, since it could instead just return from the method. (Or, is this thread that is calling _vfpThread.Abort() also potentially being aborted from yet another thread?)
In a normal scenario, what you'd need wrapped in a ThreadAbortException catcher is the 1st thread's main code that performs calls to all those business methods on the COM object.
Ideally, you'd have this as deep down the stack as the VFP methods themselves where the code would be able to gracefully close all resources/tables etc before re-throwing the exception.
And then in the main method that you passed to the ThreadStart, you'd have a similar catcher except it would peacefully return from the method, thereby terminating the thread or releasing it to the thread pool.

一个人的夜不怕黑 2024-11-26 22:33:02

是的,我确实正确理解了您的代码。-谢谢。

如果 vfp 线程未在 60 秒内正常退出,则中止该线程可能是您唯一可以做的事情。
就 Dispose 应该做什么而言,它应该尽力释放所有非托管资源,不幸的是,当它们在 VFP COM 类中使用/打开时,这些资源在此代码中被隐藏。因此,如果 COM 对象被占用,主线程将无法强制它释放这些资源。也许您可以尝试做的是将 COM 业务方法的整个主体包装在 VFP try-catch 块中,并在 catch 部分中释放资源/关闭表。 try-catch 块很有可能捕获从主线程调用 _vfpThread.Abort() 引起的 ThreadAbortException。

Yes, I did understand your code correctly.-Thanks.

Aborting the vfp thread if it does not exit gracefully within 60 seconds is perhaps the only thing you could do.
In terms of what Dispose should do - it should try its best to release all unmanaged resources, which are unfortunately hidden from this code as they are used/opened from within the VFP COM class. So, if the COM object is seized, the main thread won't be able to force it to release those resources. Perhaps what you could try doing is wrapping the entire body of the COM business method in a VFP try-catch block and releasing resources/closing tables in the catch section. There's a good chance that the try-catch block would capture the ThreadAbortException caused by calling _vfpThread.Abort() from the main thread.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文