SetUnhandledExceptionFilter是Windows下C++捕获访问违规等系统级异常的唯一顶层防线,它在进程因未处理SEH异常即将终止时触发,提供生成minidump所需的完整上下文。

Windows 下 C++ 无法用 try/catch 捕获访问违规(如空指针解引用、栈溢出、非法地址读写)这类系统级异常——它们根本不会触发 C++ 异常机制。必须用 SEH(Structured Exception Handling)才能真正拦截崩溃前的最后机会。
为什么 SetUnhandledExceptionFilter 是第一道也是最关键的防线
它不是“捕获异常”,而是注册一个顶层回调,在进程因未处理的结构化异常(EXCEPTION_ACCESS_VIOLATION、EXCEPTION_STACK_OVERFLOW 等)即将终止时被调用。这是你唯一能拿到完整上下文(线程上下文、异常记录、堆栈快照)并做现场保存(如 minidump)的机会。
- 必须在主线程启动早期(甚至
main()之前)调用,否则 DLL 加载或 CRT 初始化阶段的异常可能错过 - 回调函数签名必须是
LPTOP_LEVEL_EXCEPTION_FILTER,返回值决定系统后续行为:EXCEPTION_EXECUTE_HANDLER会终止线程但不退出进程(危险,易留脏状态);EXCEPTION_CONTINUE_SEARCH表示不处理,交给系统默认处理(弹框+退出);EXCEPTION_CONTINUE_EXECUTION仅适用于可恢复异常(如页面保护触发),对访问违规基本无效 - 不要在回调里调用
malloc、std::string、printf等依赖堆或 CRT 的函数——此时堆可能已损坏,CRT 可能未初始化或正被销毁
__try/__except 块只能用于可控范围内的局部防护
SEH 的语法糖,本质是编译器生成的异常帧注册/反注册代码。它能捕获当前函数调用链中抛出的 SEH 异常,但前提是异常发生时该帧仍在栈上且未被破坏。
- 仅适合包裹明确高危操作,比如调用第三方 DLL 的未知函数、解析不可信二进制数据、手动内存扫描等
-
__except表达式里不能调用复杂函数(如GetLastError()安全,但std::to_string()不安全),推荐只用简单条件判断,如GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION - 不能跨 DLL 边界可靠工作:若异常发生在另一个 DLL 中,且该 DLL 是用 /EHsc 编译(禁用 SEH),则
__try块无法捕获 - 与 C++ 异常混用时注意:/EHsc 编译下
__try无法捕获 C++throw;/EHa 才支持混合,但会增大二进制体积并影响优化
生成可用的 minidump 必须调用 MiniDumpWriteDump 并传入正确参数
崩溃现场保存的核心是 MiniDumpWriteDump,但它是否写出有效堆栈、模块、内存内容,完全取决于第三个参数 dumpType 和第五个参数 exceptionParam(即从 EXCEPTION_POINTERS* 中取的 ExceptionRecord 和 ContextRecord)。
立即学习“C++免费学习笔记(深入)”;
- 至少启用
MiniDumpWithException+MiniDumpWithProcessThreadData+MiniDumpWithFullMemoryInfo,否则 dump 里看不到异常原因和线程上下文 - 若要分析 STL 容器或智能指针,需额外加
MiniDumpWithPrivateReadWriteMemory(但会显著增大 dump 文件) - 务必检查返回值:
MiniDumpWriteDump失败时返回FALSE,常见原因是目标路径无写权限、磁盘满、或传入了已被释放的hProcess - dump 文件后缀建议用
.dmp,且保存路径避免含中文或空格(某些调试器解析失败)
SEH 不是错误处理机制,而是崩溃诊断的最后保险。真正健壮的程序应靠断言、输入校验、RAII 和静态分析提前规避问题;SEH 回调里能做的只有安全地落盘现场、通知监控服务、然后让进程干净退出——任何试图“恢复执行”的操作,都可能把问题掩盖得更深。


















