WPF Thread: «COM-объект, который был отделен от базового RCW, не может быть использован». - PullRequest
0 голосов
/ 02 июля 2010

Я получаю следующую ошибку:

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

Я уверен, что проблема в том, что COM-объект вызывается не в том потоке, в котором он был создан - STA.Я попытался реализовать IDisposable, но он не сработал для меня.

Есть пара постов, посвященных аналогичной проблеме, но которые все еще не решают мою проблему:

Безопасно ли этовызвать RCW из финализатора? Разблокировать объект Excel в моем деструкторе

Может ли кто-нибудь опубликовать пример / объяснить, как правильно получить доступ к 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 ]

4 голосов
/ 02 июля 2010

Проблема в ветке запуска вашей программы.Он создает COM-объект, запускает поток, а затем выходит .В рамках очистки этого основного потока .NET вызывает CoUninitialize (), и это конец COM-объекта.Получение этой ошибки - ожидаемый результат.

Нет никакого смысла позволять такому основному потоку запуска завершаться.Пусть это сделает работу, проделанную вашим собственным потоком, проблема решена.

2 голосов
/ 02 июля 2010

Обычно это происходит потому, что базовый COM-объект был освобожден из его оболочки - это происходит, когда вы отпускаете его вручную через Marshal.Release или управляемая оболочка удаляется. Использование его в неправильном потоке просто приведет к тому, что любые вызовы COM-объекта на самом деле будут происходить в потоке, в котором он был создан - я был поражен этим в прошлом, он имеет сродство к потоку для выполнения.

Вы, похоже, не избавляетесь от обертки, но я не уверен, какое влияние окажет динамическая переменная.

Вы пытались изменить состояние вашей резьбы на MTA?

1 голос
/ 03 июля 2010

Попробуйте сделать свой класс MyCom наследуемым для DispatcherObject.После того, как вы запустите другой поток, сделайте _myCom.Dispatcher.Run ().Если вы хотите поговорить с вашим COM-объектом, просто выполните _myCom.Dispatcher.BeginInvoke / Invoke.

1 голос
/ 02 июля 2010

Извините, что, возможно, не отвечаю прямо на ваш вопрос. Это всего лишь совет по-другому. Надеюсь, это поможет.

С COM-взаимодействием с Excel возникает множество ловушек - я думаю, что это не связано напрямую с COM, а с тем, как реализован Excel COM.

Я много боролся с COM-взаимодействием с Excel (а также с MsProject). Для Excel единственным хорошим решением была специальная нить для обработки всего сообщения Excel от создания до завершения. В Excel API есть несколько недостатков дизайна. Некоторые вызовы методов не являются состояниями, то есть двум потокам будет трудно заставить их работать. Было бы безопаснее делегировать все сообщения одному потоку и обрабатывать связь с другими потоками самостоятельно.

Кроме того, нить, которую вы используете для связи, ДОЛЖНА также иметь язык en / US ( Проблемы с LCID ). Обычно это приводит к другому сообщению:

Старый формат или недопустимая библиотека типов

но вам может быть полезно знать.

...