- 注册时间
- 2011-3-6
- 最后登录
- 1970-1-1
该用户从未签到
|
本文大部分来自《windows核心编程》。
例1
- //二话不说,直接上代码
- int Funcenstein2()
- {
- __try
- {
- return 3;
- }
- __finally
- {
- //在函数返回之前会处理__finally里的内容
- cout<<"finally executed"<<endl;
- }
- return 4;//此函数返回3而不是4
- }
复制代码
通过使用终止处理程序可以防止过早的执行return语句。当return语句试图退出try块的时候,编译器会让finally代码在它。即编译器保证finally代码块在出try块的时候return之前执行。
者可以想知道,编译器是如何保证此功能的呢?原来当编译器检查程序代码时,会发现try代码里有一个return语句。于是,编译器就会生成一些代码先将返回值(例子中的 3)保存在一个由它创建的一个临时变量里,然后再执行finally语句块。这个过程被称之前为局部展开(LOCAL UNWIND)。更确切的说,当系统因try代码提前退出finally时就会发生局部展开。一旦finally代码块执行完毕,编译器所创建的临时变量值就会返回给函数调用者。
由此可见,为了让整个机制运行起来,编译器必须生成一些额外的代码,而系统也要很执行一些额外的工作。在不同的cpu结构上,让终止处理工作起来的步骤也不同。需要注意的是,应该避免在try代码中使用return语句,因为这是对程序性能有害的。__leave关键字,它可以帮助我们发现那些有局部展开开销的代码
例2
-
- int Funcenstein3()
- {
- //在try中使用goto语句时,就会产生局部展开以执行finally代码块。
- //这一次当finally执行完之后。因为try和finally中都没有函数返回语句,
- //所以ReturnValue标签后面的代码也会执行。因此这函数返回4。
- //但是由于代码破坏了try块到finally的正常执行流程,可能有比较大的性能损失,其程序取决于cpu体系结构。
- int ret=0;
- __try
- {
- ret=5;
- goto ReturnValue;
- }
- __finally
- {
- cout<<"finally executed"<<endl;
- }
- ReturnValue:
- return 4;
- }
复制代码
例3
在这个例子中,终止处理将真正证明它的价值。首先看一下代码
-
- DWORD Funcfurter1()
- {
- DWORD dwTemp;
- //1. do any processing here
- __try
- {
- WaitForSingleObject(g_hSem,INFINITE);
- dwTemp=Funcinator(g_dwProtectedData);
- }
- __finally
- {
- ReleaseSemaphore(g_hSem,1,NULL);
- }
- return dwTemp;
- }
复制代码
假设try代码块中Funcinator函数存在一个缺陷会导致程序访问非法的内存。如果没有SEH这种情况下最络导致Windows错误报告(WER)弹出一个对话框:“Application has stopped working”。这个对话框在Windows上经常可以见到。一旦用户取消这个对话框,进程就会终止。但信号量依然被占用并再也得不到释放。其他进程中的纯种就会因为无休止的等待这个信号量而得不到CPU时间片。如果把信号量放在finally之中,即使用try中调用的函数发生了内存访问违规这样的异常,这个信号量也可以被释放。但是,请注意,从Windows Vista开始,须显式地保护try/finally框架,以确保在异常抛出时,finally代码会执行。
例4
现在不防做一个实验,判断一下这个函数的返回值
-
- DWORD FuncalDoodLeDoo()
- {
- DWORD dwTemp=0;
- while (dwTemp<10)
- {
- __try
- {
- if(dwTemp==2)
- continue;
- if(dwTemp==3)
- break;
- }
- __finally
- {
- dwTemp++;
- }
- dwTemp++;
- }
- dwTemp+=10;
- return dwTemp;
- }
复制代码
让我们逐步分析这个函数执行的过程:一开始将dwTemp赋值为0,然后try块中的代码开始执行,但是两个if语句都为false。于是程序正常进入到finally代码块,在这里给dwTemp的值上加1,而finally块后面的代码又将dwTemp加1。
下一次循环开时,dwTemp=2,第一个if为true,程序试如果没有__finally程序会跳到while条件判断处执行,但dwTemp值班不会改变,这将会是一个死循环。但是现在我们有终止处理程序,系统注意到continue语句将会导致提前跳出try块,于是强制执行finally语句块。 在finally语句块中dwTemp被增加到3.这次finally之后的代码块没有机会执行。因此finally执行完之后程序跳到循环顶部执行。
现在我们分析第三次迭代,这次第一个if判断表达式的值为false,第二个表达式的值为true,系统再次侦测到程序流想要提前跳出try块,于是调用finally代码块,这里的dwTemp增加到4.因为break语句的执行程序控制流从whhile循环后开始继续。因而finally块之后循环以内的代码就不会被执行了。而循环之后的代码将dwTemp的值设置为14。这是程序最终返回的结果。不用我指明,请教也不会写出FuncalDoodleDoo这样的代码。此处只是为了演示终止处理程序是如何工作的。‘
绝大多数部情况下,try块中的提前退出都会被终止处理程序所捕获,但是在进程或线程提前终止的情况下,系统没法保证finally代码块的执行。调用ExitThread或者ExitProcess可以马上终止纯种或进程,而不会引发finally执行。同样如果当前纯种或者进程因为另一个程序调用TerminateThread或TerminateProcess而不得不结束,finally代码块也不会被执行,有一些c运行期函数比如(abort),因为在其内部最络调用的是ExitProcess,也会导致finally块不能执行。我们没法阻止别的线程杀死我们的线程或进程,但是可以在自己的代码中尽量避免对ExitThread或ExitProces的草率调用。
例5
-
- DWORD Funcenstein4()
- {
- DWORD dwTemp;
-
- //1. do any processing here
- __try
- {
- WaitForSingleObject(g_hSem,INFINITE);
-
- g_dwProcectedData=5;
- dwTemp=g_dwProcectedData;
- //return the new value
- return dwTemp;
- }
- __finally
- {
- ReleaseSemaphore(g_hSem,1,NULL);
- return 103;
- }
- dwTemp = 9;
- return dwTemp;
- }
复制代码
在上面的函数中,try中的代码试图用return 返回给调用者。正如我们前面提到的那样,试图在try块中提前退出函数导致编程器生成一些额外代码,将函数返回结果保存在一个临时变量中,然后执行finally中又多了一个return,那么导致103被写入到编译器生成的临时变量时。从而覆盖了原先的值5。而返回103 |
|