2011-10-27 7 views
11

A regañadientes tengo que lidiar con excepciones estructuradas de Win32 nuevamente. Intento generar una cadena que describa una excepción. La mayor parte es sencilla, pero estoy atascado en algo básico: ¿cómo puedo convertir un código de excepción (el resultado de GetExceptionCode(), o el miembro ExceptionCode de EXCEPTION_RECORD) en una cadena que describe la excepción?¿Cómo convierto un código de excepción de Win32 en una cadena?

Estoy buscando algo que convierta, por ejemplo, 0xC0000005 a "Infracción de acceso". ¿Es solo una llamada al FormatMessage()?

+0

Sí, 'FormatMessage' debe hacer el truco. – avakar

Respuesta

3

Sí. Es un NTSTATUS, a fin de utilizar FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_FROM_HMODULE, y pasan los HMODULE de LoadLibrary("NTDLL.DLL")

Source

+0

Win32 es otro conjunto de códigos de error. – MSalters

+0

Gracias, eso casi funciona. Desafortunadamente, las cadenas en NTDLL.DLL no parecen usar los códigos de formato correctos para FormatMessage. La cadena para 0xc0000005 es 'La instrucción en% p referencia de memoria en% p.', Creo, que FormatMessage convierte a 'La instrucción en' 0x '(sic).Ver también esta [pregunta relacionada] (http://stackoverflow.com/questions/321898/how-to-get-the-name-description-of-an-exception). –

+0

Eh, ¿has pasado las direcciones reales? 'FormatMessage' ve dos parámetros'% p', y el resultado parece que no pudo formatear la primera dirección. – MSalters

7

códigos estructurado de excepciones se definen a través de números NTSTATUS. Aunque alguien de MS suggests usando FormatMessage() para convertir números NTSTATUS en cadenas, yo no haría esto. La bandera FORMAT_MESSAGE_FROM_SYSTEM se utiliza para convertir el resultado de GetLastError() en una cadena, por lo que no tiene sentido aquí. El uso del indicador FORMAT_MESSAGE_FROM_HMODULE junto con ntdll.dll dará resultados incorrectos para algunos códigos. Por ejemplo, para EXCEPTION_ACCESS_VIOLATION obtendrá The instruction at 0x, que no es muy informativo :).

Cuando observa las cadenas que están almacenadas en ntdll.dll resulta obvio que se supone que muchas de ellas se utilizan con la función printf(), no con el FormatMessage(). Por ejemplo, la cadena de EXCEPTION_ACCESS_VIOLATION es:

The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s.

%0 se trata por FormatMessage() como la secuencia de escape que significa mensaje terminador, no un inserto. Las inserciones son% 1 a% 99. Es por eso que flag FORMAT_MESSAGE_IGNORE_INSERTS no hace ninguna diferencia.

Es posible que desee cargar la cadena de ntdll.dll y pasarlo a vprintf pero se necesita para preparar argumentos exactamente como está previsto por la cadena (por ejemplo, para EXCEPTION_ACCESS_VIOLATION es unsigned long, unsigned long, char*). Y este enfoque tiene un gran inconveniente: cualquier cambio en el número, orden o tamaño de los argumentos en ntdll.dll puede romper su código.

Por lo tanto, es más seguro y fácil codificar las cadenas en su propio código. Me parece peligroso usar cadenas preparadas por otra persona sin coordinación conmigo :) y, además, para otra función. Esta es solo una posibilidad más de mal funcionamiento.

+0

Gracias por su respuesta! (Aunque la pregunta ahora es bastante antigua.) Si miras mis comentarios sobre la otra respuesta, verás que descubrí los problemas que mencionas. Acabo de encauzar los mensajes problemáticos; Creo que prefiero codificar todo lo que sugieres. (Especialmente dado que se pueden agregar más errores en el futuro). –

+0

Es una cuestión de gusto, por supuesto. Elegí imprimir el número de código (útil para posibles nuevos errores como anotó), su representación de cadena (por ejemplo, "EXCEPTION_INVALID_DISPOSITION") y otros valores de EXCEPTION_RECORD struct. Para mis necesidades es suficiente. No creo que tenga sentido mostrar la descripción detallada a los usuarios finales. La mayoría de ellos apenas lo entenderá de todos modos. E incluso para los avanzados no será útil, no arreglarán su programa. El usuario final simplemente debe pasar esa información a los desarrolladores para su investigación. Y como desarrollador, puedo leer la descripción actualizada del código de error en Internet. – 4LegsDrivenCat

1

Es complicado gestionar correctamente el formato de flujo que tienen algunas de las cadenas NTSTATUS. Debería considerar convertirlo en un mensaje Win32 con RtlNtStatusToDosError(), que viene en el encabezado Winternl.h. Necesitarás tener ntdll.lib en tu entrada del enlazador.

Ejemplo aplicación:

// Returns length of resulting string, excluding null-terminator. 
// Use LocalFree() to free the buffer when it is no longer needed. 
// Returns 0 upon failure, use GetLastError() to get error details. 
DWORD FormatNtStatus(NTSTATUS nsCode, TCHAR **ppszMessage) { 

    // Get handle to ntdll.dll. 
    HMODULE hNtDll = LoadLibrary(_T("NTDLL.DLL")); 

    // Check for fail, user may use GetLastError() for details. 
    if (hNtDll == NULL) return 0; 

    // Call FormatMessage(), note use of RtlNtStatusToDosError(). 
    DWORD dwRes = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE, 
     hNtDll, RtlNtStatusToDosError(nsCode), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
     (LPTSTR)ppszMessage, 0, NULL); 

    // Free loaded dll module and decrease its reference count. 
    FreeLibrary(hNtDll); 

    return dwRes; 
} 
+0

¿Qué hace cuando se da 0xC0000005? –

+0

@ AlanStokes Se convertirá en ERROR_NOACCESS (En la localización en inglés: "Acceso no válido a la ubicación de la memoria"). –

Cuestiones relacionadas