
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[216.73.216.30] |
![]() |
|
Страницы: (3) 1 2 [3] все ( Перейти к последнему сообщению ) |
![]() |
Сообщ.
#31
,
|
|
ЫукпШ, думаю, Рихтер подразумевал что в "// что-то делаем с данными" как раз могут быть исключения, особенно, если там вызывается код сторонних библиотек. Даже без учёта этого, очистка, выполняемая в одном месте и запрограммированная один раз, предпочтительнее множественных скопипасченных полуочисток и return в каждом if, а без SEH-блока это только goto или вложенные ifы.
Цитата ЫукпШ @ Вот это в точку. neokoder, ты сам проектируешь дизайн, тебе и определять точки, в которых программа может, перехватив исключение, безопасно продолжить работу. Если у тебя система плагинов, то это будут точки их вызовов, если это сервис, то код обработки запроса от клиентов итп. Как именно реагировать, опять же решать тебе, предварительно почитав описание соответствующих исключений в MSDN и подумав, что тебе конкретно тут предпочтительнее предпринять, может быть в зависмости от конкретного исключения, может быть и независимо от. Об этом может знать только автор конкретной программы - как она должна вести себя в этих случаях. |
Сообщ.
#32
,
|
|
|
Цитата Qraizer @ Даже без учёта этого, очистка, выполняемая в одном месте и запрограммированная один раз, предпочтительнее множественных скопипасченных полуочисток и return в каждом if, а без SEH-блока это только goto или вложенные ifы. Не совсем понял.. Рихтер фактически написал этот текст на С, для демонстрации работы с __try,__finally. Этот приер можно переписать даже так: ![]() ![]() DWORD Funcarama4() { ClassHandle hFile; // класс-хэндл. Но лучше использовать класс-файл ClassVirtualMem pMem; // класс для выделения памяти try { hFile = CreateFile(...); if(hFile == INVALID_HANDLE_VALUE) return ArcticFox; // или даже лучше так // if(!hFile) return ArcticFox; if(!pMem,Allocate(..)) return ArcticFox; if(!ReadFile(...)) return ArcticFox; } catch(...) { return ArcticFox; } // продолжаем что-то делать return bFunctionOk; } |
Сообщ.
#33
,
|
|
|
Мужики, мой вопрос в том что есть такие исключения как EXCEPTION_GUARD_PAGE(для стека) или STATUS_DATATYPE_MISALIGNMENT(актуально для Itanium). Т.е. те проблемы, которые система сама благополучно обработает(устранит) и вернёт в фильтре __except EXCEPTION_CONTINUE_EXECUTION , т.е программа должна продолжиться. Т.е. мне нужно знать те исключения, которые нельзя ни в коем случае обрабатывать с помощью EXCEPTION_EXECUTE_HANDLER.
Цитата Qraizer @ Как именно реагировать, опять же решать тебе, предварительно почитав описание соответствующих исключений в MSDN и подумав, что тебе конкретно тут предпочтительнее предпринять В том то и дело, Qraizer, я не нашёл в MSDN описания по тому или иному SEH исключению когда и почему оно возникает и самое глааное каким образом его система по дефолту обрабатывает? |
![]() |
Сообщ.
#34
,
|
|
neokoder, те исключения, которые система обработает сама, тебе и не передадутся. Сам подумай, иначе бы тебе пришлось к примеру реагировать на каждый page fault. А менеджер виртуальной памяти тогда на что, если его задача быть максимально прозрачным? Вот если ты обратишься к нераспределённому региону памяти, тогда page fault будет передан тебе, потому что в этом случае сам менеджер ещё в ядре исполнит EXCEPTION_CONTINUE_SEARCH. Будь уверен, раз исключение вышло из ядра и дошло до твоих SEH-кадров, оно не обработалось ядром, потому что оно не знает, что с ним делать. Ядро тоже следует принципу
Цитата Qraizer @ Когда оно знает, как поступить с исключением, ты и не подозреваешь, что оно вообще было. Не надо обрабатывать исключение, если ты не знаешь, что с ним делать. Отправляй дальше, там найдётся кому обработать. Добавлено Вообще-то MSDN рассказывает о причинах тех или иных исключений. Может быть не всех, не знаю, тогда прямая дорога к литературе об архитектуре процессора. Другой вопрос, почему оно произошло, и что с ним делать. Но это проблема и с C++-исключениями возникает - обычно если произошло неожидаемое исключение, значит что-то не так с дизайном, и значит надо его пересмотреть и кое-где поправить. В случае с SEH это обычно означает ошибки в программе, и значит нужно найти место, выяснить причину и устранить. Как выяснять и устранять и где удобно перехватывать и безопасно после них восстанавливаться - ну... автору кода лучше знать, готовых рецептов никто не даст. |
Сообщ.
#35
,
|
|
|
Цитата Qraizer @ neokoder, те исключения, которые система обработает сама, тебе и не передадутся. Сам подумай, иначе бы тебе пришлось к примеру реагировать на каждый page fault. Если это так, то это замечательно ![]() Цитата ЫукпШ @ Цитата (neokoder @ 10.04.11, 20:45) 1) Будет ли гарантированно раскручиваться стек и вызываться деструкторы локальных объектов(классов в основном) после Да, и информацию об этом легко найти в Сети. тут, например И в книгах, конечно. Сергей, я задавал вопрос двойной не случайно: Цитата 1) Будет ли гарантированно раскручиваться стек и вызываться деструкторы локальных объектов(классов в основном) после возникновения структурного(SEH) исключения? 2) Если да, то обязательно ли для этого использовать ключ компилятора (/EHa) или можно обойтись(/EHsc)? Если вы утвердительно ответили, то должен сказать, что ДА, но только для ключа /EHa. Я проверил. C ключами /EHs или /EHsc деструкторы не будут вызываться до тех пор пока исключение не будет обработано с помощью __except, а это значит что в той функции которая вызвала исключение все локальные объекты точно не будут освобождены, потому как туда добавить __try/__except не позволит компилер. Я вот написал два варианта обработки исключений для программы, работающей 24x7, первый вариант мой, второй вроде этот RAII как говорил Qraizer. Сразу скажу что я ошибся в том, что мой вариант может поймать необработанно исключение в другом потоке(создать новый поток может,например, сторонняя библиотека, которую мы используем в своей программе). Поймать такое исключение невозможно. Поэтому вся надежда на то что сторонняя программа будет корректно работать ![]() Вообще надеяться на то что деструкторы объектов будут обязательно вызваны в случае возникновения исключения мне кажется слишком рисковано. Потому как вызов деструкторов возможен только для объектов находящихся в стеке, т.е. нельзя например выделить память для класса динамически с помощью new и надеяться что система вызовет деструктор, его придётся вызывать явно. Я в своих программах всегда выделяю память под объекты классы и структуры динамически. А значит ключ /EHa и RAII мне не подойдёт поскольку он не позволит использовать __try/__finally/__except там где под объекты память выделяется динамически. Если полагаться только на деструкторы(2 вариант), то на мой взгляд есть ряд неудобств: 1) В программе где есть только классы это может быть удобно, но если дополнительно необходимо просто выделить динамически массив или какие-то объекты с помощью new, malloc и др., то это уже невозможно, компилятор будет писать ошибку "Невозможно использовать __try в функциях, требующих уничтожения объектов". Не использовать __finally нельзя поскольку при возникновении исключения будет утечка памяти. 2)Чтобы получить код SEH исключения нужно использовать _set_se_translator для каждого потока приложения(в приницпе для 1 варианта также необходимо блоки __try/__except для каждого нового потока). Но использование _set_se_translator не позволит задавать ни EXCEPTION_CONTINUE_SEARCH, ни EXCEPTION_CONTINUE_EXECUTION. 3) Кроме того расход памяти стека может привести к его нехватке, особенно если в программе всего один поток(и значит стек). По мне так привычнее следующий принцип, который кстати и демонстрируется Рихтером. Не важно какой объект и как мы создаём, класс или WinAPI-вызовом или ещё какой либо функцией, правило одно: каждый созданный объект должен иметь свой __finally(обычно в одном __finally разрушается несколько объектов для удобства). Т.е. всё что необходимо это дополнять каждое создание объекта его разрушением в __finally. Вариант№1 ![]() ![]() //compile without (/EH) #include <stdio.h> #include <exception> #include <conio.h> #include <tchar.h> #include <process.h> #include <windows.h> typedef unsigned (__stdcall *PTHREAD_START) (void *); #define my_beginthreadex(psa, cbStack, pfnStartAddr, pvParam, fdwCreate, pdwThreadID)((HANDLE) _beginthreadex((void *) (psa),(unsigned) (cbStack),(PTHREAD_START) (pfnStartAddr),(void *) (pvParam),(unsigned) (fdwCreate),(unsigned *) (pdwThreadID))) //------------------------------------------------------------------------------------- class Test { public: int num; static int counter; Test() { num=counter++; printf("Constructor %d done.\n",num); } ~Test() { printf("Destructor %d done.\n",num); } }; int Test::counter=1; //------------------------------------------------------------------------------------- //глобальные переменные int x,y,z; int a,b,c; volatile BOOLEAN is_lastexceptSEHsys; HANDLE hTestThread; //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- unsigned __stdcall TestThread(void *pvParam) { printf("Test thread started\n"); Sleep(5000); //SEH, деление на ноль a = 1; b = 0; c = a/b; printf("Test thread finished\n"); return 0; } //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- LONG MyCode_ExceptFilter(DWORD except_code,PEXCEPTION_POINTERS except_info) { if (0x20000000&except_code) is_lastexceptSEHsys=false; else is_lastexceptSEHsys=true; //здесь заносим в лог-файл произошедшее исключение, для примера просто печатаю printf("Date/Time Exception: 0x%X; Address: 0x%p\n",except_code,except_info->ExceptionRecord->ExceptionAddress); //отдаём системе на обработку //if (is_lastexceptSEHsys) return EXCEPTION_CONTINUE_SEARCH; //else //return EXCEPTION_EXECUTE_HANDLER; } //------------------------------------------------------------------------------------- void UnknownFunction1_2(int what) { Test *test4=NULL; DWORD dwThreadID; test4=new Test(); __try { //запускаем поток, который может вызвать исключение /* hTestThread=my_beginthreadex(NULL,0,&TestThread,NULL,0,&dwThreadID); if (hTestThread==NULL) { printf("Error: can't start testing thread.\n"); return; } CloseHandle(hTestThread);*/ //исключение bad:alloc (С++ исключение) // __int64 *pI = new __int64[0xFFFFFFF]; //SEH исключение(деление на ноль) x = 1; y = 0; z = x/y; //пользовательское исключение //if (what==1) // throw (char *)"I am throw an exception"; } __finally { if (test4) delete test4; } } //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- // Данная функция может вызывать другие функции, но принцип остаётся один: // дополнять каждое создание объекта его разрушением в __finally. //------------------------------------------------------------------------------------- void UnknownFunction1_1(int what) { Test *test3=NULL; test3=new Test(); __try { UnknownFunction1_2(1); //исключение bad:alloc (С++ исключение) //__int64 *pI = new __int64[0xFFFFFFF]; //SEH, деление на ноль //x = 1; //y = 0; //z = x/y; //пользовательское исключение //if (what==1) // throw (char *)"I am throw an exception"; } __finally { if (test3) delete test3; } } //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- // Функция1. Сначала ловим любое SEH-исключение с корректным освобождением всех объектов // и занесением в лог-файл записи о возникновении исключения. // // компилировать без (/EH), чтобы работал __finally для уничтожения объектов, // деструкторы не вызываются автоматически, но все __finally будут выполнены. // __except необходим чтобы фиксировать произошедшие исключения void MyCode_SafeCall_1() { Test *test2=NULL; test2=new Test(); //далее выполняется неизвестная функция, неизвестно какое оно даст исключение __try { __try { //здесь выполняется основная работа в программе //вызываются различные функции, которые в свою очередь могут вызывать другие //... UnknownFunction1_1(1); } __finally { if (test2) delete test2; } } __except(MyCode_ExceptFilter(GetExceptionCode(),GetExceptionInformation())) { //здесь можно обработать те SEH-исключения и пользовательские, которые мы хотим обработать //также здесь можно было бы ловить C++ исключения, но информация по ним доступна только с помощью catch, //поэтому они обрабатываются в catch printf("Here we can proceed all exceptions what we need\n"); } } //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- //разрушает все глобальные объекты void ShutdownProgram() { //здесь сохраняем, освобождаем и закрываем всё что необходимо //... } //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- // Функция 2. Её основная задача поймать С++-исключение, чтобы работа продолжилась если ошибка устранима // и корректное закрытие программы с записью в лог-файл в случае фатальной ошибки. // но ловит она все исключения поскольку так просто удобнее - всё в одном месте BOOLEAN MyCode_SafeCall() { try { MyCode_SafeCall_1(); } //первые catch должны будут ловить все известные наши собственные исключения(если понадобятся) //продолжать работу программы или нет вопрос на усмотрение разработчика catch(char *mes) { //запись в лог файл о возникшем C++ исключении printf("Date/Time Exception with message: %s\n",mes); } //catch(std::bad_alloc &) //{ // //запись в лог файл о возникшем C++ исключении // printf("Unknown function has raised an bad_alloc exception\n"); //} catch(std::exception &except) { //запись в лог файл о возникшем C++ исключении printf("Date/Time std::exception with message: %s\n",except.what()); //лучше корректно закрыть программу, иначе могут быть непредсказуемые результаты ShutdownProgram(); MessageBox(NULL,_T("Программа вызвала исключение и будет закрыта. В лог-файл записана информация о возникшем исключении."), _T("Неустранимая ошибка в программе"),MB_OK|MB_ICONERROR); return false; } catch(...) { //если исключение системное, здесь мы завершаем работу, но главное корректно, фиксируя всю необходимую информацию, //и с сообщением об ошибке if (is_lastexceptSEHsys) { ShutdownProgram(); MessageBox(NULL,_T("Программа вызвала исключение и будет закрыта. В лог-файл записана информация о возникшем исключении."), _T("Неустранимая ошибка в программе"),MB_OK|MB_ICONERROR); return false; } else { //здесь есть варианты, как поступиить если исключение проскочило, а у нас нет выше кэтчера для его обработки //лучше корректно закрыть программу, иначе могут быть непредсказуемые результаты //запись в лог файл о возникшем C++ исключении или несистемном SEH-исключении printf("Date/Time Unknown C++ exception or not system SEH exception\n"); ShutdownProgram(); MessageBox(NULL,_T("Программа вызвала исключение и будет закрыта. В лог-файл записана информация о возникшем исключении."), _T("Неустранимая ошибка в программе"),MB_OK|MB_ICONERROR); return false; } } printf("We don't exit and can continue work\n"); return true; } //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- int _tmain(int argc, _TCHAR* argv[]) { DWORD ret_code=0; Test *test1=NULL; test1=new Test(); //пример довольно избыточный, но показывающий, что обработка может быть вне главной функции main //можно было бы поставить здесь "верхнюю"(с помощью try/catch) обработку исключений из функции MyCode_SafeCall __try { if (!MyCode_SafeCall()) { ret_code=1; __leave; } //ждём здесь для проверки исключения в потоке //printf("Press any key to close the window..."); //_getch(); } __finally { if (test1) delete test1; } if (ret_code!=0) return ret_code; //если функция выполнилась успешно, то продолжаем выполнять программу //............ printf("Press any key to close the window..."); _getch(); return ret_code; } //------------------------------------------------------------------------------------- Вариант №2. Вроде бы RAII, если нет то скажите почему. ![]() ![]() //Компилировать с ключом /EHa #include <stdio.h> #include <exception> #include <conio.h> #include <tchar.h> #include <process.h> #include <windows.h> typedef unsigned (__stdcall *PTHREAD_START) (void *); #define my_beginthreadex(psa, cbStack, pfnStartAddr, pvParam, fdwCreate, pdwThreadID)((HANDLE) _beginthreadex((void *) (psa),(unsigned) (cbStack),(PTHREAD_START) (pfnStartAddr),(void *) (pvParam),(unsigned) (fdwCreate),(unsigned *) (pdwThreadID))) //------------------------------------------------------------------------------------- class SE_Exception { public: EXCEPTION_POINTERS pExceptionInfo; SE_Exception(EXCEPTION_POINTERS *pExceptInfo) { pExceptionInfo=*pExceptInfo; } ~SE_Exception() { /*printf("SE_Exception destructor\n");*/ } }; //------------------------------------------------------------------------------------- class Test { public: int num; static int counter; Test() { num=counter++; printf("Constructor %d done.\n",num); } ~Test() { printf("Destructor %d done.\n",num); } }; int Test::counter=1; //------------------------------------------------------------------------------------- //глобальные переменные int x,y,z; int a,b,c; volatile BOOLEAN is_lastexceptSEHsys; HANDLE hTestThread; //------------------------------------------------------------------------------------- unsigned __stdcall TestThread(void *pvParam) { printf("Test thread started\n"); Sleep(5000); //SEH, деление на ноль a = 1; b = 0; c = a/b; printf("Test thread finished\n"); return 0; } //------------------------------------------------------------------------------------- void trans_func( unsigned int u, EXCEPTION_POINTERS* pExceptionInfo ) { //printf( "In trans_func.\n" ); throw SE_Exception(pExceptionInfo); } //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- void UnknownFunction2_2(int what) { Test test4; DWORD dwThreadID; /* hTestThread=my_beginthreadex(NULL,0,&TestThread,NULL,0,&dwThreadID); if (hTestThread==NULL) { printf("Error: can't start testing thread.\n"); return; } CloseHandle(hTestThread);*/ //исключение bad:alloc (С++ исключение) //__int64 *pI = new __int64[0xFFFFFFF]; //SEH, деление на ноль x = 1; y = 0; z = x/y; //пользовательское исключение //if (what==1) // throw (char *)"I am throw an exception"; } //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- // Данная функция может вызывать другие функции //------------------------------------------------------------------------------------- void UnknownFunction2_1(int what) { Test test3; UnknownFunction2_2(1); //исключение bad:alloc (С++ исключение) //__int64 *pI = new __int64[0xFFFFFFF]; //SEH, деление на ноль /*x = 1; y = 0; z = x/y;*/ //пользовательское исключение /*if (what==1) throw (char *)"I am throw an exception";*/ } //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- void MyCode2_SafeCall_1() { Test test2; //далее выполняется неизвестная функция, неизвестно какое оно даст исключение //здесь выполняется основная работа в программе //... UnknownFunction2_1(1); } //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- BOOLEAN MyCode2_SafeCall() { try { MyCode2_SafeCall_1(); } //первые catch должны будут ловить наши собственные исключения(если понадобится) //продолжать работу программы или нет вопрос на усмотрение разработчика catch(char *mes) { //запись в лог файл о возникшем C++ исключении printf("Date/Time Exception with message: %s\n",mes); } catch(std::exception &except) { //запись в лог файл о возникшем C++ исключении printf("Date/Time std::exception with message: %s\n",except.what()); MessageBox(NULL,_T("Программа вызвала исключение и будет закрыта. В лог-файл записана информация о возникшем исключении."), _T("Неустранимая ошибка в программе"),MB_OK|MB_ICONERROR); return false; } catch(SE_Exception &seh) { //запись в лог файл о возникшем SEH исключении printf("Date/Time Exception: 0x%X; Address: 0x%p\n",seh.pExceptionInfo.ExceptionRecord->ExceptionCode,seh.pExceptionInfo.ExceptionRecord->ExceptionAddress); MessageBox(NULL,_T("Программа вызвала исключение и будет закрыта. В лог-файл записана информация о возникшем исключении."), _T("Неустранимая ошибка в программе"),MB_OK|MB_ICONERROR); return false; } catch(...) { //запись в лог файл о возникшем C++ исключении printf("Date/Time Unknown exception\n"); MessageBox(NULL,_T("Программа вызвала исключение и будет закрыта. В лог-файл записана информация о возникшем исключении."), _T("Неустранимая ошибка в программе"),MB_OK|MB_ICONERROR); return false; } printf("We don't exit and can continue work\n"); return true; } //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- int _tmain(int argc, _TCHAR* argv[]) { Test test1; //назначаем функцию для SEH исключений _set_se_translator(trans_func); if (!MyCode2_SafeCall()) return 1; printf("Press any key to close the window..."); _getch(); return 0; } //------------------------------------------------------------------------------------- Ещё хотелось бы при возникновении исключения у клиента на комппе записать фрагмент кода, где оно произошло, чтобы легче было найти ошибку. Вот только по адресу который даёт функция GetExceptionInformation() в параметре ExceptionAddress(это по сути значение регистра EIP) я не могу найти свой код. Почему? Знаю, что там надо какое-то преобразование делать, но я не силён в ассемблере. Может кто поможет? |
Сообщ.
#36
,
|
|
|
Цитата Этот приер можно переписать даже так: не все можно переписать так. Иногда WinAPI функция может вызвать исключенние. |
![]() |
Сообщ.
#37
,
|
|
Цитата neokoder @ Безусловно. Хип для того и придуман, чтобы объекты в нём имели глобальное время жизни. Кому нужен объект в хипе, который удаляется по выходу из функции? А иначе с чего он должен удаляться при исключении? Потому как вызов деструкторов возможен только для объектов находящихся в стеке, т.е. нельзя например выделить память для класса динамически с помощью new и надеяться что система вызовет деструктор, его придётся вызывать явно. Я в своих программах всегда выделяю память под объекты классы и структуры динамически. Я забыл, когда последний раз пользовался сырыми указателями для хранения динамических объектов. Либо std::vector<> для массивов, включая простые типы, либо std::auto_ptr<> для одиночных объектов. В ряде случаев boost::shared_ptr<>, но это для более сложных случаев с динамическими объектами, нежели обычное локальное размещение с RAII. Цитата neokoder @ Тоже безусловно. Его задача - преобзаровать SEH в C++ EH, откуда там EXCEPTION_CONTINUE_SEARCH или EXCEPTION_CONTINUE_EXECUTION? Первое определяется типом исключения, покидающего функцию трансляции, и стеком catch-блоков, второе в принципе невозможно. Попробуй придумать логический сценарий, когда после исключения нужно было бы продолжить исполнение. EXCEPTION_CONTINUE_EXECUTION используется настолько редко, что обоснованность его наличия вообще вызывает определённое сомнение, если только речь не идёт об очень низкоуровневых приложениях, типа отладчиков или тестирующих и других системых инструментов. Не даром в C++ EH нет его аналога.Но использование _set_se_translator не позволит задавать ни EXCEPTION_CONTINUE_SEARCH, ни EXCEPTION_CONTINUE_EXECUTION. P.S. Ничего особенного в коде Рихтера не увидел. |
Сообщ.
#38
,
|
|
|
Цитата neokoder @ А значит ключ /EHa и RAII мне не подойдёт поскольку он не позволит использовать __try/__finally/__except там где под объекты память выделяется динамически. Эта проблема легко обходится. Кроме того, VC не запрещает использовать /EHa, try/catch и __try/__except вместе. ![]() Добавлено Цитата neokoder @ 1) В программе где есть только классы это может быть удобно, но если дополнительно необходимо просто выделить динамически массив или какие-то объекты с помощью new, malloc и др., то это уже невозможно, компилятор будет писать ошибку "Невозможно использовать... Ты и сам легко можешь решить такую проблему и навсегда избавиться от такой необходимости. ![]() Добавлено Цитата neokoder @ Ещё хотелось бы при возникновении исключения у клиента на комппе записать фрагмент кода, где оно произошло, чтобы легче было найти ошибку. Вот только по адресу который даёт функция GetExceptionInformation() в параметре ExceptionAddress(это по сути значение регистра EIP) я не могу найти свой код. Почему? Знаю, что там надо какое-то преобразование делать, но я не силён в ассемблере. Может кто поможет? Посмотри примерчик. Возможно, там не всё идеально. А где ты ищешь и не можешь найти свой код ? Прикреплённый файл ![]() |
Сообщ.
#39
,
|
|
|
Цитата ЫукпШ @ Эта проблема легко обходится. Нет ну понятно что всё можно обойти. Я понял как вы делаете - дополнительная функция SomeFunc которая ловит специально SEH-исключения. В этой функции объекты не создаются, их невозможно создать - компилер не позволит. Об этом я и говорил. И вам придётся писать универсальную функцию SomeFunc через которую вызывать ваши функции, чтобы обрабатывать SEH-исключения. Цитата Кроме того, VC не запрещает использовать /EHa, try/catch и __try/__except вместе. Через создание доп. функций да, но не в одной функции. Говоря о невозможности я именно это имел ввиду - в пределах одной функции. Цитата ЫукпШ @ Ты и сам легко можешь решить такую проблему и навсегда избавиться от такой необходимости. В вашей функции SomeFunc, например, вы не сможете создать ни одного объекта. Только об этом я и говорил. Вообще вариантов больше конечно же чем те 2, коорые я привёл. Как кому удобнее так тот и делает. Цитата ЫукпШ @ Посмотри примерчик. Посмотрел, спасибо. Вы можете обнаружить функцию, в которой произошло исключение с помощью добавления в каждую функцию блоков try/catch. Я хотел что-то более универсальное - а именно сдампить участок памяти где произошло исключение, т.е получить дамп кода. Цитата ЫукпШ @ А где ты ищешь и не можешь найти свой код ? В принципе я уже понял, что я не мог найти. Когда вызывается оператор new и происходит исключение bad_alloc система залезает куда-то очень далеко, в свои дебри. А нужная строчка кода с оператором new как бы скрывается. Такие исключения видимо действительно можно локализовать толко в пределах одной функции, например, как это делал ты. Другие исключения, например, деление на ноль даёт непосредственно адрес инструкции кода моей программы, где это исключение произошло, т.е. адрес инструкции IDIV. Для этих исключений получить дамп кода не составляет проблем. Добавлено Цитата Qraizer @ Кому нужен объект в хипе, который удаляется по выходу из функции? Мне например нужен. Функции разные бывают, например основная функция дополнительного потока. И по завершении работы потока объекты должны быть уничтожены. И не обязательно объекты в динамической памяти, это могут быть объекты созданные WinAPI функциями и их тоже надо закрывать/уничтожать. Добавлено Цитата Qraizer @ Попробуй придумать логический сценарий, когда после исключения нужно было бы продолжить исполнение. Пожалуйста, создание таблицы ячеек и выделение физической памяти по мере необходимости. Пример есть в книге Рихтера. |
Сообщ.
#40
,
|
|
|
Цитата neokoder @ Нет ну понятно что всё можно обойти. Я понял как вы делаете - дополнительная функция SomeFunc которая ловит специально SEH-исключения. Не совсем так. я полагаю, что /EHa + try/catch практически достаточно при широком употреблении. Но иногда хочется получить адрес и контекст. Для этих целей можно использовать небольшое количество блков __try/__except, заключающих в свои обьятия крупные участки кода. Возможно, достаточно даже одного такого блока. Или ни одного - можно вообще такие блоки не использовать, а переопределить ExceptionFilter и именно из фильтра писать в лог контекст. |
![]() |
Сообщ.
#41
,
|
|
Цитата neokoder @ Естественно разные. И я ж об этом. Но является ли созданный объект локальным или глобальным, хип не знает. И компилятор тоже не знает. Только ты знаешь. Для локальных используй кассы с владением ресурсов, в частности для ресурсов динамической памяти - смарт-поинтеры, и будет счастье.Мне например нужен. Функции разные бывают, например основная функция дополнительного потока. Цитата neokoder @ Хм... Мы говорим о неожидаемых исключениях или об исключениях как часть бизнес-логики? Если ты ожидаешь исключения, о какой защите от них тогда речь? Пиши функцию, где-нибудь высоко к main(), и там в __except-фильтре фильтруй свою логику на здоровье, зачем тут тебе SEH транслировать? И ЫукпШ об этом же. Впрочем, ты можешь воспользоваться VEH, Vectored Exception Handling. создание таблицы ячеек и выделение физической памяти по мере необходимости. И кстати, для SE-трансляторов также имеет смысл писать классы владения, ставя т.о. их на обработку локально в определённых фунцкиях и восстанавливая предыдущие фильтры по выходу из них. |
Сообщ.
#42
,
|
|
|
Ладно, мужики, спасибо. Я же говорю, кому как удобнее тот так и делает. Цель то одна - кооректное освобождение всех ресурсов при возникновении исключения и запись его в лог-файл. А вариантов реализации несколько.
Я получил ответы на все свои вопросы и даже больше, так что тему можно закрывать. А вам спасибо, подниму и без того огромный ваш рейтинг ![]() |
Сообщ.
#43
,
|
|
|
Почитайте заметку: Возможен ли вызов исключения? Думаю, инфа будет полезна.
|