Как обрабатываются компоненты STA COM при использовании в службе WCF, размещенной в IIS (7+)? - PullRequest
6 голосов
/ 07 октября 2010

Из того, что я понимаю, когда COM-компонент, помеченный как использующий STA , используется из потока MTA , вызовы должны быть перенаправлены в поток STA и выполнены из него. специальная тема. В случае клиентского приложения Windows это будет означать, что оно будет выполняться в потоке пользовательского интерфейса (если помечено как STA), и что обратные вызовы от COM-компонента для меня будут обрабатываться сообщениями Windows, отправленными в скрытое окно и обработанными в цикл сообщений Windows.

Что произойдет, если я использую COM-компонент STA в службе WCF, размещенной в IIS? Будет ли рабочий процесс иметь цикл сообщений Windows в потоке STA? Могу ли я запустить свой собственный поток STA с собственным циклом сообщений?

Ответы [ 2 ]

5 голосов
/ 10 марта 2011

Среда выполнения COM следит за отправкой вызовов методов для объекта COM внутри STA: вы правы, что это основано на том же механизме ОС, который используется для отправки сообщений Windows, но вам не нужно беспокоиться о созданииэто происходит - COM делает это для вас под капотом.

Что вам делать нужно беспокоиться о том, в какой STA будут жить ваши COM-объекты. Если вы создаете экземпляр COM с поточной квартиройобъекты, использующие COM Interop из службы WCF, нужно соблюдать осторожность.

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

Необходимо убедиться, что объекты COM, которые вы создаете для обслуживания определенного запроса WCF, находятся в собственной STA, отдельной от объектов, созданных для других запросов.Есть два основных способа сделать это:

  • Раскрутить свой собственный поток, указав ApartmentState.STA в SetApartmentState() перед его запуском, для которого создаются экземпляры COM-объектов для определенного запроса.Этот подход подробно описан Скоттом Сили в по ссылке в ответе Кева : он гарантирует, что каждый вызов операции службы вызывается в новом инициализированном STA потоке.Более сложным, но более масштабируемым решением в этом направлении было бы реализовать пул повторно используемых STA-инициализированных потоков.
  • Размещайте ваши COM-объекты в приложении COM +, чтобы они жили в отдельном процессе DllHost, где COM + (через его абстракцию, называемую Activity) может позаботиться о размещении объектовдля разных запросов в разные STA.

Я не совсем уверен, что вы имеете в виду, когда ссылаетесь на обратные вызовы.Возможно, вы имеете в виду вызовы COM-методов для какого-либо COM-интерфейса, реализованного в вашем управляемом коде, посредством ссылки, передаваемой COM-объектам в качестве аргумента одному из методов COM-объектов: если это так, это должно сработать.Но, возможно, вы имеете в виду что-то еще, и в этом случае, возможно, вы могли бы изменить вопрос, чтобы уточнить.

3 голосов
/ 17 мая 2011

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

Следующий код работает, но он требует, чтобы вы вызывали COM-объект через Dispatcher.

ComWrapper comWrapper;
Thread localThread;
Dispatcher localThreadDispatcher;

public Constructor()
{
   localThread = new Thread(ThreadProc)
   {
       Name = "test"
   };
   localThread.SetApartmentState(ApartmentState.STA);

   AutoResetEvent init = new AutoResetEvent(false);

   localThread.Start(init);

   init.WaitOne();
}

private void ThreadProc(object o)
{
    localThreadDispatcher = Dispatcher.CurrentDispatcher;
    ((AutoResetEvent)o).Set();

    comWrapper = new ComWrapper()

    Dispatcher.Run();

    localThreadFinished.Set();
 }

А затем совершайте звонки следующим образом.

public void UsefulComOperation()
{
    localThreadDispatcher.Invoke(new Action( () => comWrapper.UsefulOperation);
}
...