【问题标题】:WPF Thread: "COM object that has been separated from its underlying RCW cannot be used."WPF 线程:“无法使用已与其基础 RCW 分离的 COM 对象。”
【发布时间】:2011-03-11 03:49:18
【问题描述】:

我收到以下错误:

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

【问题讨论】:

    标签: c# wpf multithreading com interop


    【解决方案1】:

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

    【讨论】:

    • 谢谢,这似乎是一个有趣的方法。你能推荐一个链接,可以更深入地解释它吗?
    • 我没有链接,但是在处理与 UI (STA) 线程不同的线程上运行的 STA COM 对象时,我采用了这种方法。 Dispatcher 基本上是它自己的消息循环(IIRC,它甚至处理 Win32 消息以及 .NET 委托)。基本上,您只需运行两个调度程序,因此您可以轻松地在两个公寓之间编组。
    【解决方案2】:

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

    您似乎没有处理包装器,但我不确定动态变量会产生什么影响。

    您是否尝试将线程单元状态更改为 MTA?

    【讨论】:

    • 由于 UI 的原因,我必须使用 STA。我应该怎么做才能防止COM被释放?
    • 啊,Hans 发现它实际上是由于应用程序中的执行流程而被丢弃的。
    【解决方案3】:

    抱歉,可能没有直接回答您的问题。这只是以不同方式处理它的建议。希望对您有所帮助。

    COM 与 Excel 的互操作存在很多缺陷 - 我认为与 COM 没有直接关系,而是与 Excel COM 的实现方式有关。

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

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

    旧格式或无效类型库

    但可能对你有用。

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

      • +1 好地方。我怀疑底层对象被释放,但没有立即在代码中发现。
      • 非常感谢!我在不理解的情况下复制粘贴了 UI 线程相关的东西。我不需要开始一个新线程。
      猜你喜欢
      • 2021-09-23
      • 2011-04-26
      • 2012-04-23
      • 1970-01-01
      • 2011-11-21
      • 2012-04-27
      • 2011-07-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多