Как создать обертку С++ Dll, которая ловит все исключения?

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

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

Пример:

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

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

Ответ 1

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

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

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

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

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

Ответ 2

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

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

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

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

Ответ 3

Приращение итератора в стандартном контейнере libary никогда не будет вызывать исключение С++. Это может привести к поведению undefined.

Ответ 4

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

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

Ответ 5

Ниже приведен код из 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);
  }

Ответ 6

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

Ответ 7

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

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

Ответ 8

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

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

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