您的位置:首页 > 其它

Win32 SEH异常深度探索_3 默认异常回调函数

2009-11-05 20:38 411 查看
So far, I've implicitly assumed that the operating system always finds a handler somewhere in the linked list of EXCEPTION_REGISTRATIONs. What happens if nobody steps up to the plate? As it turns out, this almost never happens. The reason is that the operating system sneaks in a default exception handler for each and every thread. The default handler is always the last node in the linked list, and it always chooses to handle the exception. Its actions are somewhat different than a normal exception callback routine, as I'll show later.

如果操作系统遍历整个链表都找不到一个处理异常的回调函数会如何?这种情况基本不会发生,操作系统会给每个线程自动插入一个默认异常处理函数,它在整个链表的结尾。

Let's look at where the system inserts the default, last resort exception handler. This obviously needs to occur very early in the thread's execution, before any user code executes. Figure 7 shows some pseudocode I wrote for BaseProcessStart, an internal routine in the Windows NT KERNEL32.DLL. BaseProcessStart takes one parameter, the address of the thread's entry point. BaseProcessStart runs in the context of the new process and calls the entry point to kick off execution of the first thread in the process.

下面这段伪代码演示系统如何在线程执行的最开头插入异常处理函数。实际的 BaseProcessStart 在 KERNEL32.DLL 中,接受一个线程的入口地址做参数。

view plaincopy to clipboardprint?
BaseProcessStart( PVOID lpfnEntryPoint )
{
DWORD retValue
DWORD currentESP;
DWORD exceptionCode;

currentESP = ESP;

_try
{
NtSetInformationThread( GetCurrentThread(),
ThreadQuerySetWin32StartAddress,
&lpfnEntryPoint, sizeof(lpfnEntryPoint) );

retValue = lpfnEntryPoint();

ExitThread( retValue );
}
_except(// filter-expression code
exceptionCode = GetExceptionInformation(),
UnhandledExceptionFilter( GetExceptionInformation() ) )
{
ESP = currentESP;

if ( !_BaseRunningInServerProcess ) // Regular process
ExitProcess( exceptionCode );
else // Service
ExitThread( exceptionCode );
}
}
BaseProcessStart( PVOID lpfnEntryPoint )
{
DWORD retValue
DWORD currentESP;
DWORD exceptionCode;

currentESP = ESP;

_try
{
NtSetInformationThread( GetCurrentThread(),
ThreadQuerySetWin32StartAddress,
&lpfnEntryPoint, sizeof(lpfnEntryPoint) );

retValue = lpfnEntryPoint();

ExitThread( retValue );
}
_except(// filter-expression code
exceptionCode = GetExceptionInformation(),
UnhandledExceptionFilter( GetExceptionInformation() ) )
{
ESP = currentESP;

if ( !_BaseRunningInServerProcess ) // Regular process
ExitProcess( exceptionCode );
else // Service
ExitThread( exceptionCode );
}
}

In the pseudocode, notice that the call to lpfnEntryPoint is wrapped within an _try and _except construct. This _try block is what installs the default, last resort exception handler in the linked list of handlers. All subsequent registered exception handlers will be inserted ahead of this handler in the list. If the lpfnEntryPoint function returns, the thread ran to completion without causing an exception. When this happens, BaseProcessStart calls ExitThread to terminate the tread.

可见线程的入口函数调用 lpfnEntryPoint 被 __try, __except 包围。如果函数正常退出, BaseProcessStart 调用 ExitThread 结束线程。

On the other hand, what if the thread faults and no other exception handlers handle it? In this case, control goes to the code inside the parens after the _except keyword. In BaseProcessStart, this code calls the UnhandledExceptionFilter API, which I'll come back to later. For now, the key point is that the UnhandledExceptionFilter API contains the meat of the default exception handler.

如果线程抛出未处理的异常,控制流程将进入 _except , UnhandledExceptionFilter API 会被调用,他包含了默认异常处理的主要部分。

If UnhandledExceptionFilter returns EXCEPTION_EXECUTE_HANDLER, the _except block in BaseProcessStart executes. All the _except block code does is terminate the current process by calling ExitProcess. Thinking about it for a second, this makes sense; it's common knowledge that if a program causes a fault and nobody handles the fault, the system terminates the process. What you're seeing in the pseudocode is exactly where and how this happens.

如果 UnhandledExceptionFilter 返回 EXCEPTION_EXECUTE_HANDLER , _except 块被执行,将调用 ExitProcess 结束进程。

There's one final addition to what I've just described. If the thread that faulted is running as a service and is for a thread-based service, the _except block code doesn't call ExitProcess—it calls ExitThread instead. You wouldn't want to terminate the entire service process just because one service went bad.

如果失败的线程是一个基于线程的 service ,那么 _except 块会调 ExitThread 结束当前线程。

So what does the default exception handler code in UnhandledExceptionFilter do? When I ask this question at seminars, very few people can guess what the default behavior of the operating system is when an unhandled exception occurs. With a very simple demonstration of the default handler's behavior, things usually click and people understand. I simply run a program that intentionally causes a fault, and point out the results (see Figure 8 ).

At a high level, UnhandledExceptionFilter displays a dialog telling you that a fault occurred. At that point, you're given the opportunity to either terminate or debug the faulting process. Much more happens behind the scenes, and I'll describe these things towards the end of the article.

UnhandledExceptionFilter 会做些什么?从用户角度看,它会弹出一个对话框,告诉用户错误发生地址,类型,并给出结束进程或调试进程的选择。

As I've shown, when an exception occurs, user-written code can (and often does) get executed. Likewise, during an unwind operation, user-written code can execute. This user-written code may have bugs and could cause another exception. For this reason, there are two other values that an exception callback can return: ExceptionNestedException and ExceptionCollidedUnwind. While obviously important, this is pretty advanced stuff and I'm not going to dwell on it here. It's difficult enough to understand the basic facts.

当异常发生时,在回退操作过程和异常处理过程中,都可能有用户代码被执行,这里的用户代码可能有 bug 或再次抛出异常,所以异常回调函数可以返回另两个值: ExceptionNestedException , ExceptionCollidedUnwind 。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/hongjiqin/archive/2009/09/25/4593802.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: