Есть ли чистый способ обработки исключений bad_alloc, генерируемых внутри C ++ COM-объектов? - PullRequest
2 голосов
/ 10 сентября 2011

Я работаю над различными фильтрами C ++ COM DirectShow, вызываемыми из клиентов C # через COM-взаимодействие. В коде мало использования исключений C ++. Основным исключением является оператор new, который может выдавать исключения bad_alloc.

Есть ли чистый способ обработки исключений bad_alloc способом, который может быть перехвачен клиентом C #?

Может ли новый обработчик вызвать какое-либо исключение SEH, которое может быть перехвачено клиентами взаимодействия COM?

Или было бы лучше связать обратно-совместимую версию без выброски новой в библиотеках Visual Studio и проверять каждое выделение?

Одна утомительная альтернатива - написать попытку / поймать сотни сотен точек входа COM, что кажется бесполезным, поскольку исключения bad_alloc редко восстанавливаемы.

Базовые классы DirectShow обычно проверяют наличие нулевых возвратов от оператора new, поскольку они, кажется, были написаны для более ранних версий Visual C ++, в которых не было исключений bad_alloc.

Ответы [ 4 ]

3 голосов
/ 11 сентября 2011

Контракт COM API требует, чтобы вы не позволяли НИКАКИМ исключениям C ++ проходить через границы COM API.

Это означает, что вам нужно перехватить все C ++ исключения и превратить их в HRESULT перед тем, какони оставляют ваш COM API.

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

В общем, что-то вроде:

 catch (...)
 { 
     return E_FAIL;
 }

в конце каждого из ваших API-интерфейсов COMбудет иметь большое значение для повышения надежности вашего кода.

3 голосов
/ 10 сентября 2011

Вы должны поймать исключение и вернуть E_OUTOFMEMORY. Делайте это только для больших распределений, нет смысла пытаться натолкнуться на неудачу с маленькими. Ничего хорошего никогда не случится после того, как программа израсходует 2 гигабайта виртуальной памяти, вы можете позволить ей умереть. Большие выделения, вероятно, потерпят неудачу из-за фрагментации адресного пространства, все еще может быть много неиспользуемого пространства.

1 голос
/ 12 сентября 2011

Да, обернуть все методы в большие try-catch:

#define BEGIN_COM_METHOD try {

#define END_COM_METHOD \
} catch( const std::bad_alloc& ) {\
     return E_OUTOFMEMORY;\
} catch( ... ) {\
     return E_FAIL;\
}\
return S_OK;

HRESULT YourComClass::SomeMethod()
{
    BEGIN_COM_METHOD
       DoUsefulStuff();
    END_COM_METHOD
}

Используя это, вы больше не распространяете исключения через границу COM.

0 голосов
/ 12 сентября 2011

Спасибо за ответы. Я понимаю, почему исключения C ++ не могут надежно проходить границы COM. Что должен сказать договор COM о структурированных исключениях, проходящих через границу COM?

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

void __cdecl OutOfMemoryHandler()
{
    RaiseException(STATUS_NO_MEMORY, 0, 0, NULL);
}

std::set_new_handler(OutOfMemoryHandler);

Я заметил, что ATL может сделать аналогичный трюк в AtlThrow при обработке E_OUTOFMEMORY HRESULT, вызвав структурированные исключения STATUS_NO_MEMORY, хотя поведение зависит от определенных символов препроцессора.

В нативном коде этот метод также предполагает, что опция компилятора / EHa должна быть настроена так, чтобы происходило раскручивание стека и вызывались деструкторы для структурированных исключений. Я вижу, что это может привести к снижению производительности, если бы я использовал много предложений try / catch, но в текущем контексте я вообще не использую предложения try / catch. Получу ли я прирост производительности от использования / EHa?

В любом случае хорошей идеей может быть настройка параметров компиляции структуры. Другой плакат о переполнении стека описывает неприятную ошибку, когда критическую секцию, обернутую объектом C ++ (обычно используемую в базовых классах DirectShow), не удалось разблокировать при возникновении структурированного исключения в вызываемом модуле. Настройки компилятора были установлены только для обработки исключений в C ++, и объект, обертывающий критическую секцию, не разрушался, когда возникало структурированное исключение и возникала тупиковая ситуация позже.

...