C# STAThread COMException

У меня есть внешний компонент (C++), который я хочу вызвать из моего кода C#.

Код выглядит примерно так:

using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace dgTEST
{
    class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            ExtComponentCaller extCompCaller = new ExtComponentCaller();
            result = extCompCaller.Call(input);

            Thread t = new Thread(new ThreadStart(() =>
            {
                try
                {
                    result = extCompCaller.Call(input);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }
            }));

            t.SetApartmentState(ApartmentState.STA);
            t.Start();
            t.Join();
        }
    }
}

Таким образом, проблема в том, что при первом вызове он работает хорошо, вызван внешний компонент, я получил обратный результат.

Но когда я пытаюсь вызвать его в другом потоке, я получил исключение: System.InvalidCastException: Невозможно привести объект COM типа 'System.__ComObject' ... . Я уверен, что это исключение, из-за STAThread. Потому что, если я удаляю атрибут [STAThread] из функции Main, то же самое происходит с первым вызовом внешнего компонента, который работал нормально.

Как я могу вызвать этот внешний компонент из другого потока, чтобы избавиться от этого исключения?

ОБНОВИТЬ-------------

Другая сумасшедшая вещь происходит сейчас. Когда я запускаю программу из Visual Studio с F5, проблема возникает и при первом вызове, но когда я выполняю непосредственно двоичный файл.exe, он работает (из другого потока это не так:(). Если я переключаюсь сборка от Debug до Release и запуск ее из Visual Studio с F5, первый вызов снова работает.

Почему это происходит?

Спасибо за помощь заранее!

С наилучшими пожеланиями, Zoli

1 ответ

Решение

Потоки никогда не маленькая деталь. Если код явно не задокументирован для поддержки многопоточности, то вероятность того, что он его не поддерживает, составляет 99%.

И, очевидно, этот компонент не поддерживает многопоточность. Создание другого потока STA - не волшебное решение, это все же другой поток. InvalidCastException сообщает, что в нем также отсутствует поддержка прокси / заглушки, необходимая для маршалинга вызовов из рабочего потока, например, того, который вы пытаетесь создать. Требуется для выполнения поточно-ориентированных вызовов кода, который не является поточно-ориентированным. Несмотря на то, что вы нарушили контракт для [STAThread], он должен прокачать цикл сообщений. Это цикл сообщений, который позволяет выполнять вызовы из рабочего потока в компонент, который не является потокобезопасным. Вы получаете цикл обработки сообщений из Application.Run().

Это где доллар останавливается. Это не потокобезопасно, точка. Даже если вы исправите основной поток или попросите поставщика или автора предоставить вам прокси / заглушку, вы все равно не выполнили то, что намеревались выполнить, на самом деле он не будет работать в том рабочем потоке, который вы создали. Так должно выглядеть так:

    static void Main(string[] args)
    {
        Thread t = new Thread(new ThreadStart(() =>
        {
             ExtComponentCaller extCompCaller = new ExtComponentCaller();
             result = extCompCaller.Call(input);
        }));

        t.SetApartmentState(ApartmentState.STA);
        t.Start();
        t.Join();
    }

Который создает объект в том же потоке, из которого вы делаете вызовы, поэтому он является поточно-ориентированным. По-прежнему существует проблема, заключающаяся в том, что этот рабочий поток не перекачивает цикл обработки сообщений, поэтому COM-компоненты полагаются на это. Вы узнаете, является ли это проблемой или нет, из тупика или событий, которые не запускаются. Если он уже работал нормально в вашей тестовой программе, когда вы вызывали его из основного потока, то, вероятно, у вас все в порядке, если вы не используете его.

Другие вопросы по тегам