"SetUnhandledExceptionFilter" and VC8

文章讨论了VC8中CRT对于未处理异常的行为变化,特别是在安全检查失败或参数无效的情况下,直接调用默认调试器而不通知注册的异常过滤器。提供了一个x86平台上的解决方案,通过重写SetUnhandledExceptionFilter函数来确保自定义异常处理程序始终被调用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Many programs are setting an own Unhandled-Exception-Filter , for catching unhandled exceptions and do some reporting or logging (for example creating a mini-dump ).

Now, starting with VC8 (VS2005), MS changed the behaviour of the CRT is some security related and special situations.
The CRT forces the call of the default-debugger (normally Dr.Watson) without informing the registered unhandled exception filter. The situations in which this happens are the following:

So the conclusion is: The are many situations in which your user-defined Unhandled-Exception-Filter will never be called. This is a major change to the previous versions of the CRT and IMHO not very well documented.

The solution

If you don’t want this behavior and you will be sure that your handler will be called, you need to intercept the call to SetUnhandledExceptionFilter which is used by the CRT to disable all previously installed filters. You can achieve this for x86 with the following code:

#include <windows.h>
#include <tchar.h>
#include <stdio.h>

#ifndef _M_IX86
#error "The following code only works for x86!"
#endif
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI MyDummySetUnhandledExceptionFilter(
 LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter)
{
 return NULL;
}

BOOL PreventSetUnhandledExceptionFilter()
{
 HMODULE hKernel32 = LoadLibrary(_T("kernel32.dll"));
 if (hKernel32 == NULL) return FALSE;
 void *pOrgEntry = GetProcAddress(hKernel32, "SetUnhandledExceptionFilter");
 if(pOrgEntry == NULL) return FALSE;
 unsigned char newJump[ 100 ];
 DWORD dwOrgEntryAddr = (DWORD) pOrgEntry;
 dwOrgEntryAddr += 5; // add 5 for 5 op-codes for jmp far
 void *pNewFunc = &MyDummySetUnhandledExceptionFilter;
 DWORD dwNewEntryAddr = (DWORD) pNewFunc;
 DWORD dwRelativeAddr = dwNewEntryAddr – dwOrgEntryAddr;

 newJump[ 0 ] = 0xE9;  // JMP absolute
 memcpy(&newJump[ 1 ], &dwRelativeAddr, sizeof(pNewFunc));
 SIZE_T bytesWritten;
 BOOL bRet = WriteProcessMemory(GetCurrentProcess(),
  pOrgEntry, newJump, sizeof(pNewFunc) + 1, &bytesWritten);
 return bRet;
}

LONG WINAPI MyUnhandledExceptionFilter(
struct _EXCEPTION_POINTERS *lpTopLevelExceptionFilter)
{
 // TODO: MiniDumpWriteDump
 FatalAppExit(0, _T("Unhandled Exception occured!"));
 return EXCEPTION_CONTINUE_SEARCH;
}

int _tmain()
{
 SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
 BOOL bRet = PreventSetUnhandledExceptionFilter();
 _tprintf(_T("Prevented: %d"), bRet);

 abort();  // force Dr.Watson in release!
}

Before calling PreventSetUnhandledExceptionFilter you must be sure that no other thread is running (or at least no other thread is trying to call SetUnhandledExceptionFilter).

 
<think>好的,我现在需要帮助用户了解SetUnhandledExceptionFilter函数的使用方法和示例代码。首先,我得回忆一下这个函数的作用。根据引用[2],这个函数用于设置未处理异常的回调函数,包括C和C++的异常。用户可能需要捕获程序崩溃前的异常,进行日志记录或清理工作。 接下来,我应该解释函数的基本用法。函数原型应该是接收一个回调函数指针,这个回调函数会在未处理异常发生时被调用。需要说明回调函数的参数和返回值,比如接受EXCEPTION_POINTERS结构体指针,返回LONG类型的结果。 然后,用户可能需要示例代码。我需要提供一个简单的C++例子,展示如何设置回调函数,并触发一个异常来测试。例如,在回调函数中输出异常信息,然后在main函数中故意制造一个访问冲突,比如解引用空指针。同时,要注意代码的格式和正确性,确保语法正确,特别是异常处理的部分。 另外,引用[3]提到了查看函数内部实现的方法,但用户可能更关注使用而不是内部实现,所以这部分可能不需要深入,除非用户特别询问。但当前问题集中在用法和示例,所以应该集中在如何正确调用该函数。 还需要注意,在Windows编程中,异常处理回调可能需要特定的调用约定,比如__stdcall,所以在定义回调函数时要使用正确的修饰符,如WINAPI。同时,SetUnhandledExceptionFilter的返回值是之前的异常过滤器,这点也要在示例中体现,可能不需要处理但可以提及。 另外,用户可能会遇到的问题包括回调函数未被调用的情况,比如在某些情况下系统会重置异常过滤器,比如某些函数(如堆损坏)会导致回调失效,这时候需要提醒用户注意这些限制,并参考引用[1]和[2]中的信息。 最后,生成相关问题,可能需要包括如何调试异常处理函数,或者在多线程环境中的使用注意事项,或者如何防止异常过滤器被重置等问题。</think>### SetUnhandledExceptionFilter 函数详解 #### 1. 函数作用 `SetUnhandledExceptionFilter` 是 Windows API 中用于设置未处理异常回调的函数。当程序发生未被 try/catch 块捕获的异常(如访问违规、除零错误等)时,系统会调用该回调函数,允许开发者记录崩溃信息或执行紧急清理操作[^2]。 #### 2. 函数原型 ```cpp LPTOP_LEVEL_EXCEPTION_FILTER SetUnhandledExceptionFilter( LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter ); ``` - **参数**:`lpTopLevelExceptionFilter` 为自定义异常处理函数指针 - **返回值**:返回前一个注册的异常过滤器 #### 3. 回调函数定义 ```cpp LONG WINAPI CustomUnhandledExceptionFilter( _In_ EXCEPTION_POINTERS* ExceptionInfo ); ``` - `ExceptionInfo` 包含异常类型、地址和寄存器状态 - 返回值建议为 `EXCEPTION_EXECUTE_HANDLER` 或 `EXCEPTION_CONTINUE_SEARCH` #### 4. 示例代码 ```cpp #include <Windows.h> #include <iostream> LONG WINAPI MyExceptionHandler(EXCEPTION_POINTERS* pException) { std::cerr << "Unhandled Exception Caught! Code: 0x" << std::hex << pException->ExceptionRecord->ExceptionCode << std::endl; return EXCEPTION_EXECUTE_HANDLER; } int main() { // 设置自定义异常过滤器 SetUnhandledExceptionFilter(MyExceptionHandler); // 制造一个访问违规异常 int* p = nullptr; *p = 42; // 触发异常 return 0; } ``` #### 5. 注意事项 - 异常处理函数应尽量简单,避免复杂操作 - 某些情况(如堆损坏)可能导致回调不生效 - 调试模式下可能优先触发调试器中断
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值