SetUnhandledExceptionFilter捕不到所有崩溃,因其仅作用于当前线程且易被第三方DLL干扰;可靠方案是早期同时启用SetUnhandledExceptionFilter和AddVectoredExceptionHandler,并确保在安全上下文调用MiniDumpWriteDump。

为什么 SetUnhandledExceptionFilter 捕不到所有崩溃
很多同学一上来就用 SetUnhandledExceptionFilter 注册顶层异常处理器,结果发现程序在某些 DLL 加载失败、堆损坏(如 HEAP_CORRUPTION_DETECTED)、或 STATUS_ACCESS_VIOLATION 被系统提前终止时,回调根本没执行。这是因为 SEH 异常链可能被第三方库(比如某些反作弊、注入 DLL)篡改或截断;更关键的是,SetUnhandledExceptionFilter 只对当前线程有效,而主线程崩溃前若已创建子线程且未同步设置,那些线程的异常就彻底漏掉了。
真正可靠的做法是:在进程启动早期(main 或 DllMain 中)同时启用两种机制:
- 调用
SetUnhandledExceptionFilter处理未捕获的 C++ 异常和部分 SEH 异常 - 用
AddVectoredExceptionHandler注册向量异常处理器(优先级更高、跨线程生效、不受 DLL 干扰) - 确保异常处理函数标记为
__declspec(naked)或至少禁用帧指针优化(/Oy-),避免栈回溯失效
如何用 MiniDumpWriteDump 生成可用的 dump 文件
MiniDumpWriteDump 看似简单,但参数选错会导致 dump 完全无法用于分析——比如只传 MiniDumpNormal,那堆内存、线程上下文、模块列表全都没了,WinDbg 打开后连崩溃线程都定位不到。
推荐组合使用以下标志(按需叠加):
立即学习“C++免费学习笔记(深入)”;
-
MiniDumpWithFullMemory:包含完整进程内存镜像(体积大但最稳妥,调试必选) -
MiniDumpWithThreadInfo:记录每个线程的挂起状态、TEB、栈范围 -
MiniDumpWithHandleData:保存句柄表,查资源泄漏时有用 -
MiniDumpWithUnloadedModules:捕获已卸载但符号仍存在的 DLL(常见于热更新场景)
注意:MiniDumpWriteDump 必须在异常发生后的“安全上下文”中调用——不能在 EXCEPTION_EXECUTE_HANDLER 分支里直接写(此时栈可能已破坏),应先用 RaiseException 触发二次异常跳转到干净栈空间,或在向量处理器中直接调用(它运行在独立栈上)。
在 Windows 上执行终端命令,或对比 Windows 与 Linux 的 Shell 语法时使用。建议先检测当前 Shell 是 PowerShell 还是 cmd.exe,再给出相应命令……
写入 dump 时权限和路径的几个硬坑
崩溃时程序很可能已失去写权限,或当前目录被锁定(比如被杀软监控、或运行在 Program Files 下受 UAC 限制)。直接往 "./crash.dmp" 写,十次有九次失败并静默丢弃。
务必按这个顺序 fallback:
- 首选
GetKnownFolderPath(FOLDERID_LocalAppData, ...)→"MyApp\CrashDumps\" - 次选
GetTempPath,但要检查磁盘剩余空间(GetDiskFreeSpaceEx),避免 dump 写一半中断 - 绝对避免使用相对路径、桌面路径、或硬编码的
C:\根目录 - 写入前用
CreateFile以CREATE_ALWAYS+FILE_ATTRIBUTE_TEMPORARY打开,能绕过部分杀软拦截
另外,dump 文件名建议包含时间戳 + 进程 ID + 异常代码,例如:crash_20240521_1423_1234_0xE06D7363.dmp,方便后续日志关联。
符号文件(PDB)不匹配导致 dump 无法解析怎么办
本地开发时 dump 能完美显示源码行号,但发给客户后 WinDbg 显示 “*** ERROR: Module load completed but symbols not loaded for xxx.dll”——大概率是 PDB 和二进制文件时间戳/校验和不一致。Visual Studio 默认生成的 PDB 不带完整路径信息,MiniDumpWriteDump 也只存模块基址和大小,不存 PDB 路径。
解决方法只有两个实际有效的:
- 构建时用
/Zi+/Fd"$(IntDir)$(ProjectName).pdb",并把 PDB 和 EXE 打包同发,调试时在 WinDbg 里手动设置.sympath+ . - 更鲁棒的方式:在写 dump 前调用
SymInitialize+SymLoadModule64,再用SymGetModuleInfo64获取各模块的ImageName和ModuleName,把它们连同MiniDumpWriteDump的输出一起写进一个配套的.json元数据文件,供后续自动化匹配
别信“用 dbghelp.dll 自动下载符号”这种方案——崩溃时网络不可用,而且微软符号服务器不托管你的私有 PDB。

















