Про SEH написано много (Iczelion, Matt Pietrek 1, 2, 3), но весьма замудрёно, популярнее в: SEH&VEH, \masm32\vkdebug\example\demo1\dbdemo.asm, \masm32\vkdebug\dbproc\trapex.asm Однако попытка прикрутить к программе простейший SEH обработчик привела к интересным результатам, которые я не нашёл (может плохо искал?) в статьях про SEH. Простое возвращение управления в цикл выборки сообщений, через коррекцию eip в CONTEXT (пример SEH_1 в аттаче) работает, но естественно приводит к утечке стека, поскольку исключение происходит в функции, и в стеке находятся её параметры, локальные данные, адрес возврата, да и системный обработчик SEH "мусорит" . Элементарная коррекция esp в CONTEXT приводит к нарушению работы SEH. Дело в том, что хитрая windows после вызова callback функции SEH_proc, но до восстановления регистров из CONTEXT, достраивает цепочку SEH новыми точками в стеке. После исправления esp, добавленная виндой точка SEH оказывается за пределами стека и при следующем исключении win обработчик считает её недействительной (пример SEH_2a). Если стек не исправлять, а в метке SEH_Exit: после восстановления предыдущего обработчика вызвать сбой (пример SEH_1a), то можно увидеть процесс раскрутки этой цепочки: в диалоге исключения придётся нажимать Нет несколько раз (зависит от количества нажатых Да и версии win) пока винда будет извлекать из цепочки SEH всё, что она туда напихала, а только затем она передаст управление обработчику по умолчанию и окончательно уронит программу. Таким образом, чтобы корректно вернуть управление в цикл выборки сообщений нужно исправлять не только указатель стека, но и указатель fs:[0], причём делать это не в callback функции а после выхода из неё . Пример SEH_2 в аттаче. Описанный здесь SEH совсем не похож на "классический" tru\exception, имхо потому и так сильно отличается от описанного у Matt Pietrek, впрочем это всего-лишь другой ракурс того же SEH. ЗЫ: наличие на машине CrashGuard является хорошим индикатором качества восстановления предидущего SEH: если SEH восстановлен не корректно - программа падает с системным сообщением, а если правильно - то как и положено через CrashGuard. ЗЗЫ: Впрочем достаточно следить за регистрами и стеком в отладчике, главное не перепутать ссылки на свой SEH со ссылками на SEH отладчика, которые легко отличить по адресам указывающим далеко за пределы тестируемой программы.
Всё что я знаю про SEH, написано здесь: http://www.droopy.narod.ru/develop/SEH.htm Не знаю, мне кажется, всё доступно объяснено
многа буков ниасилил какие траблы с сехом? у всех все работает гуд... скомпиль ц пример с __try __exept из MSDN и глянь асм... сравни с тем что ты делаешь... и могут глюки быть если /safeseh:no не указать
droopy В твоей ссылке того, что я написал в #1 тоже нет - если бы где-то нашёл - то не постил fr0b-p с С проблем нет - это есно но при использовании асм варианта альтернативного к __try __exept обнаружил любопытный эффект и поделился с народом, может кому пригодиться, а может кто ещё дельное наблюдение добавит
мне лично кажется что ты путаешь SEH котрый try/except и UnhandledExceptionFilter то что у тебя в примерах - это функционально UnhandledExceptionFilter а потоковый SEH просто не предназначен для того что ты хочешь
z0mailbox Спасибо, кое-что прояснилось ) Хотя вопрос зачем винда достраивает seh цепочку в #1, и почему XP надстраивает больше чем 98 мне по прежнему кажется любопытным. зы: исправил ссылку
А лично меня больше интересует, почему описатель SEH не может быть за пределами стека ? Может кто вникал в суть
_Serega_ Основная суть в том, что винда при обработке исключения сравнивает FS:[0] с esp и адресом начала стека и если адрес из FS:[0] в этом диапазоне не лежит, то наотрез отказывается вызывать заданную в FS:[0] callback функцию. А вот зачем она это делает остаётся только догадываться , возможно лишняя страховка при оживленни глючной проги, возможно действительно для совмещения раскрутки с поуровневой очисткой стека, может ещё какие соображения имеет... а нам оставляет только признать это за факт ЗЫ: Ещё одно соображение - цепочка SEH в стеке действует как разметка и облегчает обработчику исключения задачу проаналировать параметры переданные упавшей функции.
z0mailbox Проверил версию с UnhandledExceptionFilter - формат callback функции другой, а грабли те-же самые, точнее ещё круче ( Винда точно также достраивает цепочку SEH в стеке, только в отладчике это уже не посмотришь, и если при возврате в безопасную точку корректировать стек, то при следующем исключении прога падает минуя обработчик (пример SEH_4), причём в w98 через системное окошко, а в XP через зависание . Без коррекции стека (пример SEH_3) всё работает, но есно с утечкой стека. Попытка восстановить UnhandledExceptionFilter сразу после выхода из обработчика исключения результата не даёт (пример SEH_5) Т.е. получается единственный корректный способ вернуться в безопасную точку - пример SEH_2 в #1. ЗЫ: Странно, что сие вопиющее бзобразие не освещено в низкоуровневых статьях про SEH
Y_Mur А ты про раскрутку стека и "всякие там" EH_UNWINDING, RtlUnwind и т.п внимательно читал ? Лично я нет , но насколько понимаю - если твой обработчик находится не в вершине цепочки, то он должен инициировать раскрутку вышестоящих обработчиков, чтобы они могли выполнить блоки finally и удалить себя из цепочки
leo C EH_UNWINDING, RtlUnwind пока не разобрался, это ещё предстоит, но фишка в том, что глючат не предшествующие обработчики, которые собственно в моих простейших примерах вроде как никто не ставил, а последующие, которые винда зачем-то добавляет после моего возврата из callback функции . Ещё глянул От зеленого к красному 3, там есть пример где якобы "можно исправить ошибку и продолжить выполнение с безопасного места", на поверку пример оказался одноразовым аналогом моего SEH_2a, и работает тот пример соответственно лишь потому, что в нём не предусмотрено повторное исключение после возврата из SEH
Y_Mur Сейчас я глянул на приведенные вами примеры. Первое что мне бросилось в глаза: вы используете API-функцию MessageBox в обработчике SEH, а исключение вызываете в обработчике оконного сообщения. Это черева-то не очевидными последствиями. Если вам о них ничего не известно, то прочитайте об этом вот этот топик: http://www.wasm.ru/forum/viewtopic.php?id=7041
Y_Mur Если я правильно понял, винда это делает для того, чтобы иметь возможность обработать исключение в вашем SEH-обработчике. З.Ы.: Чтобы более подробно разобраться в этом вопросе, прочитайте третью часть статьи Мэтта Питрека (Matt Pietrek). Там приведен примерный псевдокод функции KiUserExceptionDispatcher. З.З.Ы.: В качестве дополнительного материала полезного для изучения SEH рекомендую статью Volodya, "Об упаковщиках в последний раз", часть вторая.
Oleg_SK Нет не правильно - надстройка происходит не до, а после вызова моей callback функции (см. #1) и соответсвенно перехватить ошибку в мём обработчике таким способом проблематично... За предупреждение про MessageBox, спасибо - учту в реальных прогах , хотя в данной теме это не критично. В псевдокоде KiUserExceptionDispatcher из ntdll.dll значится установка только одной точки SEH перед вызовом callback функции, которая изничтожается незамедлительно после возврата из неё, попутно очищая стек после функции в С стиле. А про точки SEH которые ставятся после callback и не уничтожаются после выхода из системного обработчика там ничего нет, либо Питрек что-то недописал, либо это проделки уровня ядра... А статья от Volodya, хороший ответ на вопрос от _Serega_, но ответа на свой вопрос, я там не нашёл...
Нда грабли с MessageBox прикольные ) Конечно bogrus в #18 соответствующего топика всё разьяснил, но наблюдать как MessageBox полностью перехватывает выборку сообщений (пример SEH_8) забавно, но и обойти это легко, поместив проверку флага в саму процедуру окна (пример SEH_9). А чтобы окно перерисовывалось можно блокировать флагом только критичные к параллельному выполнению с SEH части оконной процедуры
Итак если обработчик SEH возвращает управление в безопасную точку программы не корректируя стек и не уничтожая после этого EXECEPTION_REGISTRATION, то винда при каждом исключении в программе достраивает цепочку SEH, причём все эти дополнительные обработчики не обрабатывают новые исключения, а передают их по цепочке, пока не сработает SEH самой программы (пример SEH_12.asm, SEH_12a.exe) Однако, в классическом варианте, на основе _try, _finally, _except (пример SEH_10.asm) этого безобразия не наблюдается независимо ни от количества ошибок, ни от того изменяется eip в пределах защищённого блока или управление возращается на команду вызвавшую сбой. Почему так происходит и как винда определяет когда ей нужно достраивать цепочку, а когда нет? Предположение о том, что винда всегда достраивает цепочку SEH после возврата из обработчика, а снимает свою надстройку перехватывая запись в fs:[0] не подтвердилось (пример SEH_13). Тем не менее реализовать SEH, который при почти любом сбое обеспечивает продолжение работы с безопасной точки совсем не сложно - достаточно после возврата из обработчика SEH исправить указатель стека и указатель в fs:[0] (пример SEH_12b.exe) Код (Text): SEH_Restart: ; Позиция для продолжения работы в случае исключения mov esp, [SEH_esp] ; Восстановить позицию стека mov fs:[0], esp ; Восстановить свой SEH Главное избежать соблазна исправить esp не выходя из обработчика SEH: Код (Text): SEH_proc proc C pExcept: dword, pFrame: dword, pContext: dword, pDispatch: dword ; ВНИМАНИЕ callback функция должна сохранять ebx, esi, edi mov edx, pContext mov [edx].CONTEXT.regEip, offset SEH_Restart ; mov eax, [SEH_esp] ; или mov eax, [pFrame] ; mov [edx].CONTEXT.regEsp, eax ; < --- так корректировать esp нежелательно !!! ; mov fs:[0], eax ; < --- а эта коррекция не сработает !!! mov eax, ExceptionContinueExecution ; Флаг продолжить выполнение программы ret SEH_proc endp Как уже отмечалось в #14, эта ошибка встречается в примерах, где SEH используется однократно и потому порча fs:[0] на выходе из SEH остаётся незамеченной И ещё, как видно из SEH_12b, SEH_10 и SEH_13, винда хотябы раз (98) или два раза (XP) добавляет свой SEH после установленного программой Ага так это и есть разгадка появления цепочки !!! - винда делает вид, что защищает своим SEHом оконную функцию WinProc и когда возврат происходит в цикл выборки сообщений (минуя корректное завершение WinProc) то системный SEH остаётся не снятым Чтобы убедиться в этом достаточно закомментировать метку SEH_Restart: перед циклом выборки сообщений и снять комментарий с метки SEH_Restart = $ перед ret в WinProc - всё безобразие с утечкой стека и достраиванием цепочек тут же прекращается (SEH_12c.exe) Однако, если нужна страховка достаточно сложной программы от неизвестного сбоя на неизвестно каком уровне вложенности подпрограммы, то имхо надёжнее всё таки почистить стек самостоятельно и продолжить работу "с чистого листа"