VEH不能可靠捕获Windows上的非法内存访问,因其仅能捕获部分可继续执行的异常且恢复易致二次崩溃;SetUnhandledExceptionFilter更适合作为最后兜底方案捕获ACCESS_VIOLATION。

VEH 不能可靠捕获 Windows 上的“段错误”式非法内存访问(如空指针解引用、野指针读写),它只能捕获部分可继续执行的异常,且一旦触发,程序已处于未定义状态——强行恢复极易引发二次崩溃或数据损坏。
为什么 SetUnhandledExceptionFilter 比 VEH 更适合捕获非法访问
Windows 的 ACCESS_VIOLATION(0xC0000005)属于结构化异常(SEH),而 VEH 是顶层异常分发器,但它的注册顺序和优先级不如进程级的 SetUnhandledExceptionFilter 稳定;更重要的是,VEH 回调中若尝试恢复执行(比如修改 ContextRecord->Rip 跳过出错指令),会绕过系统内存保护机制,导致后续访问仍可能失败,甚至被 ASLR/CFG/ACG 拦截。
-
SetUnhandledExceptionFilter在异常未被任何 SEH 框架(__try/__except)处理时触发,时机更确定,且是 Windows 官方推荐的最后兜底方案 - VEH 注册后对所有线程生效,但主线程之外的异常可能在你没注意时被其他 DLL(如 CRT、图形驱动)的 VEH 先截获并吞掉
- 调试器存在时,VEH 可能被调试器拦截,导致你的回调根本不会执行
用 SetUnhandledExceptionFilter 捕获 EXCEPTION_ACCESS_VIOLATION 的最小可行代码
以下代码在 Visual Studio x64 Release 下可稳定触发并打印出错地址与访问类型(读/写),但不建议恢复执行,仅用于日志或崩溃转储:
在 Windows 11 上"直接发声"的 TTS(从 WSL2/TUI 调用 powershell.exe + System.Speech)。适用于用户说"说出来/读出来/语音播报/用TTS",或反馈"没声音/tts 生成的 mp3 是空的/播不出来",以及需要中文语音但 OpenClaw 内置 tts 不可用时。
LONG WINAPI CrashHandler(EXCEPTION_POINTERS* pExceptionInfo) {
auto& except = *pExceptionInfo->ExceptionRecord;
if (except.ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
DWORD64 faultAddr = (DWORD64)except.ExceptionInformation[1];
DWORD accessType = (DWORD)except.ExceptionInformation[0]; // 0=read, 1=write
printf("AV at %p, type=%s\n", (void*)faultAddr, accessType ? "WRITE" : "READ");
// 此处可调用 MiniDumpWriteDump 生成 dmp
return EXCEPTION_EXECUTE_HANDLER; // 终止进程,避免继续执行
}
return EXCEPTION_CONTINUE_SEARCH;
}
<p>int main() {
SetUnhandledExceptionFilter(CrashHandler);
int<em> p = nullptr;
</em>p = 42; // 触发 AV
}-
ExceptionInformation[0]表示访问类型:0 是读,1 是写(x64 下可靠;x86 需查文档确认) -
ExceptionInformation[1]是出错的虚拟地址,不是指令地址(ExceptionAddress才是 RIP) - 返回
EXCEPTION_EXECUTE_HANDLER会终止当前线程,防止栈被破坏后继续跑飞
VEH 的唯一合理用途:调试辅助与沙箱监控
VEH 真正适用的场景不是“容错”,而是“观测”——比如你在写一个轻量级 hook 框架或运行时分析工具,需要在任意线程触发异常时介入检查上下文,但不接管控制流:
立即学习“C++免费学习笔记(深入)”;
- 注册 VEH 后,务必在回调里快速判断是否为
EXCEPTION_ACCESS_VIOLATION,然后立即返回EXCEPTION_CONTINUE_SEARCH,让系统走默认 SEH 流程 - 不要在 VEH 回调中调用 CRT 函数(如
printf、malloc),因为堆/IO 可能已损坏;可用WriteConsoleA或直接写文件句柄 - 若需记录寄存器状态,必须用
pExceptionInfo->ContextRecord,且注意 x64 下Rsp可能已不可靠,慎用栈回溯
真正想“捕获段错误”来实现类似 Linux sigsegv 的信号处理逻辑,在 Windows 上本质是伪需求——AV 不是信号,而是进程级致命错误。能做的只有快速记录现场、生成 dump、安全退出。任何试图“修复后继续跑”的方案,都会在多线程、优化代码或启用 CFG 的环境下失效。

















