A strukturált kivételkezelés ( SEH - Structured Exception Handling ) a Microsoft Windows operációs rendszer szoftveres és hardveres kivételeinek kezelésére szolgáló mechanizmus, amely lehetővé teszi a programozóknak a kivételkezelés vezérlését, és egyben hibakereső eszköz is [1] .
Kivételt képez a program végrehajtása során bekövetkezett esemény, amely abnormálisan vagy helytelenül viselkedik. Kétféle kivétel létezik: a hardver, amelyet a processzor hoz létre , és a szoftver, amelyet az operációs rendszer és az alkalmazási programok generálnak . A strukturált kivételkezelési mechanizmus lehetővé teszi a szoftveres és hardveres kivételek azonos kezelését.
A mechanizmust a Microsoft csak fordítói szinten támogatja a nem szabványos szintaktikai konstrukciók __tryés . A kulcsszó a kód egy olyan szakaszának kiemelésére szolgál, amelyben a kivétel dobását egy vagy több blokk kezeli . A blokkban lévő kód mindig végrehajtásra kerül, függetlenül a többi blokktól és [2] . __except__finally__try__except__finally__try__except
Használati példa C és C++ nyelven
__próbáld meg { // védett kód, // amely egy SEH keretben van elhelyezve } __except ( kivételszűrő ) { _ // kivételkezelő } __végre { // kód, ami mindenképpen lefut }A kivételszűrők lehetnek közönséges függvények, amelyek három állandó kifejezést adnak vissza: [3]
Bármely folyamatban minden szál egy regisztert (16 bites választó ) használ, hogy egy szál információs blokkfs adatszerkezetére mutató mutatót tároljon, amely információkat tartalmaz az adott szálról. Ez a struktúra egy mutatót tárol a hivatkozott listában az utoljára regisztrált _EXCEPTION_REGISTRATION_RECORD struktúrára , amely tartalmaz egy mutatót a kivételkezelőre és egy mutatót az előző _EXCEPTION_REGISTRATION_RECORD bejegyzésre . [5] A szál létrehozásakor az operációs rendszer hozzáad egy alapértelmezett kivételkezelőt, amelyet a . kernel32!UnhandledExceptionFilter
A visszahíváskezelő függvény prototípusa a következő:
EXCEPTION_DISPOSITION __cdecl _except_handler ( struct _EXCEPTION_RECORD * ExceptionRecord , void * EstablisherFrame , struct_CONTEXT * ContextRecord , _ void * DispatcherContext );Minden alkalommal, amikor a programozó a konstrukciót használja __try, az _EXCEPTION_REGISTRATION_RECORD szerkezet új példánya, amely az msvcrt.dll könyvtár _except_handler3 függvényére mutat , hozzáadódik a szál vereméhez . A blokkkód __excepthívása __finallya _except_handler3-ból történik. A blokk végén a __tryfordító olyan kódot ad hozzá, amely eltávolítja az aktuális _EXCEPTION_REGISTRATION_RECORD bejegyzést, és visszaállítja a mutató értékét fs:0az előző bejegyzéshez.
Kivétel esetén a rendszer a megszakításkezelők teljes láncán sorban végighalad. Minden kezelő egy értéket ad vissza, jelezve, hogy képes-e kezelni ezt a kivételt vagy sem. Az elérhető kivételkezelők listájának végére mutató mutató FFFFFFFFaz utolsó kezelő utáni veremben található érték. Ha a rendszer megtalálja a kívánt kezelőt, akkor az irányítás átkerül rá. Ugyanakkor, miután megtalálta a felmerült kivételre vonatkozó megfelelő kezelőt, az operációs rendszer nem adja át azonnal az irányítást, hanem ismét egymás után hívja a lánc összes kezelőjét a zászlóval EH_UNWINDING, hogy tisztítsák meg (hívja a destruktort ) . [4] Ha a programozó által beállított kivételkezelő szűrők egyike sem adta vissza az EXCEPTION_EXECUTE_HANDLER vagy EXCEPTION_CONTINUE_EXECUTION értéket, akkor az UnhandledExceptionFilter alapértelmezett kivételkezelő szűrő, amelyet a szál futásra készülésekor regisztrál, végrehajtódik.
Kivétel esetén az operációs rendszer nem közvetlenül hívja meg a kivételszűrőt (amely felelős azért, hogy egy adott kezelő kezeli-e a bekövetkezett kivételt vagy sem), hanem átadja a címét a függvénynek _except_handler3, ahonnan a szűrő függvény meghívásra kerül. . A következő adatstruktúrát használja: [6]
struct _EXCEPTION_REGISTRATION { struct _EXCEPTION_REGISTRATION * prev ; void ( * kezelő )( PEXCEPTION_RECORD , PEXCEPTION_REGISTRATION , PCONTEXT , PEXCEPTION_RECORD ); struct hatókörtábla_bejegyzés * hatókörtábla ; int trylevel ; int_ebp ; _ PEXCEPTION_POINTERS xpointers ; };A mező *scopetableegy struktúrák tömbjének címére mutat scopetable_entry, a trylevel integer mező pedig egy indexre mutat ebben a tömbben. A mező _ebptartalmazza a verem keretmutató értékét, amely az EXCEPTION_REGISTRATION struktúra létrehozása előtt létezett. [7] A függvény _except_handler3meghívja a szükséges szűrőt, és a kezelő meghívása előtt letekercseli (megtisztítja) a veremet a függvény segítségével ntdll.dll!RtlUnwind.
Ha a programozó által telepített kezelők egyike sem járult hozzá a kivétel kezeléséhez, akkor egy függvényt hívunk UnhandledExceptionFilter, amely ellenőrzi, hogy a folyamat fut-e a hibakereső alatt , és tájékoztatja, ha elérhető. [7] A függvény ezután meghívja az alapértelmezett kezelőszűrőt (amelyet a függvény állít be, SetUnhandledExceptionFilterés mindig az EXCEPTION_EXECUTE_HANDLER értéket adja vissza). [7] Ezután az operációs rendszer beállításaitól függően vagy a debugger, vagy az NtRaiseHardError függvény meghívódik, ami hibaüzenetet jelenít meg. [7]