El manejo estructurado de excepciones ( SEH - Structured Exception Handling ) es un mecanismo para manejar excepciones de software y hardware en el sistema operativo Microsoft Windows que permite a los programadores controlar el manejo de excepciones y también es una herramienta de depuración [1] .
Una excepción es un evento durante la ejecución del programa que hace que se comporte de manera anormal o incorrecta. Hay dos tipos de excepciones: hardware, que son generadas por el procesador , y software, generado por el sistema operativo y los programas de aplicación . El mecanismo estructurado de manejo de excepciones le permite manejar las excepciones de software y hardware de la misma manera.
Microsoft solo admite el mecanismo en el nivel del compilador a través de la implementación de construcciones de sintaxis no __tryestándar __excepty __finally. La palabra clave se __tryusa para resaltar una sección de código en la que el lanzamiento de una excepción será manejado por uno o más bloques __except. El código en el bloque __finallysiempre se ejecutará independientemente de otros bloques __tryy __except[2] .
Ejemplo de uso en C y C++
__intentar { // código protegido, // que se coloca en un marco SEH } __excepto ( filtro de excepción ) { // manejador de excepciones } __ finalmente { // código para ejecutar de todos modos }Los filtros de excepción pueden ser funciones ordinarias que devuelven tres expresiones constantes: [3]
Cada subproceso en cualquier proceso utiliza un registro ( selector de 16 bits ) fspara almacenar un puntero a una estructura de datos de bloque de información de subprocesos que contiene información sobre ese subproceso. Esta estructura almacena un puntero a la última estructura _EXCEPTION_REGISTRATION_RECORD registrada en la lista vinculada , que incluye un puntero al controlador de excepciones y un puntero a la entrada _EXCEPTION_REGISTRATION_RECORD anterior . [5] Cuando se crea un subproceso, el sistema operativo agrega un controlador de excepciones predeterminado llamado por . kernel32!UnhandledExceptionFilter
El prototipo de la función del controlador de devolución de llamada es el siguiente:
EXCEPCIÓN_DISPOSICIÓN __cdecl _excepto_controlador ( estructura _EXCEPTION_RECORD * Registro de excepción , vacío * EstablecimientoMarco , struct_CONTEXT * ContextRecord , _ vacío * DispatcherContext );Cada vez que el programador usa la construcción , se agrega a la pila__try del subproceso una nueva instancia de la estructura _EXCEPTION_REGISTRATION_RECORD, que apunta a la función _except_handler3 de la biblioteca msvcrt.dll . El código de bloque se llama desde _except_handler3. Al final del bloque , el compilador agrega código que elimina la entrada _EXCEPTION_REGISTRATION_RECORD actual y restaura el valor del puntero a la entrada anterior. __except__finally__tryfs:0
Cuando ocurre una excepción, el sistema itera a través de toda la cadena de controladores de interrupción en secuencia. Cada controlador devuelve un valor que indica si puede manejar esta excepción o no. El puntero al final de la lista de controladores de excepciones disponibles es el valor FFFFFFFFubicado en la pila después del último controlador. Si el sistema encuentra el controlador deseado, se le transfiere el control. Al mismo tiempo, después de encontrar el controlador relevante para la excepción que ha surgido, el sistema operativo no le transfiere el control de inmediato, sino que una vez más llama secuencialmente a todos los controladores a lo largo de la cadena con la bandera EH_UNWINDINGpara limpiar (llamar al destructor ) . [4] Si ninguno de los filtros del controlador de excepciones establecido por el programador devolvió EXCEPTION_EXECUTE_HANDLER o EXCEPTION_CONTINUE_EXECUTION, entonces se ejecuta el UnhandledExceptionFilter filtro del controlador de excepciones predeterminado, que se registra cuando el subproceso se prepara para ejecutarse.
Cuando ocurre una excepción, el sistema operativo no llama directamente al filtro de excepciones (que es responsable de si un controlador en particular manejará o no la excepción que ha ocurrido), sino que pasa su dirección a la función _except_handler3, desde donde se llama a la función de filtro. . Utiliza la siguiente estructura de datos: [6]
estructura _EXCEPCIÓN_REGISTRO { estructura _EXCEPCIÓN_REGISTRO * anterior ; vacío ( * controlador )( PEXCEPTION_RECORD , PEXCEPTION_REGISTRO , PCONTEXTO , PEXCEPCIÓN_REGISTRO ); struct scopetable_entry * scopetable ; int nivel de prueba ; int_ebp ; _ PEXCEPTION_POINTERS punteros x ; };El campo *scopetableapunta a la dirección de una matriz de estructuras scopetable_entryy el campo entero de nivel de prueba apunta a un índice en esta matriz. El campo _ebpcontiene el valor del puntero del marco de pila que existía antes de la creación de la estructura EXCEPTION_REGISTRATION. [7] La función _except_handler3llama al filtro requerido y, antes de llamar al controlador, desenrolla (limpia) la pila mediante la función ntdll.dll!RtlUnwind.
Si ninguno de los controladores instalados por el programador aceptó manejar la excepción, se llama a una función UnhandledExceptionFilterque verifica si el proceso se está ejecutando bajo el depurador y le informa si está disponible. [7] A continuación, la función llama al filtro de controlador predeterminado (que establece la función SetUnhandledExceptionFiltery que siempre devuelve EXCEPTION_EXECUTE_HANDLER). [7] Luego, según la configuración del sistema operativo, se llama al depurador o a la función NtRaiseHardError, que muestra un mensaje de error. [7]