Recherche…


Remarques

Chaque thread aura son propre dernier code d'erreur. L'API Windows définira le dernier code d'erreur sur le thread appelant.

Vous devez toujours appeler la fonction GetLastError immédiatement après avoir vérifié la valeur de retour d'une fonction API Windows.

La plupart des fonctions de l'API Windows définissent le dernier code d'erreur lorsqu'elles échouent. Certains définiront également le dernier code d'erreur lorsqu'ils réussiront. Il existe un certain nombre de fonctions qui ne définissent pas le dernier code d'erreur. Consultez toujours la documentation de la fonction API Windows.

Il est dangereux d'utiliser FORMAT_MESSAGE_FROM_SYSTEM sans FORMAT_MESSAGE_IGNORE_INSERTS lorsque vous utilisez la fonction FormatMessage pour obtenir une description d'un code d'erreur.

introduction

L'API Windows est fournie au moyen d'une interface C-callable. Le succès ou l'échec d'un appel d'API est signalé strictement par des valeurs de retour. Les exceptions ne font pas partie du contrat documenté (bien que certaines implémentations d' API puissent générer des exceptions SEH , par exemple lors de la transmission d'un argument lpCommandLine en lecture seule à CreateProcess ).

Le rapport d'erreurs appartient en gros à l'une des quatre catégories suivantes:

La documentation de chaque appel d'API appelle explicitement comment les erreurs sont signalées. Toujours consulter la documentation.

Erreur signalée par la valeur de retour uniquement

Certains appels d'API renvoient un seul indicateur d'échec / succès, sans aucune information supplémentaire (par exemple, GetObject ):

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

Erreur signalée avec des informations supplémentaires sur l'échec

En plus d'une valeur de retour d'échec / réussite, certains appels d'API définissent également la dernière erreur en cas d'échec (par exemple, CreateWindow ). La documentation contient généralement le libellé standard suivant pour ce cas:

Si la fonction réussit, la valeur renvoyée est <valeur de réussite spécifique à l’API> .
Si la fonction échoue, la valeur de retour est <valeur d'erreur spécifique à l'API> . Pour obtenir des informations d'erreur étendues, appelez GetLastError .

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

Il est essentiel que vous GetLastError() IMMÉDIATEMENT. Le dernier code d'erreur peut être remplacé par une autre fonction. Par conséquent, s'il y a un appel de fonction supplémentaire entre la fonction ayant échoué et l'appel à GetLastError() , le retour de GetLastError() ne sera plus fiable. Prenez des précautions supplémentaires lorsque vous traitez avec les constructeurs C ++.

Une fois que vous obtenez un code d'erreur, vous devez l'interpréter. Vous pouvez obtenir une liste complète des codes d'erreur sur MSDN, à la page Codes d'erreur système (Windows) . Vous pouvez également consulter les fichiers d'en-tête de votre système. le fichier avec toutes les constantes de code d'erreur est winerror.h . (Si vous avez le SDK officiel de Microsoft pour Windows 8 ou plus récent, il se trouve dans le sous-dossier shared du dossier include.)

Remarques sur l'appel de GetLastError() dans d'autres langages de programmation

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

Avec .net, vous ne devriez pas directement GetLastError() P / Invoke vers GetLastError() . Cela est dû au fait que le runtime .net rend les autres appels d'API Windows sur le même thread derrière votre dos. Par exemple, le ramasse-miettes peut appeler VirtualFree() s'il trouve suffisamment de mémoire qu'il n'utilise plus, et cela peut se produire entre votre appel de fonction prévu et votre appel à GetLastError() .

Au lieu de cela, .net fournit la fonction Marshal.GetLastWin32Error() , qui récupère la dernière erreur du dernier appel P / Invoke que vous avez effectué. Utilisez ceci au lieu d'appeler directement GetLastError() .

(.net ne semble pas vous empêcher d'importer GetLastError() toute façon; je ne sais pas pourquoi.)

Aller

Les différentes fonctionnalités fournies par Go pour appeler des fonctions DLL (qui résident à la fois dans le package syscall et le package golang.org/x/sys/windows ) renvoient trois valeurs: r1 , r2 et err . r2 n'est jamais utilisé; Vous pouvez utiliser l'identificateur vide ici. r1 est la valeur de retour de la fonction. err est le résultat de l'appel à GetLastError() mais converti en un type qui implémente une error , vous pouvez donc le transmettre aux fonctions appelantes à gérer.

Parce que Go ne sait pas quand appeler GetLastError() et quand ne pas le faire, il retournera toujours une erreur non nil . Par conséquent, le langage typique de gestion des erreurs Go

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

ne fonctionnera pas. Au lieu de cela, vous devez vérifier r1 , exactement comme vous le feriez dans C, et utiliser uniquement err si cela indique que la fonction a renvoyé une erreur:

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

Erreur signalée avec des informations supplémentaires sur l'échec et le succès

Certains appels d'API peuvent réussir ou échouer de plusieurs manières. Les API renvoient généralement des informations supplémentaires à la fois pour les invocations réussies et pour les erreurs (par exemple, 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.
    }
}

Erreur signalée en tant que valeur HRESULT

HRESULT s sont des valeurs numériques de 32 bits, où les bits ou les plages de bits codent des informations bien définies. Le MSB est un indicateur d'échec / succès, les bits restants stockant des informations supplémentaires. L'échec ou le succès peut être déterminé en utilisant les macros FAILED ou SUCCEEDED . HRESULT s sont couramment utilisés avec COM, mais apparaissent également dans les implémentations non COM (par exemple, 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).
        ...
    }
}

Conversion d'un code d'erreur en une chaîne de message

GetLastError renvoie un code d'erreur numérique. Pour obtenir un message d'erreur descriptif ( par exemple , pour afficher à un utilisateur), vous pouvez appeler 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 ++ , vous pouvez simplifier considérablement l'interface en utilisant la classe 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.");
    }
}

REMARQUE: Ces fonctions fonctionnent également pour les valeurs HRESULT . Changez simplement le premier paramètre de DWORD dwErrorCode à HRESULT hResult . Le reste du code peut rester inchangé.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow