windows下程序崩溃时需用setunhandledexceptionfilter注册seh异常回调,在其中调用minidumpwritedump生成dump;必须早于线程创建调用,传入正确exception_pointers和minidump_type(如minidumpwithdatasegs|minidumpwithunloadedmodules|minidumpwithindirectlyreferencedmemory),并确保pdb匹配。

崩溃时捕获异常并调用 MiniDumpWriteDump
Windows 平台下,程序崩溃(如访问非法内存、除零、未处理 C++ 异常)时,默认不会生成 dump。必须主动注册异常处理回调,并在其中调用 MiniDumpWriteDump。关键不是“监听崩溃”,而是“接管未处理异常”——即设置 SetUnhandledExceptionFilter 回调函数。
常见错误是只捕获 std::exception,但多数崩溃(如空指针解引用)根本不会抛出 C++ 异常,而是触发结构化异常(SEH)。所以必须处理 SEH,而非仅靠 try/catch。
-
SetUnhandledExceptionFilter必须在主线程早期调用(如main()开头),否则子线程崩溃无法捕获 - 回调函数签名必须为
LONG WINAPI MyUnhandledExceptionFilter(PEXCEPTION_POINTERS) - 调用
MiniDumpWriteDump前,确保目标路径可写、磁盘有空间,且避免在回调中做复杂操作(如分配内存、加载 DLL) - 推荐使用
MiniDumpWithIndirectlyReferencedMemory | MiniDumpScanMemory标志,能更好捕获 STL 容器内容,但会增大 dump 体积
MiniDumpWriteDump 的参数怎么选才实用
dump 质量取决于传给 MiniDumpWriteDump 的 MINIDUMP_TYPE 参数。盲目加全量标志(如 MiniDumpWithFullMemory)会导致几百 MB 文件,难以传输分析;太轻量(如仅 MiniDumpNormal)又可能缺失堆栈关键上下文。
日常调试推荐组合:MiniDumpWithDataSegs | MiniDumpWithUnloadedModules | MiniDumpWithIndirectlyReferencedMemory。它包含数据段、已卸载模块信息(用于符号回溯)、以及通过指针链可达的内存(对 std::vector、std::string 等很关键)。
- 不要用
MiniDumpWithFullMemory:除非明确需要整个进程地址空间,否则纯属浪费 -
MiniDumpWithHandleData在排查句柄泄漏时有用,但常规崩溃分析通常不需要 -
hProcess和hThread必须用GetCurrentProcess()和GetCurrentThread(),不能传INVALID_HANDLE_VALUE或其他线程句柄 -
ExceptionParam(即PEXCEPTION_POINTERS)必须传入,否则无法记录崩溃现场寄存器和调用栈
为什么 dump 文件打不开或符号不匹配
生成了 .dmp 文件,但用 WinDbg 或 Visual Studio 打开后看不到源码、变量名、甚至调用栈全是 ???,问题几乎都出在符号(PDB)缺失或路径不对。
不是“生成 dump 就完事”,而是“dump + 匹配 PDB + 正确加载路径”三者缺一不可。PDB 必须与崩溃时的二进制文件(EXE/DLL)精确对应(时间戳、校验和一致),且调试器需知道它的位置。
- 编译时必须开启生成 PDB:
/Zi(MSVC),并确保/DEBUG链接选项启用 - PDB 文件不能重命名,也不能放在不同目录下——VS 默认只查 EXE 同目录或环境变量
_NT_SYMBOL_PATH指向的路径 - 建议在 dump 生成时,把 PDB 复制到 dump 同目录,并命名为
xxx.pdb(与模块名一致),VS 会自动识别 - 若模块是动态加载的 DLL,确保其 PDB 也一并部署,否则该模块的栈帧无法解析
多线程环境下要注意什么
主线程设置的 SetUnhandledExceptionFilter 对所有线程生效,但回调执行时,崩溃线程已处于异常状态,其他线程仍在运行——这可能导致 dump 中部分线程堆栈混乱,甚至死锁(比如崩溃发生时正持有全局锁,而 dump 写入又尝试获取同一锁)。
最稳妥做法是:在异常回调里,立刻暂停所有其他线程(用 SuspendThread),再调用 MiniDumpWriteDump,完成后恢复。但这本身有风险——某些系统线程(如 APC 线程)不应被挂起。
- 更安全的做法是:只对崩溃线程生成 dump,不强制挂起其他线程;依赖
MiniDumpWithThreadInfo获取各线程基本状态,足够定位大多数问题 - 避免在回调中调用
malloc、new、printf等可能触发堆操作或输出缓冲的函数——崩溃时堆可能已损坏 - 如果程序用了自定义堆管理器(如 tcmalloc、mimalloc),需确认其是否兼容 SEH 上下文,否则
MiniDumpWriteDump可能失败
真正难的不是生成 dump,而是让 dump 在另一台机器上也能还原出可读的调用栈和局部变量——这取决于你有没有管好 PDB 的生成、分发和路径配置。漏掉任意一环,dump 就只是个大文件。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











