如何统一处理所有错误,包括内部C库错误

Jos*_*Fox 3 c c++ exception-handling visual-studio

我希望优雅地处理所有内部错误,而无需程序终止.

作为讨论在这里,使用_set_se_translator 捕捉除以零错误.

但是它没有捕获,例如,C运行时库错误-1073740777(0xc0000417),这可能是由printf的格式字符串引起的,它们的百分号不应该出现.(这只是一个例子;当然我们应该检查这样的字符串).要处理这些,需要_set_invalid_parameter_handler.

此处列出大约十个其他此类处理程序.

此外,这个将捕获未捕获的C++异常:SetUnhandledExceptionFilter.因此,它可以与__set__ ...函数一起使用.(关于在MSVC 2008中使用它的文章.)

我想捕获任何和所有错误,以便我可以处理它们(通过记录,抛出现代C++标准异常并返回特定于应用程序的错误代码).是否有一个捕获一切的处理程序?

另请参阅StackOverflow上的内容.

我正在使用Visual Studio 2008.

Wil*_*ill 7

我会提醒你注意这一点.

内部错误无法恢复.如果除以零或其他 - 程序是不可恢复的.

如果将终止处理程序转换为继续运行程序的程序,则无法保证程序的状态,并且您可以稍后以不同的方式崩溃和损坏.想象一下,例如,当您转移终止时,程序正在持有一些或其他资源!

让我们有一个令人讨厌的例子:

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

如果您不终止该计划会怎样?下次有人尝试记录某些内容时,该程序会发生什么?

我不能强调这一点 - 你应该使用挂钩终止方法进行额外的日志记录等等,而不是别的.之后你应该继续退出.


有一个完全不同的错误类别,可以被拦截:

许多API使用返回代码与调用者通信以发出错误信号.使用宏或辅助函数检查这些返回代码并将其转换为异常是合适的.这是因为您可以在代码中找到它.

如果你覆盖_set_errno处理程序或其他东西,你会导致你没有编写的代码,期望setter正常返回不返回,并且它可能没有完成清理.

  • Printf失败只会在很短的时间内调用处理程序 - 其余的时间,它们只会崩溃. (2认同)
  • @mlimber - 为什么要将除以零的转换成C++异常?"捕捉"这个并尝试用它做一些有意义的事情是愚蠢的.如果您的程序除以零,则取消引用无效指针等,这是一个错误.在你修复bug之前它应该失败.如果程序的逻辑基于错误的存在,屏蔽错误等,这是一个非常糟糕的主意. (2认同)
  • @JoshuaFox:这样的错误不可恢复,不是因为错误本身,而是因为周围代码的假设.从除零或者访问冲突中抛出C++异常是愚蠢的,因为整数除法和内存赋值是C++标准中的无抛出操作,通过抛出继续执行将导致不在C++代码和其他清理中调用析构函数(longjmp)或基于错误代码)不能在C代码中运行.从printf抛出也是如此,因为它是C并且因此是非运行函数.记录和中止是可以的. (2认同)

met*_*tal 6

没有通用处理程序.你需要安装每一个.我用过这样的东西:

///////////////////////////////////////////////////////////////////////////
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." );
Run Code Online (Sandbox Code Playgroud)

然后在main或app init中,我这样做:

BOOL CMyApp::InitInstance()
{
    ExceptionHandlerInstaller ehi;
    // ...
}
Run Code Online (Sandbox Code Playgroud)

请注意,这会将结构化异常转换为常规异常,但会通过简单地打印错误消息来处理终止(例如,当nothrow()函数抛出异常时会调用它).您不太可能希望对所有不同类型的错误使用单个处理程序,这就是他们不提供错误的原因.

  • 请记住,你需要从每个线程调用`_set_se_translator` ... (3认同)