2010-06-16 24 views
0

我遇到了线程和处理资源的问题。处理其他线程拥有的ActiveX资源

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

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

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

(我剥代码降到最低,包括消除任何安全问题。)

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 
    } 
} 

回答

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模式,并且不要尝试处理任何托管对象。

UPDATE

评论:

的AxControl是通过Visual Studio中生成的.NET互操作程序包装DLL。

在这种情况下,什么是Dispose()方法?我不明白为什么要在一个ActiveX控件中实现这种方法,该控件具有确定性定稿 - 通常,当最后一个COM参考被发布时,您会进行清理。

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

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

AxControl是由Visual Studio生成的.NET Interop Wrapper DLL。那里的每个ActiveX控件都是从.NET控件派生而来的。 (当然可以将它放到Windows窗体上)。 – 2010-06-16 08:30:16

0

我正在处理由第三方提供的活动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错误,如果语句有助于在不需要调用情况下的性能。

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