Windows C++程序需用SEH捕获ACCESS_VIOLATION等底层异常:编译启用/EHs-与/EHa,main中调用SetUnhandledExceptionFilter注册MyTopLevelExceptionHandler;该函数校验pExp非空、比对ExceptionCode、调MiniDumpWriteDump生成dump(文件须FILE_SHARE_WRITE);__try/__except块可保护代码,但禁用C++对象;崩溃现场通过ContextRecord取寄存器、SymFromAddr查符号、StackWalk64回溯调用栈(首次需设AddrFrame.Offset为Rbp/Ebp)。

在Windows平台开发C++程序时,遇到访问违规(ACCESS_VIOLATION)、除零(INT_DIVIDE_BY_ZERO)等底层异常,必须通过SEH(Structured Exception Handling)机制捕获,否则进程会直接终止且无法记录现场信息。
启用SEH编译支持并注册全局异常处理函数
在Visual Studio中右键项目→属性→C/C++→代码生成→启用C++异常→选择“否”(/EHs-),再将“启用SEH异常”设为“是”(/EHa)。这一步不可跳过,【/EHs- + /EHa 必须同时启用,仅开/EHa会导致C++异常被SEH吞掉】。
在main()开头调用SetUnhandledExceptionFilter(MyTopLevelExceptionHandler),该函数返回上一个过滤器指针,建议保存以便异常链式调用。
编写顶层异常处理函数MyTopLevelExceptionHandler
函数签名必须为LONG CALLBACK MyTopLevelExceptionHandler(PEXCEPTION_POINTERS pExp)。
立即学习“C++免费学习笔记(深入)”;
第一步:检查pExp是否为空指针,空则直接返回EXCEPTION_CONTINUE_SEARCH,避免解引用崩溃。
第二步:用pExp->ExceptionRecord->ExceptionCode比对常见错误码——如0xC0000005(ACCESS_VIOLATION)、0xC0000094(INT_DIVIDE_BY_ZERO)、0xC000001D(ILLEGAL_INSTRUCTION)。
第三步:调用MiniDumpWriteDump获取完整内存快照,传入当前进程句柄、GetCurrentProcessId()、文件句柄(CreateFile打开.dmp路径)、MiniDumpWithFullMemory、NULL、NULL、NULL。注意:若未以FILE_SHARE_WRITE方式打开文件,dump会写入失败且无提示。
在关键代码段嵌入__try/__except块
方法一:保护指针解引用操作
__try { int x = *p; } __except(EXCEPTION_EXECUTE_HANDLER) { OutputDebugString(L"捕获空指针访问"); }
方法二:按异常类型精细响应
__except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { /* 只处理AV,其他异常继续向上抛 */ }
注意:__try块内不能包含C++对象构造或析构(如std::string局部变量),否则可能引发栈展开冲突导致二次崩溃。
提取崩溃现场关键信息
从pExp->ContextRecord中读取寄存器状态:EIP/RIP(崩溃指令地址)、ESP/RSP(栈顶)、EBP/RBP(帧指针)。
用SymInitialize初始化符号引擎,传入GetCurrentProcess()和符号路径(如".;C:\symbols"),再调用SymFromAddr获取崩溃地址对应的函数名与行号。
调用StackWalk64遍历调用栈,每层需填充STACKFRAME64结构体并传入上下文,【第1次调用前必须手动设置AddrFrame.Offset = ContextRecord->Rbp(x64)或 Ebp(x86)】,否则栈回溯会中断在第一层。


















