[ATL][trick] обработка исключений
От: Alexander G Украина  
Дата: 25.11.09 14:46
Оценка: 103 (7)
ATL бросает исключения. В основном это <atlsecurity.h>, также при преобразовании строк при ошибке с локалью. Например, CString::MakeLower бросает. В дебаге там сначала идут ассерты, в релизе — сразу исключения.

Кидает он их хитро:

1. Если требуется, чтобы он кидал пользовательские исключения, можно определить _ATL_CUSTOM_THROW и определить свою AtlThrow(HRESULT hr).
2. Если требуется отказаться от C++ исключений, определить _ATL_NO_EXCEPTIONS и он кидает SEH.
3. Иначе кидает CAtlException.

CAtlException неприятная штука, тем, что это не наследник std::exception, а всего лишь тривиальный враппер для HRESULT. Раузмеется, сам HRESULT в качестве исключения был бы ещё хуже, но даже CAtlException требует своего catch. Поэтому целесообразно определить свой AtlThrow, бросающий наследника std::exception.

Т.к. оригинальный AtlThrow — макро, свой тоже можно определить как макро, дополнительно передав __FILE__ и __LINE__ и получив больше контекста. boost::exception подходит для этой цели, но непосредственно BOOST_THROW_EXCEPTION не подходит, т.к. этот макрос создаёт локальные объекты, требующие раскрутки, а AtlThrow используется в ATL совместно с SEH-обработкой.

Ниже вроде рабочий пример, пробовать в релизе, чтобы сразу был ексепшн, без ассерта.

#include <Windows.h>
#include <iostream>
#include <limits>
#include <stdexcept>
#include <boost/exception.hpp>

__declspec(noinline, noreturn) void
  MyAtlThrow(HRESULT hr, char const * func, char const* file, int line);

#define _ATL_CUSTOM_THROW

#define AtlThrow(hr) MyAtlThrow( \
  hr, BOOST_CURRENT_FUNCTION, __FILE__, __LINE__)

#include <atlbase.h>
#include <atlstr.h>

//////////////////////////////////////////////////////////////////////////

class com_error : public std::exception
{
public:
  explicit com_error(HRESULT hr) : hr_(hr) {}

  const char *what( ) const  {
    return "TODO: Use FormatMessage with hr_";
  }

private:
  HRESULT hr_;
};

__declspec(noinline, noreturn) void
MyAtlThrow(HRESULT hr, char const * func, char const* file, int line)
{
  boost::throw_exception(boost::enable_error_info(com_error(hr)) 
    << boost::throw_function(func) 
    << boost::throw_file(file) 
    << boost::throw_line(line));
}

int main()
{
  try
  {
    CString s;
    int i = -1;
    s.Tokenize(_T("wr"), i);
  }
  catch (std::exception& ex)
  {
    std::cout << boost::diagnostic_information(ex);
  }
}
Русский военный корабль идёт ко дну!
Re: [ATL][trick] обработка исключений
От: SchweinDeBurg Россия http://zarezky.spb.ru/
Дата: 25.11.09 15:13
Оценка: 7 (1)
Здравствуйте, Alexander G, Вы писали:

AG>Ниже вроде рабочий пример, пробовать в релизе, чтобы сразу был ексепшн, без ассерта.


Интересно, только имя не очень удачное ИМХО — будет "сливаться" с _com_error, генерирущимся при использовании директивы #import.
[ posted via RSDN@Home 1.1.4 stable SR1 r568, accompanied by silence ]
- Искренне ваш, Поросенок Пафнутий ~ ICQ#116846877
In Windows, there’s always a catch… © Paul DiLascia
Re[2]: [ATL][trick] обработка исключений
От: Alexander G Украина  
Дата: 25.11.09 15:22
Оценка: 92 (2) +1
Здравствуйте, SchweinDeBurg, Вы писали:

SDB>Интересно, только имя не очень удачное ИМХО — будет "сливаться" с _com_error, генерирущимся при использовании директивы #import.


Да, надо как-то так (заодно старые catch(CAtlException) не ломаем):
class CAtlExceptionEx 
  : public std::exception
  , public CAtlException
{
public:
  explicit com_error(HRESULT hr) : CAtlException(hr) {}

  const char * what( ) const  {
    ...
  }
};


Кстати, как бы ещё этот _com_error "победить" ?
Русский военный корабль идёт ко дну!
Re[3]: [ATL][trick] обработка исключений
От: SchweinDeBurg Россия http://zarezky.spb.ru/
Дата: 25.11.09 15:57
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Да, надо как-то так (заодно старые catch(CAtlException) не ломаем):


О, то что доктор прописал!

AG>Кстати, как бы ещё этот _com_error "победить" ?


В каком смысле? Тоже на потомка std::exception перейти?
[ posted via RSDN@Home 1.1.4 stable SR1 r568, accompanied by silence ]
- Искренне ваш, Поросенок Пафнутий ~ ICQ#116846877
In Windows, there’s always a catch… © Paul DiLascia
Re[4]: [ATL][trick] обработка исключений
От: Alexander G Украина  
Дата: 25.11.09 16:09
Оценка:
Здравствуйте, SchweinDeBurg, Вы писали:

AG>>Кстати, как бы ещё этот _com_error "победить" ?


SDB>В каком смысле? Тоже на потомка std::exception перейти?


Ну да.

Я вообще не использую catch(...), т.к. (1) нельзя ловить некоторые исключения, которые ловятся фреймворками, например boost::thread_interrupted; (2) возможна компиляция с /EHa, но нельзя просто так ловить многие SEH исключения, особенно stack overflow; (3) если где-то бросаются инты, то об этом нужно знать. Соответсвенно, мне хочется, чтобы catch(std::exception&) ловил всё, что имеет смысл ловить.
Русский военный корабль идёт ко дну!
Re[5]: [ATL][trick] обработка исключений
От: SchweinDeBurg Россия http://zarezky.spb.ru/
Дата: 25.11.09 16:16
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Ну да.


ИМХО тут даже с макросами будет сложно нашаманить, учитывая что этот код генерит компилятор.

AG>Я вообще не использую catch(...),


Аналогично.

AG>нельзя просто так ловить многие SEH исключения, особенно stack overflow;


Я пользуюсь _set_se_translator(): CSeException (MFC, но сути дела это не меняет — тамошний CException это "аналог" std::exception в том смысле, что является базовым типом для всех исключений).
[ posted via RSDN@Home 1.1.4 stable SR1 r568, accompanied by silence ]
- Искренне ваш, Поросенок Пафнутий ~ ICQ#116846877
In Windows, there’s always a catch… © Paul DiLascia
Re[6]: [ATL][trick] обработка исключений
От: Юрий Жмеренецкий ICQ 380412032
Дата: 26.11.09 03:54
Оценка: 15 (1) +1
Здравствуйте, SchweinDeBurg, Вы писали:

SDB>Я пользуюсь _set_se_translator(): CSeException


extern "C" int WINAPI _tWinMain( ... )
{
   _set_se_translator(&CSeException::Translator);

   ...
}


Лучше бы в конце функции установить старый обработчик (который возвращает _set_se_translator), иначе вызывающий может удивиться. Для WinMain это так критично, а например в COM, модуль, содержащий реализацию CSeException::Translator может быть выгружен в самый неподходящий момент.

Можно написать какой-нибудь scoped_translator, который к констукторе вызывает _set_se_translator и запоминает старый обработчик, а в деструкторе — восстанавливает.
Re[7]: [ATL][trick] обработка исключений
От: SchweinDeBurg Россия http://zarezky.spb.ru/
Дата: 26.11.09 09:08
Оценка:
Здравствуйте, Юрий Жмеренецкий, Вы писали:

ЮЖ>Можно написать какой-нибудь scoped_translator, который к констукторе вызывает _set_se_translator и запоминает старый обработчик, а в деструкторе — восстанавливает.


Согласен, да и вообще этот пример давно пора поправить — AfxWinMain() там должен быть.
[ posted via RSDN@Home 1.1.4 stable SR1 r568, accompanied by silence ]
- Искренне ваш, Поросенок Пафнутий ~ ICQ#116846877
In Windows, there’s always a catch… © Paul DiLascia
Re[6]: stack overflow
От: Alexander G Украина  
Дата: 27.11.09 19:46
Оценка: 14 (1)
Здравствуйте, SchweinDeBurg, Вы писали:

AG>>нельзя просто так ловить многие SEH исключения, особенно stack overflow;


SDB>Я пользуюсь _set_se_translator(): CSeException (MFC, но сути дела это не меняет — тамошний CException это "аналог" std::exception в том смысле, что является базовым типом для всех исключений).


Кстати, именно stack overflow просто так ловить нельзя, даже преобразовав его в C++ исключение.
См. _resetstkoflw.
См настройку boost::regex BOOST_REGEX_RECURSIVE и BOOST_REGEX_HAS_MS_STACK_GUARD.
Русский военный корабль идёт ко дну!
Re[5]: [ATL][trick] обработка исключений
От: remark Россия http://www.1024cores.net/
Дата: 27.11.09 20:22
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Я вообще не использую catch(...), т.к. (1) нельзя ловить некоторые исключения, которые ловятся фреймворками, например boost::thread_interrupted; (2) возможна компиляция с /EHa, но нельзя просто так ловить многие SEH исключения, особенно stack overflow; (3) если где-то бросаются инты, то об этом нужно знать. Соответсвенно, мне хочется, чтобы catch(std::exception&) ловил всё, что имеет смысл ловить.



Отделить boost::thread_interrupted от, например, CAtlException на общих основаниях навряд ли получится. Однако по крайней мере можно ловить все С++ исключения — это SEH исключения с кодом 0xE06D7363.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[6]: [ATL][trick] обработка исключений
От: Alexander G Украина  
Дата: 27.11.09 20:48
Оценка:
Здравствуйте, remark, Вы писали:

R>Отделить boost::thread_interrupted от, например, CAtlException на общих основаниях навряд ли получится.

Поймать CAtlException и не поймать boost::thread_interrupted можно двумя способами: (1) явно поймать CAtlException (2) явно поймать boost::tread_interrupted и сразу же его бросить повторно:
catch(boost::thread_interrupted&) { throw; } 
catch(...) {}
Мне второй способ не особо нравится (хотя про boost::tread_interrupted приходится помнить в любом случае).

R>Однако по крайней мере можно ловить все С++ исключения — это SEH исключения с кодом 0xE06D7363.

Ну да. Казалось бы, пользы немного, их нужно ловить как С++ исключения, чтобы хотя бы деструктор вызвать, однако такому __except есть применение. В некторых случаях я ловлю C++ исключения, вызываю для них MiniDumpWriteDump и бросаю обратно, после чего они штатно обрабатываются. Что-то кроме 0xE06D7363 ловить в тех местах не имеет, т.к. в этом случае приложение всё равно аврийно завершится дамп будет сделан в unhandled exception filter.


R>

Русский военный корабль идёт ко дну!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.