Sunday, July 25, 2010

Ошибка RPC_E_SERVERFAULT

Однажды я столкнулся с проблемой переноса проекта с 2003 на 2008 студию. Был некий тестовый проект. Это ATL exe-сервер, генеренный. Он умел хостить Adobe Flash ActiveX контрол. Так вот, при переносе его на 2008 студию флеш ролик не грузится. Проблема была в вызове метода:

spShockwaveFlash->put_Movie(bsURL);

он возвращал RPC_E_SERVERFAULT. В output пишет:

An internal error occurred in a remote procedure call (RPC).
0x80010105 The server threw an exception.


Хотя другие вызовы методов/свойств почему-то отработали, и запросы QI интерфейсов тоже отработали. Искать причину тяжело, т.к. это уже внутренность флеша и RPC. Путем экспирементов над тестовым проектом нашел решение:

#define _ATL_APARTMENT_THREADED

В итоге причина была в том, что по-умолчанию ATL использует _ATL_FREE_THREADED:

atldef.h
1.// Threading
2.
3.#ifndef _ATL_SINGLE_THREADED
4.#ifndef _ATL_APARTMENT_THREADED
5.#ifndef _ATL_FREE_THREADED
6.#define _ATL_FREE_THREADED
7.#endif
8.#endif
9.#endif


Макрос определяет синхронизацию для регистрации фабрик классов ATL ехе-сервера, синхронизацию в их счетчиках ссылок для CComObjectThreadModel и CComGlobalsThreadModel, и самое главное - определяет инициализацию СОМ для ехе-сервера. Инициализация происходит в конструкторе модуля:

atlbase.h
1.CAtlExeModuleT::CAtlExeModuleT() throw()
2.{
3....
4.HRESULT hr = T::InitializeCom();


далее:

atlbase.h
1....
2.static HRESULT InitializeCom() throw()
3.{
4.#if ((_WIN32_WINNT >= 0x0400 ) || defined(_WIN32_DCOM)) & defined(_ATL_FREE_THREADED)
5. return CoInitializeEx(NULL, COINIT_MULTITHREADED);
6.#else
7. return CoInitialize(NULL);
8.#endif
9.}
10....


Т.е., если вы не задефайнили _ATL_APARTMENT_THREADED для ехе-клиента ActiveX-а, где активикс создается в главном треде, у вас мало того что будут избыточные локи, а еще вы можете выхватить непонятную ошибку типа RPC_E_SERVERFAULT ввиду не правильной инициализации СОМ для главного потока. В msdn по этому поводу ничего не сказано, зато есть сорцы ATL.