SetUnhandledExceptionFilter无法捕获异常原因及解决方法

转自:http://name5566.com/934.html

很多 C/C++ 程序会设置自己的 Unhandled Exception Filter 用于捕获 Unhandled exceptions 并输出一些信息(例如,创建 mini-dump 或者输出调用栈到日志文件中)。

从 VC++2005 开始出于安全因素微软改变了 CRT 的行为。在以下情况下 CRT 不会通知被注册的 Unhandled Exception Filter:

  1. 调用了 abort() 并且设置 abort 的行为为 _CALL_REPORTFAULT(Release 版本默认使用此设置)
  2. Security Checks 失败时,具体来说就是检查到一些会引发安全问题的堆栈溢出时不会通知被注册的 Unhandled Exception Filter,会引发安全问题的堆栈溢出包括:覆盖了函数的返回值,覆盖了 Exception handler 的地址,覆盖了某些类型的参数。关于编译器的 Security Checks 的内容,详细参考:http://msdn.microsoft.com/en-us/library/Aa290051(注意,此文章谈到的是 Visual Studio .NET 2003,其中 _set_security_error_handler 函数在 VC++2005 以及以上版本已经无法使用)
  3. 如果没有调用 _set_invalid_parameter_handler 设置 Invalid parameter handler 时,检查到了非法的参数

CRT 是通过何种方式使得我们注册的 Unhandled Exception Filter 不被调用的?答案在 CRT 的代码中:

 
 
  1. /* 代码来源于 gs_report.c */
  2. /* Make sure any filter already in place is deleted. */
  3. SetUnhandledExceptionFilter(NULL);
  4. UnhandledExceptionFilter(&ExceptionPointers);

CRT 通过调用 SetUnhandledExceptionFilter 并传递参数 NULL 来清除用户注册的 Unhandled Exception Filter。如果期望用户注册的 Unhandled Exception Filter 总是被调用那么应该避免 CRT 中相关的清理代码。做法之一就是修改 CRT 代码并且编译为静态库(微软的 VC++ Libraries 开发 Lead Martyn Lovell 在 https://connect.microsoft.com/feedback/ViewFeedback.aspx?FeedbackID=101337&SiteID=210 谈到过有关的问题),这里并不建议使用此做法。另外一种做法则是改变 SetUnhandledExceptionFilter 的行为,使得 CRT 对 SetUnhandledExceptionFilter 的调用不起任何作用(更加详细的论述可以参考《Windows 核心编程》相关章节)。

 
 
  1. // 无法得知此代码来源于
  2. #ifndef _M_IX86
  3. #error "The following code only works for x86!"
  4. #endif
  5.  
  6. // 此函数一旦成功调用,之后对 SetUnhandledExceptionFilter 的调用将无效
  7. void DisableSetUnhandledExceptionFilter()
  8. {
  9. void* addr = (void*)GetProcAddress(LoadLibrary("kernel32.dll"),
  10. "SetUnhandledExceptionFilter");
  11.  
  12. if (addr)
  13. {
  14. unsigned char code[16];
  15. int size = 0;
  16.  
  17. code[size++] = 0x33;
  18. code[size++] = 0xC0;
  19. code[size++] = 0xC2;
  20. code[size++] = 0x04;
  21. code[size++] = 0x00;
  22.  
  23. DWORD dwOldFlag, dwTempFlag;
  24. VirtualProtect(addr, size, PAGE_READWRITE, &dwOldFlag);
  25. WriteProcessMemory(GetCurrentProcess(), addr, code, size, NULL);
  26. VirtualProtect(addr, size, dwOldFlag, &dwTempFlag);
  27. }
  28. }

只需要在注册 Unhandled Exception Filter 之后调用 DisableSetUnhandledExceptionFilter() 函数,那么之后所有对 SetUnhandledExceptionFilter 的调用都将无效,自然 CRT 也无法通过调用 SetUnhandledExceptionFilter 来清除用户注册的 Unhandled Exception Filter。


  • 4
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
在Qt中,可以使用QApplication类的setUnhandledExceptionFilter函数来设置全局异常处理函数。具体步骤如下: 1. 创建一个全局异常处理函数,该函数需要接收一个std::exception指针作为参数,如下所示: ``` void myExceptionHandler(std::exception *e) { qDebug() << "Exception caught: " << e->what(); } ``` 2. 在应用程序的main函数中,调用QApplication类的setUnhandledExceptionFilter函数并传入全局异常处理函数,如下所示: ``` int main(int argc, char *argv[]) { QApplication app(argc, argv); qInstallMessageHandler(myMessageHandler); QApplication::setUnhandledExceptionFilter(myExceptionHandler); // 设置全局异常处理函数 // 其他代码... return app.exec(); } ``` 在上述代码中,我们先调用了qInstallMessageHandler函数,设置了一个自定义的消息处理函数myMessageHandler,以便在程序运行过程中可以将一些调试信息输出到控制台。然后,我们调用QApplication类的setUnhandledExceptionFilter函数,将全局异常处理函数myExceptionHandler传入。 当程序中发生未捕获异常时,该异常会被传递给myExceptionHandler函数进行处理。在该函数中,我们可以打印出异常信息,或者进行一些其他的处理操作。 注意:在Qt中,如果一个异常没有被捕获,那么程序将会调用terminate函数结束运行,因此我们必须在全局异常处理函数中尽可能地处理异常,避免程序异常终止。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值