Windows SEH中的局部展开与全局展开

经过阅读windows核心编程一书,对SEH中的局部展开和全局展开终于有了一个比较清晰的概念。
先说说__finally块的语义,__finally块语义就是只要是指令流将要流出其对应的__try块时,则__finally块中的代码将得到执行。那么所谓展开就是保证__finally块中的代码得到执行。下面详细解释下局部展开和全局展开

 

局部展开就是指某个函数中的__try块中的代码因为执行了return,break,goto,continue等指令使得指令流要提前流出__try块或者因为指令流到了__try块底部要自然流出,这时进行局部展开,说白了就是执行__finally块中的代码,以保证__finally语义的正确性。这里注意局部展开是代码的自然执行过程,而不是由异常等引起的。

 

全局展开则是由异常引起的,所以这里先大致说下SEH对异常的处理过程,当某个__try块中的代码触发了异常时(也可能是__try块中调用的函数中引发异常),操作系统会从最靠近引发异常代码的地方开始从下层往上层查找__except块(这里的层是指__try块的嵌套层),对于找到的每一个__except块,会先计算它的异常过滤器,如果过滤器返回EXCEPTION_CONTINUE_SEARCH,则说明此__except块不处理此类异常,需要继续往上层查找,如果某过滤器返回EXCEPTION_EXECUTE_HANDLER则说明此__except块可以处理此类异常,即找到了异常的处理代码,此时停止查找,但是在执行该__except块中的异常处理代码之前,要先进行全局展开。全局展开的过程与查找

__except块的过程类似,只不过这次是查找从底层向上查找__finally块,查找过程中遇到的每一个__finally块中的代码都被执行,直到查找到前面说的处理异常的__except块那一层停止,这时全局展开完成,然后执行__except块中的异常处理代码,执行完异常处理代码之后,指令流从__except块后的第一条指令开始。从这里也可以看出全局展开也是为了保证__finally语义的正确性,因为指令流从引发异常代码转到到__except异常处理代码时也导致了指令流从__try块嵌套层中所有与__finally对应的__try块中流出,由前面的__finally语义说明可知,此时必须要执行全局展开过程以包成__finally语义的正确性。

 

这里另外做一个特别说明,前面提到了两种过滤器返回值(EXCEPTION_CONTINUE_SEARCH和EXCEPTION_EXECUTE_HANDLER),只有过滤器返回EXCEPTION_EXECUTE_HANDLER才可能会引发全局展开,在windows SEH中还有一种过滤器返回值是EXCEPTION_CONTINUE_EXECUTION,它的意思是指令流重新返回引发异常的代码,重新执行一次。这个返回值也不会引起全局展开,因为既然指令流返回了引发异常代码处,表明没有流出__try块,所以不需要展开。

 

 

个人表达能力有限,上述表述感觉很拗口,下面搞个例子来看看应该就比较清楚了。

 

void ExceptTest2()
{
 int i = 0;
 int *p = NULL;
 __try
 {
  __try
  {
   __try
   {
    __try
    {
     __try
     {
      *p = 1; //1.这里引发异常
      i = 10;
     }
     __finally
     {
      i = 11; //4.全局展开第一步
     }
    }
    __finally
    {
     i = 8; //5.全局展开第二步
    }
    i = 9;  //这里不会被执行,因为全局展开完成后,将执行异常处理代码
   }
   __except (EXCEPTION_CONTINUE_SEARCH)//2.过滤器返回EXCEPTION_CONTINUE_SEARCH,继续向上层查找
   {
    i = 1; //因为过滤器表明不处理异常,所以这里也不会被执行
   }

   i = 2;  //这里不会被执行,因为全局展开完成后,将执行异常处理代码
   
   __try  
   {
    i = 5; //这里不会被执行,因为全局展开完成后,将执行异常处理代码
   }
   __finally
   {
    i = 6; //这里不会执行(因为其对应的__try块中没有代码被执行)
   }
  }
  __finally
  {
   i = 35; //6.全局展开第三步
  }
 }
 __except(EXCEPTION_EXECUTE_HANDLER) //3.找到了异常处理块,开始全局展开
 {
  i = 20; //6.执行异常处理代码
 }
 i=10; //7.异常处理完成之后,指令指针到此
}

你可能感兴趣的:(Windows SEH中的局部展开与全局展开)