Как обрабатывать все ошибки, в том числе внутренние ошибки библиотеки C, единообразно - PullRequest
3 голосов
/ 28 января 2010

Я хотел обработать все внутренние ошибки изящно, без завершения программы.

Как обсуждено здесь , использование _set_se_translator фиксирует ошибки деления на ноль.

Но он не перехватывает, например, ошибку библиотеки времени выполнения C -1073740777 (0xc0000417) , которая может быть вызвана строками формата для printf , у которых есть знак процента, где они не должно. (Это всего лишь пример; конечно, мы должны проверять наличие таких строк). Для их обработки требуется _set_invalid_parameter_handler .

Есть еще около десяти таких обработчиков, перечисленных здесь .

Кроме того, он будет перехватывать необработанные исключения C ++: SetUnhandledExceptionFilter . Таким образом, его можно использовать вместе с функциями __ set__ ... . (Статья о , использующей его в MSVC 2008.)

Я хочу перехватить все ошибки, чтобы их можно было обработать (путем регистрации, выдачи современного стандартного исключения C ++ и возврата кода ошибки для конкретного приложения). Есть ли один обработчик, который ловит все?

См. Также это на StackOverflow .

Я использую Visual Studio 2008.

Ответы [ 2 ]

7 голосов
/ 28 января 2010

Я бы предостерег от этого.

Внутренние ошибки не могут быть исправлены. Если вы делите на ноль или что-то еще - программа не подлежит восстановлению.

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

Давайте рассмотрим неприятный пример:

void log(const char* fmt,...) {
   lock(logfile);
   va_args...
   fvprintf(logfile,fmt,__...  <--- this line calls the terminator
   unlock(logfile);
}

Что произойдет, если вы не прекратите работу программы? Что происходит с программой в следующий раз, когда кто-то пытается что-то записать?

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

<Ч />

Существует совершенно другой класс ошибок, которые можно перехватить:

Многие API связываются с вызывающим абонентом, используя коды возврата, чтобы сигнализировать об ошибках. Целесообразно использовать макросы или вспомогательные функции для проверки этих кодов возврата и преобразования их в исключения. Это потому, что этот выбор находится в вашем коде, где вы можете его видеть.

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

6 голосов
/ 28 января 2010

Универсального обработчика не существует. Вам нужно установить каждый. Я использовал что-то вроде этого:

///////////////////////////////////////////////////////////////////////////
template<class K, class V>
class MapInitializer
{
    std::map<K,V> m;
public:
    operator std::map<K,V>() const 
    { 
        return m; 
    }

    MapInitializer& Add( const K& k, const V& v )
    {
        m[ k ] = v;
        return *this;
    }
};

///////////////////////////////////////////////////////////////////////////
struct StructuredException : std::exception
{
    const char *const msg;
    StructuredException( const char* const msg_ ) : msg( msg_ ) {}
    virtual const char* what() const { return msg; }
};

///////////////////////////////////////////////////////////////////////////
class ExceptionHandlerInstaller
{
public:
    ExceptionHandlerInstaller()
        : m_oldTerminateHandler( std::set_terminate( TerminateHandler ) )
        , m_oldUnexpectedHandler( std::set_unexpected( UnexpectedHandler ) )
        , m_oldSEHandler( _set_se_translator( SEHandler ) )
    {}

    ~ExceptionHandlerInstaller() 
    { 
        std::set_terminate( m_oldTerminateHandler );
        std::set_unexpected( m_oldUnexpectedHandler );
        _set_se_translator( m_oldSEHandler );
    }

private:
    static void TerminateHandler() 
    { 
        TRACE( "\n\n**** terminate handler called! ****\n\n" );
    }

    static void UnexpectedHandler() 
    { 
        TRACE( "\n\n**** unexpected exception handler called! ****\n\n" );
    }

    static void SEHandler( const unsigned code, EXCEPTION_POINTERS* )
    {
        SEMsgMap::const_iterator it = m_seMsgMap.find( code );
        throw StructuredException( it != m_seMsgMap.end() 
            ? it->second 
            : "Structured exception translated to C++ exception." );
    }

    const std::terminate_handler  m_oldTerminateHandler;
    const std::unexpected_handler m_oldUnexpectedHandler;
    const _se_translator_function m_oldSEHandler;

    typedef std::map<unsigned, const char*> SEMsgMap;
    static const SEMsgMap m_seMsgMap;
};

///////////////////////////////////////////////////////////////////////////
// Message map for structured exceptions copied from the MS help file
///////////////////////////////////////////////////////////////////////////
const ExceptionHandlerInstaller::SEMsgMap ExceptionHandlerInstaller::m_seMsgMap 
    = MapInitializer<ExceptionHandlerInstaller::SEMsgMap::key_type, 
                     ExceptionHandlerInstaller::SEMsgMap::mapped_type>()
    .Add( EXCEPTION_ACCESS_VIOLATION,         "The thread attempts to read from or write to a virtual address for which it does not have access. This value is defined as STATUS_ACCESS_VIOLATION." )
    .Add( EXCEPTION_ARRAY_BOUNDS_EXCEEDED,    "The thread attempts to access an array element that is out of bounds, and the underlying hardware supports bounds checking. This value is defined as STATUS_ARRAY_BOUNDS_EXCEEDED." )
    .Add( EXCEPTION_BREAKPOINT,               "A breakpoint is encountered. This value is defined as STATUS_BREAKPOINT." )
    .Add( EXCEPTION_DATATYPE_MISALIGNMENT,    "The thread attempts to read or write data that is misaligned on hardware that does not provide alignment. For example, 16-bit values must be aligned on 2-byte boundaries, 32-bit values on 4-byte boundaries, and so on. This value is defined as STATUS_DATATYPE_MISALIGNMENT." )
    .Add( EXCEPTION_FLT_DENORMAL_OPERAND,     "One of the operands in a floating point operation is denormal. A denormal value is one that is too small to represent as a standard floating point value. This value is defined as STATUS_FLOAT_DENORMAL_OPERAND." )
    .Add( EXCEPTION_FLT_DIVIDE_BY_ZERO,       "The thread attempts to divide a floating point value by a floating point divisor of 0 (zero). This value is defined as STATUS_FLOAT_DIVIDE_BY_ZERO." )
    .Add( EXCEPTION_FLT_INEXACT_RESULT,       "The result of a floating point operation cannot be represented exactly as a decimal fraction. This value is defined as STATUS_FLOAT_INEXACT_RESULT." )
    .Add( EXCEPTION_FLT_INVALID_OPERATION,    "A floatin point exception that is not included in this list. This value is defined as STATUS_FLOAT_INVALID_OPERATION." )
    .Add( EXCEPTION_FLT_OVERFLOW,             "The exponent of a floating point operation is greater than the magnitude allowed by the corresponding type. This value is defined as STATUS_FLOAT_OVERFLOW." )
    .Add( EXCEPTION_FLT_STACK_CHECK,          "The stack has overflowed or underflowed, because of a floating point operation. This value is defined as STATUS_FLOAT_STACK_CHECK." )
    .Add( EXCEPTION_FLT_UNDERFLOW,            "The exponent of a floating point operation is less than the magnitude allowed by the corresponding type. This value is defined as STATUS_FLOAT_UNDERFLOW." )
    .Add( EXCEPTION_GUARD_PAGE,               "The thread accessed memory allocated with the PAGE_GUARD modifier. This value is defined as STATUS_GUARD_PAGE_VIOLATION." )
    .Add( EXCEPTION_ILLEGAL_INSTRUCTION,      "The thread tries to execute an invalid instruction. This value is defined as STATUS_ILLEGAL_INSTRUCTION." )
    .Add( EXCEPTION_IN_PAGE_ERROR,            "The thread tries to access a page that is not present, and the system is unable to load the page. For example, this exception might occur if a network connection is lost while running a program over a network. This value is defined as STATUS_IN_PAGE_ERROR." )
    .Add( EXCEPTION_INT_DIVIDE_BY_ZERO,       "The thread attempts to divide an integer value by an integer divisor of 0 (zero). This value is defined as STATUS_INTEGER_DIVIDE_BY_ZERO." )
    .Add( EXCEPTION_INT_OVERFLOW,             "The result of an integer operation causes a carry out of the most significant bit of the result. This value is defined as STATUS_INTEGER_OVERFLOW." )
    .Add( EXCEPTION_INVALID_DISPOSITION,      "An exception handler returns an invalid disposition to the exception dispatcher. Programmers using a high-level language such as C should never encounter this exception. This value is defined as STATUS_INVALID_DISPOSITION." )
    .Add( EXCEPTION_INVALID_HANDLE,           "The thread used a handle to a kernel object that was invalid (probably because it had been closed.) This value is defined as STATUS_INVALID_HANDLE." )
    .Add( EXCEPTION_NONCONTINUABLE_EXCEPTION, "The thread attempts to continue execution after a non-continuable exception occurs. This value is defined as STATUS_NONCONTINUABLE_EXCEPTION." )
    .Add( EXCEPTION_PRIV_INSTRUCTION,         "The thread attempts to execute an instruction with an operation that is not allowed in the current computer mode. This value is defined as STATUS_PRIVILEGED_INSTRUCTION." )
    .Add( EXCEPTION_SINGLE_STEP,              "A trace trap or other single instruction mechanism signals that one instruction is executed. This value is defined as STATUS_SINGLE_STEP." )
    .Add( EXCEPTION_STACK_OVERFLOW,           "The thread uses up its stack. This value is defined as STATUS_STACK_OVERFLOW." );

Затем в main или app init я делаю это:

BOOL CMyApp::InitInstance()
{
    ExceptionHandlerInstaller ehi;
    // ...
}

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

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