WPF线程:“无法使用已与其基础RCW分离的COM对象。”

时间:2010-07-02 15:00:14

标签: c# wpf multithreading com interop

我收到以下错误:

"COM object that has been separated from its underlying RCW cannot be used."

我确定问题是因为COM对象不是在它创建的线程上调用的 - STA。我试图实现IDisposable,但它对我没用。

有几个帖子处理类似的问题,但仍然无法解决我的问题:

Is it safe to call an RCW from a finalizer? Release Excel Object In My Destructor

任何人都可以发布一个示例/解释如何从另一个线程正确访问COM对象吗?

以下是显示问题的最小代码:

using System;
using System.Threading;

namespace Test.ComInterop
{
    public class Program
    {
        MyCom _myCom;

        [STAThread]
        static void Main( string[] args )
        {
            new Program();
        }

        public Program()
        {
            _myCom = new MyCom();

            // this method call works
            string version = _myCom.ComMethod();

            StartThread();
        }

        private void StartThread()
        {
            Thread t = new Thread( UIRun );
            t.SetApartmentState( ApartmentState.STA );
            t.Start();
        }

        void UIRun()
        {
            TestUI window = new TestUI();
            window.Show();

            // this method call fails
            window.Title = _myCom.ComMethod();

            window.Closed += ( sender2, e2 ) 
                => window.Dispatcher.InvokeShutdown();

            System.Windows.Threading.Dispatcher.Run();
        }
    }

    class MyCom
    {
        private dynamic _com;
        public MyCom()
        {
            _com = Activator.CreateInstance(
                Type.GetTypeFromProgID( "Excel.Application" ) );
        }

        public string ComMethod()
        {
            return (string) _com.Version;
        }
    }    
}

4 个答案:

答案 0 :(得分:4)

问题是程序的启动线程。它创建COM对象,启动一个线程,然后退出。作为清除主线程的一部分,.NET调用CoUninitialize(),这是COM对象的结束。获得该错误是预期的结果。

让你的主要启动线程退出是没有意义的。让它现在通过你自己的线程完成工作,问题解决了。

答案 1 :(得分:2)

通常这是因为底层COM对象已从其包装器中释放 - 当您通过Marshal.Release手动释放它或者托管托管包装器时会发生这种情况。在错误的线程上使用它只会导致对COM对象的任何调用实际发生在它创建的线程上 - 我在过去曾被它蜇过,它具有线程亲和力来执行。

您似乎没有处置包装器,但我不确定动态变量的影响是什么。

您是否尝试将线程公寓状态更改为MTA?

答案 2 :(得分:1)

很抱歉也许没有直接回答你的问题。这仅仅是以不同方式处理它的建议。希望它有所帮助。

使用COM与Excel互操作有很多陷阱 - 我认为不是与COM直接相关,而是Excel COM的实现方式。

我在与Excel(以及MsProject)的COM互操作方面苦苦挣扎。对于Excel,唯一的好解决方案是一个专用线程,用于处理从创建到终止的整个Excel通信。 Excel API中存在一些设计缺陷。一些方法调用不是无状态的,这意味着两个线程将很难使这些东西工作。将所有通信委托给一个线程并自己处理与其他线程的通信会更安全。

除此之外,您用于通信的线程也必须具有en / US文化(LCID issues)。这通常会产生另一条消息:

  

旧格式或无效类型库

但对您来说可能有用。

答案 3 :(得分:1)

尝试为DispatcherObject继承MyCom类。启动其他线程后,执行_myCom.Dispatcher.Run()。当你想与你的COM对象交谈时,只需做一个_myCom.Dispatcher.BeginInvoke / Invoke。