CoInitializeEx возвращает S_OK при вызове внутри COM-объекта

Некоторое время назад мне пришлось модифицировать старую COM DLL (Visual C ++ 2010, ATL), перенеся ее из модели потоков «Квартира» в «Оба», то есть теперь ее можно вызывать из потоков STA и MTA без сериализации вызовов (конечно, Мне пришлось добавить внутреннюю синхронизацию для общих данных). Это, в свою очередь, вызывало проблемы при преобразовании событий COM (точек подключения) в события .NET, когда моя DLL вызывается через Interop из приложения .NET (я должен поддерживать STA и MTA даже в приложениях .NET).
Чтобы решить эти проблемы, я изменил способ запуска событий.

1) Если DLL вызывается в контексте STA, она работает как прежде, то есть создает невидимое окно, затем, когда событие должно быть вызвано, оно вызывает PostMessage для этого окна, тогда основной поток STA вызывает фактическое событие. код запуска, то есть функции-члены CProxy_IMyEventFiringInterface (CProxy_IMyEventFiringInterface является производным от IConnectionPointImpl).

2) если DLL вызывается в контексте MTA, у меня нет основного потока COM, и я не могу сделать PostMessage, поэтому я использую созданный мной собственный поток и позволяю этому потоку вызывать функции IConnectionPointImpl.

Но AFAIK не существует Windows API, который определяет, является ли вызывающий поток STA или MTA. Многие сайты предлагают использовать CoInitializeEx так:

HRESULT hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
switch(hr)
{
case S_FALSE:  // we are in a Multi-Threaded Apartment (MTA)
CoUninitialize(); // each successful call to CoInitialize or CoInitializeEx, including any call that returns S_FALSE, must be balanced by a corresponding call to CoUninitialize
break;
case RPC_E_CHANGED_MODE:  // we are in a Single-Threaded Apartment (STA)
break;
default:  // IMPOSSIBLE!!!!
}

Я решил поместить этот вызов в CoInitializeEx в CMyComComponent :: FinalConstruct.
Все работало нормально … до сегодняшнего дня. В клиентском сценарии я вижу из своего инструмента трассировки, что для определенного приложения .NET EXE (у меня нет исходного кода) приведенный выше код заканчивается в ветви по умолчанию, потому что CoInitializeEx вернул S_OK.
Как это может быть возможным? Документация Microsoft гласит, что S_OK означает «Библиотека COM была успешно инициализирована в этом потоке», но я ВНУТРИ COM-объекта, библиотека COM ДОЛЖНА быть уже инициализирована!
Кстати, ветвь по умолчанию не закрывает приложение, но, поскольку S_OK был возвращен, он называется CoUninitialize (каждый успешный вызов CoInitialize или CoInitializeEx, включая любой вызов, который возвращает S_FALSE, должен быть сбалансирован соответствующим вызовом CoUninitialize) и затем DLL продолжает предполагать STA (неправильный ход задним числом). Но это не STA: более поздний PostMessage возвращает FALSE.

Я могу просто изменить код, чтобы использовать MTA по умолчанию, если CoInitializeEx (NULL, COINIT_MULTITHREADED) возвращает S_OK, я должен был сделать это с самого начала.
Но я также хочу быть уверен, что это правильно, чтобы избежать дальнейших проблем в будущем.
большое спасибо
Деметрио

6

Решение

Это возможно, когда вы находитесь на неявный MTA нить. Правильная функция такова:

BOOL IsMultiThreadedApartment() throw()
{
HRESULT nResult = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if(SUCCEEDED(nResult))
CoUninitialize();
return SUCCEEDED(nResult);
}

Вы попали в эту необычную ситуацию, потому что приложению-вызывающему не удалось инициализировать COM в потоке, в котором он создает экземпляр вашего класса. Однако имейте в виду, что если позднее вызывающая сторона инициализирует поток как STA, вы можете попасть в сложную ситуацию, когда ваш класс будет работать в режиме MTA в потоке STA. дела CoInitializeEx С другой стороны, вы сами можете вызвать сбой вызывающей стороны из-за поздней инициализации COM.

5

Другие решения

Других решений пока нет …