Buscar..


Observaciones

Cada hilo tendrá su propio último código de error. La API de Windows establecerá el último código de error en el subproceso de llamada.

Siempre debe llamar a la función GetLastError inmediatamente después de verificar el valor de retorno de una función de la API de Windows.

La mayoría de las funciones de la API de Windows establecen el último código de error cuando fallan. Algunos también establecerán el último código de error cuando tengan éxito. Hay una serie de funciones que no establecen el último código de error. Consulte siempre la documentación de la función API de Windows.

No es seguro usar FORMAT_MESSAGE_FROM_SYSTEM sin FORMAT_MESSAGE_IGNORE_INSERTS cuando se usa la función FormatMessage para obtener una descripción de un código de error.

Introducción

La API de Windows se proporciona mediante una interfaz que se puede llamar en C. El éxito o el fracaso de una llamada a la API se informa estrictamente a través de valores de retorno. Las excepciones no forman parte del contrato documentado (aunque algunas implementaciones de API pueden generar excepciones SEH , por ejemplo, al pasar un argumento lpCommandLine de solo lectura a CreateProcess ).

El informe de errores aproximadamente se divide en una de cuatro categorías:

La documentación para cada llamada a la API llama explícitamente, cómo se informan los errores. Siempre consulte la documentación.

Error reportado solo por el valor de retorno

Algunas llamadas a la API devuelven un solo indicador de falla / éxito, sin ninguna información adicional (por ejemplo, GetObject ):

if ( GetObjectW( obj, 0, NULL ) == 0 ) {
    // Failure: no additional information available.
}

Error reportado con información adicional en caso de falla

Además de un valor de retorno de error / éxito, algunas llamadas a la API también establecen el último error en caso de error (por ejemplo, CreateWindow ). La documentación generalmente contiene la siguiente redacción estándar para este caso:

Si la función tiene éxito, el valor de retorno es <valor de éxito específico de la API> .
Si la función falla, el valor de retorno es <valor de error específico de API> . Para obtener información de error extendida, llame a GetLastError .

if ( CreateWindowW( ... ) == NULL ) {
    // Failure: get additional information.
    DWORD dwError = GetLastError();
} else {
    // Success: must not call GetLastError.
}

Es vital que llame a GetLastError() INMEDIATAMENTE. El último código de error puede ser sobrescrito por cualquier otra función, por lo que si hay una llamada de función extra entre la función que falló y la llamada a GetLastError() , el retorno de GetLastError() ya no será confiable. Tenga mucho cuidado al tratar con constructores de C ++.

Una vez que obtenga un código de error, deberá interpretarlo. Puede obtener una lista completa de códigos de error en MSDN, en la página Códigos de error del sistema (Windows) . Alternativamente, puede buscar en los archivos de encabezado de su sistema; El archivo con todas las constantes de código de error es winerror.h . (Si tiene el SDK oficial de Microsoft para Windows 8 o más reciente, esto está en la subcarpeta shared de la carpeta de inclusión).

Notas sobre la llamada a GetLastError() en otros lenguajes de programación

Idiomas .net (C #, VB, etc.)

Con .net, no debe P / Invoke a GetLastError() directamente. Esto se debe a que el tiempo de ejecución de .net realizará otras llamadas a la API de Windows en el mismo hilo que está detrás de su espalda. Por ejemplo, el recolector de basura puede llamar a VirtualFree() si encuentra suficiente memoria que ya no está usando, y esto puede suceder entre su llamada de función deseada y su llamada a GetLastError() .

En su lugar, .net proporciona la función Marshal.GetLastWin32Error() , que recuperará el último error de la última llamada P / Invoke que usted mismo realizó. Use esto en lugar de llamar a GetLastError() directamente.

(.net no parece impedirle importar GetLastError() todos modos; no estoy seguro de por qué).

Ir

Las diversas funciones que proporciona Go para llamar a las funciones de DLL (que residen en el paquete syscall y en el paquete golang.org/x/sys/windows ) devuelven tres valores: r1 , r2 y err . r2 nunca se usa; Puede usar el identificador en blanco allí. r1 es el valor de retorno de la función. err es el resultado de llamar a GetLastError() pero se convierte en un tipo que implementa un error , por lo que puede pasarlo a las funciones de llamada para que lo manejen.

Como Go no sabe cuándo llamar a GetLastError() y cuándo no, siempre devolverá un error no nil . Por lo tanto, el lenguaje típico de manejo de errores Go

r1, _, err := syscall.Syscall12(CreateWindowW.Addr(), ...)
if err != nil {
    // handle err
}
// use r1

no trabajará. En su lugar, debe verificar r1 , exactamente como lo haría en C, y solo usar err si eso indica que la función devolvió un error:

r1, _, err := syscall.Syscall12(CreateWindowW.Addr(), ...)
if r1 == 0 {
    // handle err
}
// use r1

Error reportado con información adicional sobre el fracaso y el éxito

Algunas llamadas a la API pueden tener éxito o fallar de más de una manera. Las API generalmente devuelven información adicional tanto para invocaciones exitosas como para errores (por ejemplo, CreateMutex ).

if ( CreateMutexW( NULL, TRUE, L"Global\\MyNamedMutex" ) == NULL ) {
    // Failure: get additional information.
    DWORD dwError = GetLastError();
} else {
    // Success: Determine which mutex was returned.
    if ( GetLastError() == ERROR_ALREADY_EXISTS ) {
        // Existing mutex object returned.
    } else {
        // Newly created mutex object returned.
    }
}

Error reportado como valor HRESULT

Los HRESULT son valores numéricos de 32 bits, donde los bits o rangos de bits codifican información bien definida. El MSB es un indicador de falla / éxito, con los bits restantes almacenando información adicional. Fracaso o el éxito se puede determinar usando los fallidos o tenido éxito macros. HRESULT se usan comúnmente con COM, pero también aparecen en implementaciones que no son COM (por ejemplo, StringCchPrintf ).

const size_t cchBuf = 5;
wchar_t buffer[cchBuf] = { 0 };
HRESULT hr = StringCchPrintfW( buffer, cchBuf, L"%s", L"Hello, world!" );
if ( FAILED( hr ) ) {
    // Failure: Determine specific reason.
    switch ( hr ) {
    case STRSAFE_E_INSUFFICIENT_BUFFER:
        // Buffer too small; increase buffer and retry.
        ...
    case STRSAFE_E_INVALID_PARAMETER:
        // Invalid parameter; implement custom error handling (e.g. logging).
        ...
    default:
        // Some other error code; implement custom error handling (e.g. logging).
        ...
    }
}

Convertir un código de error en una cadena de mensaje

GetLastError devuelve un código de error numérico. Para obtener un mensaje de error descriptivo ( por ejemplo , para mostrarlo a un usuario), puede llamar a FormatMessage :

// This functions fills a caller-defined character buffer (pBuffer)
// of max length (cchBufferLength) with the human-readable error message
// for a Win32 error code (dwErrorCode).
// 
// Returns TRUE if successful, or FALSE otherwise.
// If successful, pBuffer is guaranteed to be NUL-terminated.
// On failure, the contents of pBuffer are undefined.
BOOL GetErrorMessage(DWORD dwErrorCode, LPTSTR pBuffer, DWORD cchBufferLength)
{
    if (cchBufferLength == 0)
    {
        return FALSE;
    }

    DWORD cchMsg = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                                 NULL,  /* (not used with FORMAT_MESSAGE_FROM_SYSTEM) */
                                 dwErrorCode,
                                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                                 pBuffer,
                                 cchBufferLength,
                                 NULL);
    return (cchMsg > 0);
}

En C ++ , puede simplificar considerablemente la interfaz utilizando la clase std::string :

#include <Windows.h>
#include <exception>
#include <stdexcept>
#include <memory>
#include <string>
typedef std::basic_string<TCHAR> String;

String GetErrorMessage(DWORD dwErrorCode)
{
    LPTSTR psz = NULL;
    const DWORD cchMsg = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
                                         | FORMAT_MESSAGE_IGNORE_INSERTS
                                         | FORMAT_MESSAGE_ALLOCATE_BUFFER,
                                       NULL, // (not used with FORMAT_MESSAGE_FROM_SYSTEM)
                                       dwErrorCode,
                                       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                                       reinterpret_cast<LPTSTR>(&psz),
                                       0,
                                       NULL);
    if (cchMsg > 0)
    {
        // Assign buffer to smart pointer with custom deleter so that memory gets released
        // in case String's c'tor throws an exception.
        auto deleter = [](void* p) { ::HeapFree(::GetProcessHeap(), 0, p); };
        std::unique_ptr<TCHAR, decltype(deleter)> ptrBuffer(psz, deleter);
        return String(ptrBuffer.get(), cchMsg);
    }
    else
    {
        throw std::runtime_error("Failed to retrieve error message string.");
    }
}

NOTA: Estas funciones también funcionan para los valores HRESULT . Simplemente cambie el primer parámetro de DWORD dwErrorCode a HRESULT hResult . El resto del código puede permanecer sin cambios.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow