Как создать оболочку C ++ Dll, которая перехватывает все исключения? - PullRequest
13 голосов
/ 12 февраля 2009

Как видно из заголовка, мы ищем способ перехватить все исключения из фрагмента кода C ++ и обернуть его в DLL. Таким образом, мы можем защитить приложение, которое использует эту DLL, от любых ошибок, возникающих в этой DLL.

Однако, это не представляется возможным с C ++ под Windows.

Пример:

void function()
{  
    try  
    {    
        std::list<int>::iterator fd_it;
        fd_it++;  
    } catch(...) {}
}

Возникающее исключение не перехватывается ни стандартным блоком try / catch C ++, ни какой-либо функцией трансляции SEH, установленной _set_se_translator(). Вместо этого происходит сбой библиотеки DLL, и программа, которая использует библиотеку DLL, прерывается. Мы скомпилировали с Visual C ++ 2005, с опцией / SHa. Кто-нибудь знает, возможно ли в C ++ / Win32 уловить подобные проблемы и создать надежную оболочку DLL?

Ответы [ 8 ]

24 голосов
/ 12 февраля 2009

Единственный способ создать надежную оболочку DLL - это загрузить ошибочную DLL в другой процесс, чтобы в случае сбоя он не затормозил ваш основной процесс.

Поймать все исключения C ++ кажется разумным, но перехват всех структурированных исключений - это другая история. SEH может показаться , что поможет вам пройти большую часть пути, потому что он позволяет выявлять нарушения доступа, исключения деления на ноль и т. Д.

Но что, если глючная DLL коснется незафиксированной страницы из стека другого потока? Доступ к памяти приведет к сбою страницы, будет вызван обработчик исключений, и теперь эта страница больше не является защитной страницей. Когда этому потоку нужно увеличить стек, он получит нарушение прав доступа, и процесс завершится сбоем. ( Эти сообщений описывают этот случай более подробно.)

Другая вероятная проблема: глючная DLL-библиотека аварийно завершает работу, удерживая объект синхронизации, но вы используете SEH, чтобы перехватить исключение. Если ваш процесс пытается получить тот же объект синхронизации, он блокируется вместо сбоя. Общий объект синхронизации может быть частью среды выполнения C или ОС: что, если глючная DLL 1 загружает глючную DLL 2, которая вылетает в DllMain(), пока глючная DLL 1 удерживает блокировку загрузчика? Будет ли ваш процесс тупиковым при следующей загрузке DLL?

Для получения дополнительной информации о том, почему это (и такие функции, как IsBadReadPtr(), которые имеют аналогичные проблемы) является неправильным использованием SEH:

8 голосов
/ 12 февраля 2009

В Windows C ++ имеет 2 различных стиля исключений: исключения C ++ и SEH.

SEH - это форма исключений только для окон ( несколько сродни сигналам в UNIX). Это скорее исключение системного уровня. Они будут выброшены для таких операций, как недопустимый доступ к указателю, проблемы с выравниванием и т. Д. *

Если вы хотите перехватить каждое исключение, которое может быть сгенерировано приложением C ++ в Windows, вам нужно поймать оба. К счастью, есть способ смешать использование исключений C ++ и SEH. Недавно я написал подробное сообщение в блоге об этом, оно должно вам помочь.

http://blogs.msdn.com/jaredpar/archive/2008/01/11/mixing-seh-and-c-exceptions.aspx

4 голосов
/ 13 февраля 2009

Код ниже взят из Zeus IDE. Он будет перехватывать любые сгенерированные Windows исключения:

Шаг № 1: Определение функции фильтра исключений

  DWORD ExceptionFilter(EXCEPTION_POINTERS *pointers, DWORD dwException)
  {
    //-- we handle all exceptions
    DWORD dwResult = EXCEPTION_EXECUTE_HANDLER;

    switch (dwException)
    {
      case EXCEPTION_ACCESS_VIOLATION:
      case EXCEPTION_DATATYPE_MISALIGNMENT:
      case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
      case EXCEPTION_FLT_DENORMAL_OPERAND:
      case EXCEPTION_FLT_DIVIDE_BY_ZERO:
      case EXCEPTION_FLT_INEXACT_RESULT:
      case EXCEPTION_FLT_INVALID_OPERATION:
      case EXCEPTION_FLT_OVERFLOW:
      case EXCEPTION_FLT_STACK_CHECK:
      case EXCEPTION_FLT_UNDERFLOW:
      case EXCEPTION_INT_DIVIDE_BY_ZERO:
      case EXCEPTION_INT_OVERFLOW:
      case EXCEPTION_PRIV_INSTRUCTION:
      case EXCEPTION_NONCONTINUABLE_EXCEPTION:
      case EXCEPTION_BREAKPOINT:
        dwResult = EXCEPTION_EXECUTE_HANDLER;
        break;
    }

    return dwResult;
  }

Шаг № 2: Оберните код в __ try и __ кроме , как показано ниже:

  __try
  {
    // call your dll entry point here
  }
  __except(ExceptionFilter(GetExceptionInformation(), 
                           GetExceptionCode()))
  {
    //-- display the fatal error message
    MessageBox(0, "An unexpected error was caught here!", 
               "Unexpected Error", MB_OK);
  }
4 голосов
/ 12 февраля 2009

Увеличение итератора в стандартном контейнере библиотеки никогда не вызовет исключения C ++. Это может дать вам неопределенное поведение.

3 голосов
/ 12 марта 2010

Конрад Рудольф: Конечно, этот код содержит «логическую ошибку», чтобы проиллюстрировать проблему, которая может возникнуть. Как человек говорит, что хочет быть в состоянии защитить свою DLL от любых возможных ошибок. Вы не думаете, что это законный вопрос? Слышал о продавце продукции. Некоторые из нас живут в реальном мире и живут с реальными проблемами. Просто невозможно решить проблемы всех остальных

3 голосов
/ 12 февраля 2009

Этот код содержит логическую ошибку, если она вообще есть. Поскольку логическая ошибка представляет собой ошибку, не проглатывайте исключение - исправьте ошибку !

Конечно, это относится к конкретному коду. Другие предложили более общий совет. Однако я обнаружил, что многие люди на самом деле делают предпочитают отлавливать исключения, а не исправлять логические ошибки, и это просто недопустимо.

2 голосов
/ 12 февраля 2009

Вы смотрели на функцию Windows API SetUnhandledExceptionFilter?

Обычно я вызываю его в функции DllMain, и он генерирует мини-дамп при сбое DLL. Однако: (а) я не знаю, перехватывает ли это исключение приложений, а также исключения DLL, и (б) я не знаю, можно ли вернуть обработчик таким образом, чтобы выполнение программы могло продолжаться. Документы говорят да, но я никогда этого не делал.

1 голос
/ 20 февраля 2009

Что вы собираетесь делать после того, как поймаете исключение (особенно для исключений SEH)?

На самом деле вы не можете делать предположений о состоянии процесса, реально единственно возможный вариант - это (необязательно) выгрузить ядро ​​и выйти.

Любая попытка и дальнейшие действия обязательно вызовут у вас проблемы в долгосрочной перспективе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...