SetUnhandledExceptionFilter仅捕获未调试状态下未被SEH处理的结构化异常(如访问违规、非法指令),不捕获C++异常或调试器已接管的异常;回调须为LONG WINAPI函数,DLL中易失效,需由主EXE统一设置并验证注册成功。

SetUnhandledExceptionFilter 能捕获哪些崩溃
它只捕获当前线程未处理的结构化异常(SEH),比如 access violation、stack overflow、illegal instruction,但**不捕获 C++ 异常(throw)**,也不捕获调试器已接管的异常(如在 VS 中按 F5 运行时,很多异常会被调试器先截获)。真正上线后脱离调试器运行时,它才最有效。
常见误判:程序闪退没进回调,大概率是异常被调试器吞了,或用了 SetThreadExceptionFilter(不存在)、拼错函数名(SetUnhandleExceptionFilter)、或回调函数没用 __declspec(dllexport)(DLL 场景下必须)。
回调函数必须满足的签名和调用约定
Windows 要求异常过滤器函数必须是 LONG WINAPI UnhandledExceptionFilterHandler(PEXCEPTION_POINTERS pExceptionInfo),其中 WINAPI 即 __stdcall。C++ 成员函数、lambda、普通 __cdecl 函数都会导致栈错乱或直接不调用。
实操建议:
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 用全局函数或
static成员函数(但需显式转成函数指针) - 函数体开头加
if (!pExceptionInfo) return EXCEPTION_CONTINUE_SEARCH;防空指针 - 返回值只能是
EXCEPTION_EXECUTE_HANDLER(自行处理并终止)、EXCEPTION_CONTINUE_SEARCH(交由系统默认处理)、EXCEPTION_CONTINUE_EXECUTION(极危险,仅限修复可恢复异常,如页面保护触发后手动VirtualProtect)
为什么 SetUnhandledExceptionFilter 在 DLL 中经常失效
主 EXE 设置的过滤器对 DLL 里的异常有效,但 DLL 自己调用 SetUnhandledExceptionFilter **只影响该 DLL 的线程上下文,且容易被 EXE 后续调用覆盖**。更严重的是:如果 DLL 是延迟加载或通过 LoadLibrary 动态加载,而过滤器在 DllMain 中设置,此时线程环境不稳定,系统可能忽略注册。
安全做法:
- DLL 不主动设全局过滤器,由宿主 EXE 统一设置
- 若必须由 DLL 提供崩溃收集能力,暴露一个初始化函数(如
MyDllInitCrashHandler()),由 EXE 显式调用,并确保在主线程初始化完成后再调用 - 避免在
DllMain的DLL_PROCESS_ATTACH中调用SetUnhandledExceptionFilter
捕获后怎么安全地记录堆栈和退出
异常发生时,堆栈可能已损坏,不能调用大多数 CRT 函数(如 fopen、malloc、std::string 构造),也不能用 C++ 异常机制。唯一可靠的是 Windows API 的异步信号安全子集。
推荐组合:
- 用
MiniDumpWriteDump(dbghelp.dll)写 mini dump:需提前LoadLibrary并获取函数指针,不能静态链接 - 用
WriteConsole或OutputDebugString打印简短信息(如异常代码、地址) - 用
TerminateProcess(GetCurrentProcess(), exitCode)强制退出,不要用exit()或return - 避免在回调里弹 MessageBox(UI 线程可能已死,导致卡死)
最易被忽略的一点:SetUnhandledExceptionFilter 注册后,仍可能被第三方 SDK(如某些反作弊、图形驱动、甚至旧版 Qt)悄悄覆盖——上线前务必用 GetUnhandledExceptionFilter() 检查是否仍是你的函数地址。

















