windows核心程式設計--SEH(結構異常處理)
SEH 的工作原理。
Windows 程式設計中最重要的理念就是訊息傳遞,事件驅動。當GUI應用程式觸發一個訊息時,系統將把該訊息放入訊息佇列,然後去查詢並呼叫窗體的訊息處理函式(CALLBACK),傳遞的引數當然就是這個訊息。我們同樣可以把異常也當作是一種訊息,應用程式發生異常時就觸發了該訊息並告知系統。系統接收後同樣會找它的“回撥函式”,也就是我們的異常處理例程。當然,如果我們在程式中沒有做異常處理的話,系統也不會置之不理,它將彈出我們常見的應用程式錯誤框,然後結束該程式。所以,當我們改變思維方式,以CALLBACK
的思想來看待 SEH,SEH 將不再神祕。
SEH 是 Windows 系統提供的功能,跟開發工具無關。值得一提的是,VC 將 SEH 進行了封裝 try catch finally,c++中也可以用c的封裝 __try{}__except(){} 和 __try{}__finally{}. 所以當你建立一個C++ try塊時,編譯器就生成一個S E H_ _t r y塊。一個C + +c a t c h測試變成一個S E H異常過濾器,並且c a t c h中的程式碼變成S E H_ _e x c e p t塊中的程式碼。實際上,當你寫一條C++ throw語句時,編譯器就生成一個對Wi
n d o w s的R a i s e E x c e p t i o n函式的呼叫。用於t h r o w語句的變數傳遞給R a i s e E x c e p t i o n作為附加的引數。
一個簡單的使用SEH的例子
假如要實現一個完全強壯的應用程式,該程式需要每週7天,每天2 4小時執行。在今天的世界裡,軟體變得這麼複雜,有那麼多的變數和因子來影響程式的效能,筆者認為如果不用S E H,要實現完全強壯的應用程式簡直是不可能的。我們先來看一個樣板程式,即C的執行時函式s t r c p y:
char * strDestination,
const char * strSource);
這是一個相當簡單的函式,它怎麼會引起一個程序結束呢?如果呼叫者對這些引數中的某一個傳遞N U L L(或任何無效的地址),s t r c p y就引起一個存取異常,並且導致整個程序結束。
使用S E H,就可以建立一個完全強壯的s t r c p y函式:
{
__try
{
strcpy(strDestination, strSource);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
// Nothing to do here
}
return (strDestination);
}
這個函式所做的一切就是將對s t r c p y的呼叫置於一個結構化的異常處理框架中。如果s t r c p y執行成功,函式就返回。如果s t r c p y引起一個存取異常,異常過濾器返回E X C E P T I O N _E X E C U T E _ H A N D L E R,導致該執行緒執行異常處理程式程式碼。在這個函式中,處理程式程式碼什麼也不做,R o b u s t S t r C p y只是返回到它的呼叫者,根本不會造成程序結束。
另一個使用:
void test()
{
int * p = 0x00000000 ; // pointer to NULL
__try
{
puts( " in try " );
__try
{
puts( " in try " );
// causes an access violation exception;
// 導致一個儲存異常
* p = 13 ;
// 呵呵,注意這條語句
puts( " 這裡不會被執行到 " );
}
__finally
{
puts( " in finally " );
}
// 呵呵,注意這條語句
puts( " 這裡也不會被執行到 " );
}
__except(puts( " in filter 1 " ), 0 )
{
puts( " in except 1 " );
}
}
void main()
{
puts( " hello " );
__try
{
test();
}
__except(puts( " in filter 2 " ), 1 )
{
puts( " in except 2 " );
}
puts( " world " );
}
上面的程式執行結果如下:
hello
in try
in try
in filter 1
in filter 2
in finally
in except 2
world
Press any key to continue
另一個混合c++的異常處理使用:
// 注意,這是 C++ 程式,檔名為: SEH-test.cpp
#include " stdio.h "class A
{
public :
void f1() {}
// 丟擲 C++ 異常
void f2() { throw 888 ;}
} ;
// 這個函式中使用了 try-catch 處理異常,也即 C++ 異常處理
void test1()
{
A a1;
A a2,a3;
try
{
a2.f1();
a3.f2();
}
catch ( int errorcode)
{
printf( " catch exception,error code:%d\n " , errorcode);
}
}
// 這個函式沒什麼改變,仍然採用 try-except 異常機制,也即 SEH 機制
void test()
{
int * p = 0x00000000 ; // pointer to NULL
__try
{
// 這裡呼叫 test1 函式
test1();
puts( " in try " );
__try
{
puts( " in try " );
// causes an access violation exception;
// 導致一個儲存異常
* p = 13 ;
puts( " 這裡不會被執行到 " );
}
__finally
{
puts( " in finally " );
}
puts( " 這裡也不會被執行到 " );
}
__except(puts( " in filter 1 " ), 0 )
{
puts( " in except 1 " );
}
}
void main()
{
puts( " hello " );
__try
{
test();
}
__except(puts( " in filter 2 " ), 1 )
{
puts( " in except 2 " );
}
puts( " world " );
}
上面程式不僅能夠被編譯通過,而且執行結果也是正確的(和預期的一樣,同樣符合 C++ 異常處理模型的規則,和 SEH 異常模型的處理規則)。其結果如下:
hello
catch exception,error code:888
in try
in try
in filter 1
in filter 2
in finally
in except 2
world
Press any key to continue