Невозможно вызвать COM-объект, созданный из STAThread из других потоков STA - PullRequest
7 голосов
/ 01 февраля 2010

Я новичок в COM и пытаюсь понять разницу между STA и MTA. Я попытался создать пример, который показал бы, что COM может управлять вызовами к объекту, созданному в STA, который не является потокобезопасным.

MyCalcServer класс здесь создается с использованием простого объекта ATL. Используемые настройки такие же, как в этой статье :

  • Модель резьбы: Квартира
  • Агрегация: Нет
  • Интерфейс: Пользовательский

MyCalcServer COM-объект используется в другом проекте C #, а именно:

class Program
{
    [STAThread]
    static void Main(string[] args)
    {
        MyCOMLib.MyCalcServer instance = new MyCOMLib.MyCalcServer();
        string output1;
        instance.ChangeValue("Gant", out output1);
        Console.WriteLine(output1);


        Thread t1 = new Thread(() =>
        {
            while (true)
            {
                string output;
                instance.ChangeValue("Gant", out output);
                Console.WriteLine(output);
            }
        });
        t1.SetApartmentState(ApartmentState.STA);
        t1.Start();

        // :
        // also has t2 and t3 here with similar code
        // :

        t1.Join(); t2.Join(); t3.Join();

    }
}

Однако это всегда приводит к тому, что InvalidCastException (E_NOINTERFACE) вызывается внутри кода t1. Я также попытался изменить ApartmentState на MTA безуспешно.

Невозможно привести COM-объект типа «MyCOMLib.MyCalcServerClass» для тип интерфейса 'MyCOMLib.IMyCalcServer. это операция не удалась, потому что QueryInterface вызов на COM компонент для интерфейса с IID '{B005DB8C-7B21-4898-9DEC-CBEBE175BB21}' не удалось из-за следующей ошибки: Нет такой интерфейс поддерживается (исключение от HRESULT: 0x80004002 (E_NOINTERFACE)).

Может кто-нибудь объяснить, что я здесь делаю не так?

Ответы [ 2 ]

3 голосов
/ 01 февраля 2010

Вы явно просите COM создать экземпляр для основного потока, а затем передаете это другому потоку. Конечно, при некоторых обстоятельствах это разрешено (например, объявить MyCalcServer как многопоточный).

Но в вашем случае, похоже, вам нужно создать прокси для другого потока. В обычных COM-клиентах это делается CoMarshalInterThreadInterfaceInStream. Есть большая статья, чтобы уточнить это http://www.codeproject.com/KB/COM/cominterop.aspx

1 голос
/ 02 февраля 2010

Мне удалось получить это разрешение.

Поскольку я новичок в COM, я мало что знаю о Proxy / Stub и о том, что они необходимы для объединения вещей между STA и STA. После того, как создал новый проект ATL и удостоверьтесь, что у меня есть пометка «Merge Proxy / Stub». Проблема исчезла.

Я считаю полезной информацию с этой страницы: Почему я хочу объединить код Proxy / Stub с моим проектом DLL.

Прокси / заглушки, обеспечивающие стандарт маршалинг для вашего компонента. Во многих В некоторых случаях компонент на основе DLL не может нужен прокси / заглушка, потому что он работает в том же контексте своего клиента, и эта опция может показаться бесполезной на первый взгляд. Тем не менее, COM использует маршалинг Процесс синхронизации доступа к компонент в многопоточном ситуации. Итак, компонент на основе DLL потребуется прокси / заглушка DLL как минимум два случая:

  • Он запускает многопоточный клиент и должен пройти интерфейс указатель между квартирами (СТА на СТА или от MTA до STA).

  • DCOM может предоставить суррогатный процесс для компонента на основе DLL, поэтому что к нему можно получить доступ в распределенная среда. В этом случае прокси / заглушка необходимы для маршала между машинами.

Путем слияния кода прокси / заглушки с ваша реализация, вам не нужно распределите две DLL, только одну.

Я отмечу ответ @ Dewfy как принимающий, поскольку он пролил свет на тему прокси

...