结构化异常处理

結構化異常處理，是Windows作業系統上，Microsoft對C/C++程序語言做的語法擴展，用於處理異常事件的程序控制結構。
異常事件是打斷程序正常執行流程的不在期望之中的硬體、軟體事件。硬體異常是CPU拋出的如「除0」、數值溢出等；軟體異常是作業系統與程序通過RaiseException語句拋出的異常。
Microsoft擴展了C語言的語法，用 try-except與try-finally語句來處理異常。異常處理程序可以釋放已經獲取的資源、顯示出錯信息與程序內部狀態供調試、從錯誤中恢復、嘗試重新執行出錯的代碼或者關閉程序等等。
一個__try語句不能既有__except，又有__finally。但try-except與try-finally語句可以嵌套使用。

try-finally語句

__try {
     // 受保护执行的代码
 }__finally {
     // 清理用途的代码}
__try複合語句中受保護的代碼以任何方式執行結束後，不論__try複合語句是因為出現異常而非正常結束，還是沒有出現異常而正常結束，__finally複合語句中的代碼都會被執行。
如果__try複合語句中受保護的代碼的執行沒有出現異常，包括用goto語句或longjump系統函數跳出__try複合語句等情形，這時將執行__finally複合語句，然後執行try-finally語句之後的其他代碼。
在__finally複合語句中使用內在函數AbnormalTermination判斷是正常結束還是非正常結束__try複合語句。

執行順序

如果__try複合語句中受保護的代碼執行中出現了異常，首先按函數調用順序從新向舊搜索所有包含了出現異常的執行點的try-except語句，執行每個except的過濾函數，直到某個try-except語句的過濾函數結果值為EXCEPTION_EXECUTE_HANDLER。這時，再從包含異常出現的執行點的最內層try-finally語句開始由內向外執行每個finally塊中的代碼，直至回退到那個過濾函數結果值為EXCEPTION_EXECUTE_HANDLER的try-except語句執行其except塊，最後執行該try-except語句後面的其他代碼。 
__leave關鍵字用在 try-finally 語句的受保護節（try塊）中，其效果是跳轉到受保護節的結尾處。 與goto語句跳出受保護節不同，__leave語句更為有效，因為它不會導致堆疊展開。

例子

#include <stdio.h>#include <windows.h> // for EXCEPTION_ACCESS_VIOLATION#include <excpt.h>int filter(unsigned int code, struct _EXCEPTION_POINTERS *ep) 
{
   printf("在异常表达式中\n");
   if (code == EXCEPTION_ACCESS_VIOLATION) {
      printf("接受处理访问违例异常\n");
      return EXCEPTION_EXECUTE_HANDLER;
   }
   else {
      printf("其他异常都不处理\n");
      return EXCEPTION_CONTINUE_SEARCH;
   };}int main(){
   int* p = 0x00000000;   // pointer to NULL

   printf("开始主程序\n");
   __try{
      printf("进入外层的try\n");
      __try{
         printf("进入内层的try\n");
         int *p=0; // 空指针
         *p = 13;    // 导致访问冲突异常
      }__finally{
         printf("在finally内部。");
         printf(AbnormalTermination() ? "非正常终止\n" : "正常终止\n");
      }
   }__except(filter(GetExceptionCode(), GetExceptionInformation())){
      printf("在except内部\n");
   }
   printf("主函数结束\n");}
輸出結果為：
开始主程序
进入外层的try
进入内层的try
在异常表达式中
接受处理访问违例异常
在finally内部。非正常终止
在except内部
主函数结束

與Windows異常處理機制的關係

Windows作業系統（自Windows95起），對每個用戶執行緒，都設立一個異常處理幀鍊表來處理異常事件。該鍊表的每個異常處理幀由兩個成員組成，分別是鍊表上一項地址、當前異常處理器地址，組成了結構_EXCEPTION_REGISTRATION_RECORD。異常處理器是指一個處理異常的回調函數（callback function）。執行緒信息塊（thread information block）的開始處（即FS:[0]指向的內存，FS是CPU的一個段暫存器）保存了異常處理幀鍊表的表頭項的地址。程序執行遇到異常事件而中斷時，作業系統的RtlDispatchException函數會從FS:[0]指向的鍊表表頭依次調用每個節點包含異常處理回調函數，直到某個異常處理回調函數的返回值為0表示已經處理該異常，該執行緒可以恢復執行。鍊表最末一項是作業系統在裝入執行緒時設置的指向kernel32!UnhandledExceptionFilter函數，該函數總是向用戶顯示「Application error」對話框。 
上述異常處理器程序及鍊表，是由用戶程序自己安裝的。鍊表各節點保存在程序調用棧（call stack）上。
Windows異常處理機制支持嵌套異常的處理，即在執行異常處理回調函數時再次發生異常。這種情況下仍遵照普通異常處理機制，作業系統RtlDispatchException函數再入處理新出現的嵌套的異常。嵌套的異常的處理函數得到的DispatcherContext參數值即為在執行時發生了新異常的異常幀的地址。
各種程式語言基於上述Windows異常處理機制，設計了各自的異常處理語句控制結構。Microsoft擴展了C語言語法，設計了結構化異常處理的try-except與try-finally語句。一個函數的所有在函數的的try-except與try-finally形成了一個基於包含（enclosing）關係的森林 (資料結構)。一個函數內如果有__try語句，則在函數的入口與結尾處，編譯器插入了EH_prolog與EH_epilog代碼，把函數內所有在try塊中被保護的代碼包了起來。 EH_prolog在調用棧上創建一個_EXCEPTION_REGISTRATION_RECORD，作為異常處理鍊表的新的表頭，其中包含了Visual C++ 的運行時庫msvcrt.dll的__except_handler4函數地址。在函數塊的結尾處，EH_epilog把這項_EXCEPTION_REGISTRATION_RECORD從鍊表頭移除，恢復其原來的表頭。__except語句中的過濾表達式，由掛在鍊表中的異常處理回調函數MSVCR100D!__except_handler4來調用執行，返回值即為過濾表達式的求值結果。
實際上，編譯器實現結構化異常時，把鍊表每項的資料結構由2個成員擴展為5個成員，即在高地址方向追加了一個scopetable_entries類型結構體數組的指針、一個整型項表示執行點位於當前函數的哪個try塊中、一個保存暫存器EBP的整數項。此後（低地址方向）緊接著是一個指向EXCEPTION_POINTERS結構的指針（前述的內在函數GetExceptionInformation即返回這個指針值）。
異常發生時，作業系統的異常處理機制的ntdll!RtlDispatchException函數會從FS:[0]指向的鍊表表頭依次調用異常幀鍊表的每個節點所包含的異常處理回調函數MSVCR100D!__except_handler4，根據該回調函數的返回值來確定異常是否已經被處理，可以根據異常上下文（Exception context）恢複線程的執行。__except_handler4回調函數實際上只是調用了MSVCR100D!__except_handler4_common函數。__except_handler4_common函數是實際的workhorse，負責在當前異常幀所在的函數中查找那個try-except語句能夠處理該異常（即過濾表達式的結果為1） 。如果不存在這樣的try-except塊，__except_handler4_common函數返回ExceptionContinueExecution（值0），由RtlDispatchException繼續訪問異常幀鍊表的下一個節點。如果找到了能處理當前異常的try-except塊，__except_handler4_common函數首先調用全局展開函數_EH4_GlobalUnwind，把從異常幀鍊表表頭所在的函數，直到能處理當前異常的函數的下一層函數，都做棧展開（unwinding）；然後，對能處理當前異常的函數，從包含執行點的最內存__try語句，直到能處理異常的try-except塊，調用局部展開函數_EH4_LocalUnwind；再執行能處理異常的try-except塊的異常處理代碼；最後，繼續執行try-except之後的其他代碼。
全局展開，是由_EH4_GlobalUnwind調用ntdll!RtlUnwind，RtlUnwind遍歷訪問異常幀鍊表，把從表頭幀到目標幀（不含）的所有異常處理回調函數用異常碼（STATUS_UNWIND 即0C0000027H）、異常標誌（EXCEPTION_UNWINDING即值2）調用。異常處理回調函數根據當前的異常碼與異常標誌，對當前異常幀所在函數，從包含了產生異常的執行點的最內層__try語句開始，直至函數內的最外層try塊，依次調用try-finally的清理用途代碼。這一步實際上是調用_EH4_LocalUnwind函數來完成。
局部展開，由_EH4_LocalUnwind函數實現，是從包含了產生異常的執行點的最內層__try語句開始，按著代碼的包含關係向外直至目標try-except語句為止，依次調用try-finally的清理用途代碼。

SAFESEH

為了防止篡改異常處理函數的地址，自Windows XP SP2起引入了檢查異常處理函數的地址是否有效。但這僅用於x86。而x64或ARM已經把所有異常處理函數放在了PDATA節中；該節叫做「.pdata」或者Exception Directory,用於存放處理異常的信息。
連結器選項/SAFESEH[:NO]用於設置是否需要開啟。
如果/SAFESEH被使用，連結器生成的可執行映像文件（image）包含它的安全的異常處理函數的清單。 
如果/SAFESEH未使用，連結器將檢查如果所有的模塊都兼容於安全的異常處理函數特性，則生成的可執行映像文件（image）包含它的安全的異常處理函數的清單；如果有模塊不兼容於安全的異常處理函數特性，則生成的可執行映像文件（image）不包含安全的異常處理函數的清單。
如果/SAFESEH:NO被使用，連結器生成的可執行映像文件（image）不包含安全的異常處理函數的清單。