通过visualstudio运行程序,经常会在output里面看到类似的输出,

A first chanceexception of type 'System.DivideByZeroException' occurred in xxx.dll


我们的问题

  • 什么是first chance exception?
  • 什么是second chance exception?
  • 他们对开发人员的意义是什么?

异常分发处理的机制

理解windows异常分发机制主要要看明白这张图(摘自Windows Internal Chapter 3),


异常分发的流程如下

1.所有的异常都会根据系统的idt(interrupt dispatch table)分发到陷阱处理器(trap handler),系统的idt可以通过内核调试命令!idt查看。

2. Trap handler将异常信息交给内核中的异常分发器,异常分发器先查看是否存调试器与之相关联,如果存在则将控制权交给调试器,这就是第一次异常发生(first chance exception)这样做使得我们在异常发生之初,自定义异常处理之前得到控制权可以查看异常的原始信息。

3.如果没有相关联的调试器或者调试器未对该异常进行处理,分发器开始基于栈查找异常处理器。

4.如果没有找到异常处理器或者异常处理器为对其进行处理,分发器再次尝试分发给调试器端口,第二次异常发生(second chance exception)。

5.如果没有调试器或者调试器没有处理第二次异常,分发器将该异常发送给环境子系统的异常端口用来转换成各种不同的环境子系统相应的错误提示代码。

6.如果环境子系统对异常为进行处理,分发器分发给内核默认异常处理器。该异常处理器是在线程启动的时候注册的。可以通过system internal提供的process explore察看线程的启动地址。

线程的启动代码大致如下

VOID RtlUserThreadStart(VOID){

    LPVOID lpStartAddr = (R/E)AX; // Located in the initial thread context structure
    LPVOID lpvThreadParm = (R/E)BX; // Located in the initial thread context structure
    LPVOID lpWin32StartAddr;

    lpWin32StartAddr = Kernel32ThreadInitThunkFunction ? Kernel32ThreadInitThunkFunction :
lpStartAddr;
    __try {

        DWORD dwThreadExitCode = lpWin32StartAddr(lpvThreadParm);
        RtlExitUserThread(dwThreadExitCode);

    } __except(RtlUnhandledExceptionFilter(
            GetExceptionInformation())) {

        RtlExitUserProcess(GetExceptionCode());
    }
}
void Win32StartOfProcess(
    LPTHREAD_START_ROUTINE lpStartAddr,
    LPVOID lpvThreadParm){

    lpStartAddr(lpvThreadParm);
}

该异常处理逻辑会根据以下注册表项进行相应的处理,例如我机器上的注册表项配置为visualstudio debug,异常发生的时候vs会启动让用户来决定是否要attach到进程查看异常。

HKLM\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\AeDebug

Logo

更多推荐