seh不是c++异常,不能用catch捕获;windows结构化异常(如accessviolation)与c++标准异常机制完全独立,即使启用/eha也仅为编译器层面的不完全转换,不可靠;安全捕获需用setunhandledexceptionfilter或__try/__except。

SEH不是C++异常,不能用catch捕获
Windows的结构化异常处理(SEH)和C++标准异常是两套完全独立的机制。用try/catch抓不到AccessViolation、StackOverflow这类系统级崩溃——哪怕你开了/EHsc或/EHa。VC++的/EHa确实能“混合捕获”,但本质是编译器把SEH异常转成了C++异常对象,底层仍是SEH触发,且行为不可靠:比如栈被破坏时catch块可能根本执行不了。
- 真正安全的崩溃捕获必须用
SetUnhandledExceptionFilter或__try/__except -
/EHa在Release模式下可能被优化掉异常表,导致崩溃直接退出 - 不要在
__except块里调用std::string、malloc或任何可能触发新异常的函数——此时堆栈和堆状态未知
用SetUnhandledExceptionFilter注册全局崩溃处理器
这是最常用也最稳妥的方式,适用于捕获进程内所有未处理的SEH异常,包括野指针解引用、除零、非法指令等。它不依赖编译器选项,且在DLL中也能生效(需注意线程安全性)。
- 必须在
main或DllMain早期调用,晚于DLL加载后可能错过初始化阶段崩溃 - 回调函数签名固定为
LPTOP_LEVEL_EXCEPTION_FILTER,参数是EXCEPTION_POINTERS*,从中可读取ExceptionRecord->ExceptionCode(如EXCEPTION_ACCESS_VIOLATION)和上下文寄存器值 - 别在回调里做复杂逻辑:写日志建议用
WriteFile而非fprintf;生成dump推荐MiniDumpWriteDump而非调用system("procdump")
LONG WINAPI CrashHandler(EXCEPTION_POINTERS* pExceptionInfo) {
HANDLE hFile = CreateFile(L"crash.dmp", GENERIC_WRITE, 0, nullptr,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hFile != INVALID_HANDLE_VALUE) {
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(),
hFile, MiniDumpNormal, pExceptionInfo, nullptr, nullptr);
CloseHandle(hFile);
}
return EXCEPTION_EXECUTE_HANDLER; // 终止进程
}
注意返回值:EXCEPTION_EXECUTE_HANDLER表示你已处理,进程将终止;EXCEPTION_CONTINUE_SEARCH会继续向上查找其他过滤器;EXCEPTION_CONTINUE_EXECUTION极危险,仅限极少数修复场景(如x86下修正EIP跳过非法指令)。
通过 handsfree-windows CLI控制 Windows 本机应用 (UIA) 和浏览器 (Playwright) 的引导技能,用于自动化或测试桌面应用。
__try/__except适合局部高风险代码段
当你明确知道某段代码容易崩溃(比如调用第三方DLL的不透明接口、解析恶意格式文件),用__try/__except包住它,比全局过滤器更精准,也避免干扰正常异常流。
-
__except括号内必须是整型表达式,常用EXCEPTION_EXECUTE_HANDLER或GetExceptionCode()判断类型 - 不能在
__try块内定义需要析构的C++对象(如std::vector),因为SEH展开不调用析构函数——这是C++对象泄漏的常见根源 - VS2015+默认禁用
__try在/CLR下使用,若项目混用托管代码,需改用AppDomain::UnhandledException事件
__try {
DangerousParse(input); // 可能触发ACCESS_VIOLATION
} __except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
Log("Parse failed with AV");
}
调试符号与dump分析才是关键落地环节
捕获到崩溃只是第一步,没符号信息的dump几乎无法定位问题。很多团队只配了SetUnhandledExceptionFilter却忽略符号链路,结果dump里全是0x7ff... 地址。
- 发布版本必须保留
.pdb文件,并确保路径可被WinDbg或Visual Studio识别(可通过_NT_SYMBOL_PATH环境变量指向本地目录或SymStore服务器) - 用
MiniDumpWriteDump时传入MiniDumpWithFullMemory标志代价大,日常用MiniDumpWithIndirectlyReferencedMemory已足够还原调用栈 - 注意x64下SEH是基于表驱动(table-based),
__try块不能跨函数边界——即不能在函数A里开__try,然后调用函数B并在B里崩溃,此时B的栈帧可能不在SEH链上
真正难的从来不是“怎么捕获”,而是“捕获后怎么确认是野指针还是use-after-free,以及是否复现稳定”。没有符号、没有复现步骤、没有内存快照,捕获本身只是个安慰剂。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










