SEH无法捕获内核崩溃(如BSOD),因其仅作用于Ring 3用户态异常,而内核崩溃发生在Ring 0,系统直接停机,SEH无执行机会。

SEH 不能捕获内核崩溃——这是最关键的事实。Windows 内核崩溃(如 BSOD)发生在 Ring 0,用户态程序运行在 Ring 3,SEH 是用户态的异常分发机制,根本收不到 KeBugCheckEx 或类似内核级终止信号。
为什么 __try/__except 对蓝屏完全无效
SEH 只能拦截当前线程上下文中的结构化异常,比如 STATUS_ACCESS_VIOLATION、STATUS_INTEGER_DIVIDE_BY_ZERO 这类由 CPU 异常触发、经 KiDispatchException 转入用户态的异常。而内核崩溃是全局同步事件:系统直接停机、清空所有用户态栈、跳转到内核错误处理路径,你的 __except 块连加载机会都没有。
常见误判场景:
- 看到驱动报错或 dmp 文件里有“KERNEL_SECURITY_CHECK_FAILURE”,就以为自己程序能用 SEH 捕获——不能,那是内核 dump,不是你进程的异常
- 在测试中故意调用
DbgBreakPoint()或触发 IRP 超时,观察到进程退出但没进__except——这正说明异常未被 SEH 捕获,而是被系统终止了 - 依赖
SetUnhandledExceptionFilter捕获“最后机会”——它同样只对用户态异常有效,对蓝屏零作用
真正能做的:监控和响应崩溃前兆
虽然抓不住 BSOD,但你可以感知内核异常传导到用户态的“尾巴”,比如驱动引发的访问违规、堆损坏、或设备 I/O 超时导致的 STATUS_DEVICE_PROTOCOL_ERROR。这类异常仍走 SEH 流程。
实操建议:
- 在关键模块(尤其是调用
DeviceIoControl、ReadFile等与驱动交互的代码)外层加__try/__except,过滤GetExceptionCode() == STATUS_ACCESS_VIOLATION等可恢复异常 - 用
SetThreadErrorMode(SEM_NOGPFAULTERRORBOX)防止弹窗阻塞,配合MiniDumpWriteDump保存现场 - 监听
WinStationDisconnected或服务控制管理器(SCM)发来的SERVICE_CONTROL_STOP——某些严重驱动故障会导致会话强制注销,这是间接信号
调试时如何区分异常来源
用 WinDbg 分析用户态 crash dump 时,看 !analyze -v 输出里的 PROCESS_NAME 和 FAULTING_MODULE:
- 如果
FAULTING_MODULE是ntoskrnl.exe或某驱动(如mydriver.sys),且STACK_TEXT显示大量nt!KiDispatchException→nt!RtlDispatchException→ 用户栈,说明异常已下推到你的进程——SEH 可捕获 - 如果
BUGCHECK_CODE是0x1e(KMODE_EXCEPTION_NOT_HANDLED)或0x3b(SYSTEM_SERVICE_EXCEPTION),且STACK_TEXT根本没进你模块,说明崩溃停留在内核,你的代码没机会执行任何 handler - 用
!pte查虚拟地址页表项,确认ACCESS_VIOLATION是否真由用户态指针越界引起,而非驱动篡改了你的内存页属性
最常被忽略的一点:SEH 的有效性高度依赖编译选项。/EHsc 会禁用 C++ 异常与 SEH 的混合捕获;/EHa 才允许 catch(...) 捕获 SEH 异常,但代价是增大二进制体积、影响性能。别在 Release 下盲目开 /EHa,先确认你真需要捕获 STATUS_STACK_OVERFLOW 这类罕见异常。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











