【问题标题】:Disposing ActiveX resources owned by another thread释放另一个线程拥有的 ActiveX 资源
【发布时间】:2011-03-04 08:29:34
【问题描述】:

我遇到了线程和处理资源的问题。

我有一个 C# Windows 窗体应用程序,它在线程中运行昂贵的操作。此线程实例化一个 ActiveX 控件 (AxControl)。必须释放此控件,因为它使用大量内存。所以我实现了一个 Dispose() 方法,甚至一个析构函数。

线程结束后,析构函数被调用。遗憾的是,这是由 UI 线程调用的。所以调用 activexControl.Dispose();失败并显示消息“COM 对象已与其底层 RCW 分离”,因为该对象属于另一个线程。

如何正确地做到这一点,或者它只是我使用的一个糟糕的设计?

(我将代码精简到最低限度,包括消除任何安全问题。)

class Program
{
    [STAThread]
    static void Main()
    {
        // do stuff here, e.g. open a form

        new Thread(new ThreadStart(RunStuff);

        // do more stuff
    }

    private void RunStuff()
    {
        DoStuff stuff = new DoStuff();
        stuff.PerformStuff();
    }
}

class DoStuff : IDisposable
{
    private AxControl activexControl;

    DoStuff()
    {
        activexControl = new AxControl();
        activexControl.CreateControl(); // force instance
    }

    ~DoStuff()
    {
        Dispose();
    }

    public void Dispose()
    {
        activexControl.Dispose();
    }

    public void PerformStuff()
    {
        // invent perpetuum mobile here, takes time
    }
}

【问题讨论】:

    标签: garbage-collection activex c#-2.0


    【解决方案1】:

    为什么不让工作线程显式处理它?

    你可以改变

    DoStuff stuff = new DoStuff();
    stuff.PerformStuff();
    

    到一个

    using(DoStuff stuff = new DoStuff())
    {
         stuff.PerformStuff();
    }
    

    所以你甚至不必担心。

    【讨论】:

      【解决方案2】:

      我不清楚你所说的实现 Dispose 方法的 ActiveX 控件是什么意思。 IDisposable 模式适用于托管代码。要释放 COM 对象,您通常会使用 Marshal.ReleaseComObject - 也许您是在 AxControl 类中执行此操作,而您没有显示其实现。

      上面的代码有几个问题。

      您应该处置 IDisposable DoStuff 实例:

      private void RunStuff() 
      { 
          using (DoStuff stuff = new DoStuff())
          {
              stuff.PerformStuff(); 
          }
      } 
      

      您不应该在终结器中访问托管资源 - 在您的情况下,终结器调用 Dispose 然后引用托管 axControl 实例。在您的终结器运行时,此实例可能已被收集。

      由于您没有直接在 DoStuff 类中使用非托管资源,因此您可能不需要终结器,但如果您有终结器,请遵循 MSDN 上的标准 IDisposable 模式,并且不要尝试释放任何托管对象.

      更新

      评论:

      AxControl 是由 Visual Studio 生成的 .NET Interop Wrapper DLL。

      在这种情况下,Dispose() 方法是什么?我不明白你为什么要在具有确定性终结的 ActiveX 控件中实现这样的方法——通常你会在最后一个 COM 引用被释放时进行清理。

      您的 DoStuff.Dispose 方法可能想要释放 COM 对象,例如

      public void Dispose()  
      {  
          activexControl.Dispose();  
          Marshal.ReleaseComObject(activexControl);
      }
      

      【讨论】:

      • AxControl 是由 Visual Studio 生成的 .NET Interop Wrapper DLL。每个 ActiveX 控件都派生自 .NET 控件。 (当然可以将其放置到 Windows 窗体上)。
      【解决方案3】:

      我正在处理由第三方提供的活动 x 控件,因此无法更改 ActiveX 中的处理例程。

      我相信您是正确的,“已与其底层 RCW 分离的 COM 对象”问题是线程问题,而不是处置实现问题。 COM 不能很好地与托管线程和托管内存相处。

      试试这样的:

      // Check to see if we need to use Invoke before wasting time with it
      if (activexControl.InvokeRequired)
      {
          // invoke Dispose on the control's native thread to avoid the COM exception
          activexControl.Invoke(() => activexControl.Dispose());
      }
      else
      {
          // run dispose on this thread
          activexControl.Dispose();
      }
      

      只需要调用即可避免 COM 错误,if 语句有助于在不需要调用的情况下提高性能。

      请注意创建活动 x 控件的线程需要仍然处于活动状态,否则您会遇到不同的问题。在您生成新线程后,在您的精简代码中,main 退出,当您以后需要它时,它可能不会活着。我一直在单元测试中遇到这个问题,并且必须在退出我的顶级函数之前插入等待其他线程结束的阻塞调用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多