问题背景:
Windows平台常用的异常捕获函数有时候不起作用,特别是XP系统上,有些runtime error捕获不到,但是其他系统如win7、win8等又能正常捕获,导致产品发出去后遇到这类异常,莫名其妙的弹一个错误框出来导致用户体验特别不好。
问题解决:
windows平台win32提供的常用捕获异常的接口有下面三种:
_set_invalid_parameter_handler(MyInvalidParameterHandler);
_set_purecall_handler(MyPureCallHandler);
SetUnhandledExceptionFilter(doSomething);
这上面三个win32的接口参数可以设置自己的回调函数,及遇到异常后开发可以做最后的处理,如生成dump文件、启动报警机制等等。上面三个函数的设置后能捕获系统SEH异常和CRT异常。但是程序在XP系统上或者其他系统上会出现设置了全局的SetUnhandledExceptionFilter,却捕获不到子线程或者子模块的CRT异常,还是会弹出系统默认的错误框。
之所以还弹出默认的错误框,因为设置的全局捕获函数没起作用而是调用了系统默认的SetUnhandledExceptionFilter,开发自己设置SetUnhandledExceptionFilter() 时,是将用户注册的函数地址存到全局变量 kernel32!BasepCurrentTopLevelFilter 中,而捕获不到异常的时候发现自己设置的函数地址和系统触发的地址不一样,说明系统调用了默认的。为了解决上面注册了全局捕获函数而捕获不到子模块子线程异常的问题,这里可以采用人工代码打补丁的方式来解决,需要自己实现一个打补丁函数,在设置全局捕获函数后手动调用打补丁函数,实例代码如下:
SetUnhandledExceptionFilter(doSomething);
PatchSetUnhandledExceptionFilter() //打补丁的具体实现如下
void PatchSetUnhandledExceptionFilter()
{
static BYTE RETURN_CODE[] = { 0xc2, 0x04, 0x00};
MEMORY_BASIC_INFORMATION mbi;
DWORD dwOldProtect = 0;
DWORD pfnSetFilter =(DWORD)GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "SetUnhandledExceptionFilter");
VirtualQuery((void *)pfnSetFilter, &mbi, sizeof(mbi) );
VirtualProtect( (void *)pfnSetFilter, sizeof(RETURN_CODE), PAGE_READWRITE, &dwOldProtect);
//VirtualProtectEx(GetCurrentProcess(), (void *)pfnSetFilter, sizeof(RETURN_CODE), PAGE_READWRITE, &dwOldProtect);
//如64系统下VirtualProtect后WriteProcessMemory崩溃,则用VirtualProtectEx
WriteProcessMemory(GetCurrentProcess(), (void *)pfnSetFilter,RETURN_CODE,sizeof(RETURN_CODE), NULL);
VirtualProtect((void *)pfnSetFilter, sizeof(RETURN_CODE), mbi.Protect, 0);
//VirtualProtectEx(GetCurrentProcess(),(void *)pfnSetFilter, sizeof(RETURN_CODE), mbi.Protect, 0);
FlushInstructionCache(GetCurrentProcess(), (void *)pfnSetFilter, sizeof(RETURN_CODE));
}