seh不能用catch捕获访问违规,必须用__try/__except或setunhandledexceptionfilter;混用需/eha,x64下seh机制简化,推荐统一用setunhandledexceptionfilter+minidumpwritedump做崩溃采集。

SEH不是C++异常,不能用catch捕获Access Violation
Windows的SEH(Structured Exception Handling)和C++ try/catch是两套互不兼容的机制。直接在catch(...)里试图捕获0xC0000005(ACCESS_VIOLATION)会失败——它根本不会进入C++异常处理流程。这是最常踩的坑:以为写了catch(...)就万事大吉,结果程序照样弹窗崩溃。
真正能拦截硬件异常(如空指针解引用、栈溢出、非法地址访问)的,只有SEH原生机制:__try/__except块,或全局的SetUnhandledExceptionFilter回调。
-
__try/__except适合局部防护,比如封装高危API调用(如LoadLibrary、VirtualAlloc) -
SetUnhandledExceptionFilter用于进程级兜底,但注意:它只对当前线程有效,且在DLL中注册可能被主程序覆盖 - VS编译器默认开启
/EHsc,会禁用SEH;若要混用,必须显式加/EHa(慎用,影响性能且破坏RAII)
__except过滤表达式必须返回EXCEPTION_EXECUTE_HANDLER或EXCEPTION_CONTINUE_SEARCH
__except后面的括号里不是布尔值,而是一个整型表达式,其返回值决定系统行为。写成__except(1)看似“总是捕获”,实则危险:它会掩盖本该继续向上传递的异常(比如EXCEPTION_BREAKPOINT调试断点),导致调试器失灵。
正确做法是检查GetExceptionCode()并分类响应:
__except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{
// 仅拦截访问违规,其余异常照常崩溃或交由上层处理
}
- 不要在过滤表达式里调用复杂函数(如
printf、malloc),此时堆栈可能已损坏 - 避免在
__except块内抛C++异常——跨机制抛出会触发未定义行为 -
GetExceptionInformation()可获取EXCEPTION_POINTERS*,用于记录崩溃现场(IP、寄存器、内存地址)
SetUnhandledExceptionFilter的回调函数必须足够轻量且异步信号安全
当异常未被任何__try捕获时,系统会调用SetUnhandledExceptionFilter注册的回调。但此时程序状态极不稳定:堆可能损坏、CRT锁可能死锁、甚至栈已溢出。所以回调里禁止做以下操作:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 调用
malloc、new、std::string等依赖堆或全局对象的操作 - 调用
printf、std::cout等非异步信号安全函数(可能卡死) - 加载DLL或调用COM接口(模块句柄可能无效)
可行做法是:用WriteProcessMemory把关键寄存器/栈帧快照写入预分配的共享内存,或直接调用MiniDumpWriteDump生成dmp文件(需提前加载dbghelp.dll并缓存函数指针)。
另外注意:Windows Vista+ 默认启用“SafeSEH”,若你的模块没通过链接器/SAFESEH校验,系统会忽略其注册的过滤器。
64位下SEH被大幅简化,__try/__except仅限于编译器生成的帧信息
x64 Windows完全弃用了旧版SEH链表机制,改用基于表驱动(table-based)的异常分发。这意味着:
-
__try/__except仍可用,但底层不再靠修改FS:[0]链表,而是依赖编译器在PE头中生成.pdata和.xdata节 - 手动构造异常帧(如老式
SetThreadStackGuarantee配合jmp)在x64下失效 -
SetUnhandledExceptionFilter在x64上依然有效,但回调函数签名必须为LONG WINAPI UnhandledExceptionFilter(PEXCEPTION_POINTERS)
如果你的代码需要同时支持x86/x64,别依赖__try嵌套深度或FS段偏移;统一用SetUnhandledExceptionFilter + MiniDumpWriteDump做崩溃采集更可靠。另外,x64下异常处理开销比x86略高,频繁使用__try会影响热点路径性能。
真正难的从来不是注册一个过滤器,而是确保崩溃发生时,你还能拿到有效的内存上下文——这要求所有关键数据结构(如日志缓冲区、dump路径字符串)在程序启动早期就静态分配并锁定。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










