24.1 SetUnhandledExceptionFilter未处理异常--《Windows核心编程》
对于未处理异常,例如异常过滤返回EXCEPTION_CONTINUE_SEARCH,向上搜索,但无法搜索到处理部分,产生未处理异常。Windows提供了 SetUnhandledExceptionFilter 函数,给我们处理异常的最后机会,否则 Windows 会正式认为这个异常没有得到处理。
默认情况下,如果没有设置 SetUnhandledExceptionFilter 函数,对于未处理异常,会调用操作系统的默认异常处理代码 UnhandledExceptionHandler。
通常应该在进程初始化阶段调用这个函数,一旦调用,进程中所有线程的未抛出异常都会导致 SetUnhandledExceptionFilter 参数所指定的的异常过滤函数的执行。
// 负责处理未处理异常
// 进程初始化时,设置处理未处理异常过滤函数
PTOP_LEVEL_EXCEPTION_FILTER SetUnhandledExceptionFilter(
PTOP_LEVEL_EXCEPTION_FILTER pTopLevelExceptionFilter
);
参数是一个异常过虑函数,原型必须与下面类似
// 返回值和结果
// EXCEPTION_EXECUTE_HANDLER 进程终止
// EXCEPTION_CONTINUE_EXECUTION 从抛出异常处再执行
// EXCEPTION_CONTINUE_SEARCH 不处理
LONG WINAPI TopLevelUnhandledExceptionFilter(
PEXCEPTION_POINTERS pExceptionInfo
);
C/C++运行函数安装的默认的全局异常过滤程序: __CxxUnhandledExceptionFilter,用 NULL 调用 SetUnhandledExceptionFilter 将全局未处理异常过滤程序设回 UnhandledExceptionFilter
#include <windows.h>
#include <stdio.h>
long __stdcall callback(_EXCEPTION_POINTERS* excp)
{
MessageBox(0, "Error", "error", MB_OK);
printf("Error address %x\n", excp->ExceptionRecord->ExceptionAddress);
printf("CPU register:\n");
printf("eax %x ebx %x ecx %x edx %x\n", excp->ContextRecord->Eax,
excp->ContextRecord->Ebx, excp->ContextRecord->Ecx,
excp->ContextRecord->Edx);
return EXCEPTION_EXECUTE_HANDLER; // 进程终止
}
int main(int argc, char* argv[])
{
PTOP_LEVEL_EXCEPTION_FILTER pException = SetUnhandledExceptionFilter(callback);
_asm int 3 // 只是为了让程序崩溃
return 0;
}
我们都知道,每个线程的执行都是从 NTDLL.dll 中的函数 BaseThreadStart 开始的:
VOID BaseThreadStart(
PTHREAD_START_ROUTINE pfnStartAddr,
PVOID pvParam
)
{
__try
{
ExitThread((pfnStartAddr)(pvParam));
}
__except (UnhandledExceptionFilter(GetExceptionInformation()))
{
ExitProcess(GetExceptionCode());
}
}
// 上述UnhandledExceptionFilter的返回值和结果
// EXCEPTION_EXECUTE_HANDLER 执行ExitProcess,进程退出
// EXCEPTION_CONTINUE_EXECUTION 从抛出异常处再次执行
// EXCEPTION_CONTINUE_SEARCH 未处理异常
// 上述UnhandledExceptionFilter的返回值和结果
// EXCEPTION_EXECUTE_HANDLER 执行ExitProcess,进程退出
// EXCEPTION_CONTINUE_EXECUTION 从抛出异常处再次执行
// EXCEPTION_CONTINUE_SEARCH 未处理异常
如果我们的线程抛出一个异常,并且所有安装的异常过滤程序都返回 EXCEPTION_CONTINUE_SEARCH,系统提供的一个特殊函数 UnhandledExceptionFilter 将会被调用。
本书讨论的是用户态的开发。对内核态线程抛出未处理异常,在蓝屏前,系统让相关设备驱动调用 CrashDmp.sys在页文件中建立 Crash Dump,再停止所有操作。重启后,会查看页文件是否包含了一个 Crash Dump,如果有表示数据得到了存储 ,系统让WerFault运行,产生错误报告,并发送给Microsoft服务器。