C++在Windows上捕获内核级崩溃需用SEH机制:①__try/__except捕获局部异常;②SetUnhandledExceptionFilter注册全局处理器;③VEH实现高优先级拦截;④MiniDumpWriteDump生成转储;⑤WER上报至系统服务。

如果在Windows平台开发C++应用程序时遇到内核级崩溃(如访问违规、除零、栈溢出等),系统可能通过结构化异常处理(SEH)机制向用户态抛出异常。以下是捕获并响应此类异常的多种技术方案:
一、使用__try / __except语法块捕获SEH异常
该方法直接利用Microsoft Visual C++编译器支持的SEH扩展语法,在函数作用域内建立异常处理边界,可捕获由内核触发的硬件异常(如STATUS_ACCESS_VIOLATION)。
1、在函数内部使用__try关键字包裹可能引发异常的代码段。
2、在__try块后紧跟__except关键字,并传入异常过滤表达式,例如GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION。
立即学习“C++免费学习笔记(深入)”;
3、在__except块中编写恢复逻辑或日志记录,避免程序直接终止。
4、注意:此语法仅在x86平台默认启用;x64平台需配合/SAFESEH:NO链接器选项或使用向量式SEH替代,且不能在C++异常处理(try/catch)嵌套中混用。
二、设置全局未处理异常过滤器SetUnhandledExceptionFilter
该方法注册一个顶层异常处理器,当线程中无其他__except或catch能处理异常时,系统将调用该回调函数,适用于捕获内核抛出后未被局部SEH拦截的致命异常。
1、定义符合LPTOP_LEVEL_EXCEPTION_FILTER签名的回调函数,参数为EXCEPTION_POINTERS*指针。
2、在main()或DllMain()入口处调用SetUnhandledExceptionFilter并传入该函数地址。
3、在回调函数中检查exceptionInfo->ExceptionRecord->ExceptionCode是否为0xC0000005(ACCESS_VIOLATION)或其他内核异常码。
4、必须在回调中返回EXCEPTION_EXECUTE_HANDLER以阻止系统默认弹窗,但不可长期运行复杂逻辑,否则可能阻塞系统异常分发流程。
三、结合Vectored Exception Handling(VEH)进行优先级更高的拦截
VEH机制允许注册向量异常处理函数,其调用优先级高于SEH和UnhandledExceptionFilter,可在异常分发至线程栈前第一时间介入,适合对内核异常做预判与重定向。
1、调用AddVectoredExceptionHandler(TRUE, handler)注册前置处理函数,TRUE表示插入到链表头部。
2、handler函数接收PEXCEPTION_POINTERS参数,通过exceptionInfo->ExceptionRecord->ExceptionAddress获取触发异常的指令地址。
3、若判定为可恢复场景(如合法内存映射延迟触发),可修改context->Rip(x64)或context->Eip(x86)跳过故障指令。
4、VEH处理函数不得抛出C++异常,且必须在返回前调用RemoveVectoredExceptionHandler注销自身,否则可能引发重复注册冲突。
四、利用MiniDumpWriteDump生成崩溃转储供事后分析
当捕获到内核级异常后,立即保存进程内存快照,便于后续使用WinDbg等工具定位崩溃根源,尤其适用于无法实时调试的生产环境。
1、在__except块或VEH handler中打开文件句柄,准备写入.dmp文件。
2、调用MiniDumpWriteDump,传入当前进程句柄、异常线程ID、EXCEPTION_POINTERS结构及MINIDUMP_TYPE标志(推荐MiniDumpWithFullMemory | MiniDumpWithHandleData)。
3、确保目标路径具有写权限,且磁盘空间充足;避免在低内存场景下请求MiniDumpWithFullMemory导致写入失败。
4、生成的.dmp文件需与对应PDB符号文件配套使用,否则堆栈信息将无法解析为源码行号。
五、通过Windows Error Reporting(WER)上报异常至微软服务端
利用系统内置错误报告框架,在捕获SEH异常后提交结构化数据至WER服务,触发自动分析、崩溃聚合与补丁匹配,适用于发布版产品。
1、调用WerRegisterRuntimeExceptionModule注册运行时模块,传入模块基址与回调函数指针。
2、在异常处理器中构造WER_REPORT_INFORMATION结构,填充崩溃时间、模块版本、自定义bucket ID等字段。
3、调用WerReportCreate创建报告对象,再调用WerReportAddDump附加内存转储(可选)。
4、必须在调用WerReportSubmit前设置REPORT_TYPE为WerReportCritical,否则内核异常不会触发完整上报流程。


















