Ricerca…


Osservazioni

Ogni thread avrà il suo ultimo codice di errore. L'API di Windows imposterà l'ultimo codice di errore sul thread chiamante.

È necessario chiamare sempre la funzione GetLastError immediatamente dopo aver controllato il valore restituito da una funzione dell'API di Windows.

La maggior parte delle funzioni dell'API di Windows imposta l'ultimo codice di errore quando falliscono. Alcuni imposteranno anche l'ultimo codice di errore quando avranno successo. Ci sono un certo numero di funzioni che non impostano l'ultimo codice di errore. Fare sempre riferimento alla documentazione della funzione API di Windows.

Non è sicuro utilizzare FORMAT_MESSAGE_FROM_SYSTEM senza FORMAT_MESSAGE_IGNORE_INSERTS quando si utilizza la funzione FormatMessage per ottenere una descrizione di un codice di errore.

introduzione

L'API di Windows viene fornita tramite un'interfaccia C-callable. Il successo o il fallimento di una chiamata API viene riportato rigorosamente attraverso i valori di ritorno. Le eccezioni non fanno parte del contratto documentato (sebbene alcune implementazioni API possano sollevare eccezioni SEH , ad esempio quando si passa un argomento lpCommandLine di sola lettura a CreateProcess ).

La segnalazione degli errori cade approssimativamente in una delle quattro categorie:

La documentazione per ogni chiamata API chiama esplicitamente come vengono segnalati gli errori. Consultare sempre la documentazione.

Errore segnalato solo dal valore di ritorno

Alcune chiamate API restituiscono un singolo flag di errore / successo, senza alcuna informazione aggiuntiva (es. GetObject ):

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

Errore segnalato con ulteriori informazioni sull'errore

Oltre a un valore di errore / esito positivo, alcune chiamate API impostano anche l'ultimo errore in caso di errore (ad esempio, CreateWindow ). La documentazione di solito contiene il seguente testo standard per questo caso:

Se la funzione ha esito positivo, il valore restituito è <valore successo specifico dell'API> .
Se la funzione non riesce, il valore restituito è <valore errore specifico dell'API> . Per ottenere informazioni sull'errore estese, chiama GetLastError .

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

È fondamentale che si chiami GetLastError() IMMEDIATAMENTE. L'ultimo codice di errore può essere sovrascritto da qualsiasi altra funzione, quindi se c'è una chiamata di funzione aggiuntiva tra la funzione non riuscita e la chiamata a GetLastError() , il ritorno da GetLastError() non sarà più affidabile. Prestare particolare attenzione quando si ha a che fare con costruttori C ++.

Una volta ottenuto un codice di errore, sarà necessario interpretarlo. È possibile ottenere un elenco completo dei codici di errore su MSDN, nella pagina Codici errore di sistema (Windows) . In alternativa, puoi cercare nei file di intestazione del tuo sistema; il file con tutte le costanti del codice di errore è winerror.h . (Se disponi dell'SDK ufficiale di Microsoft per Windows 8 o successivo, questo è nella sottocartella shared della cartella include.)

Note su come chiamare GetLastError() in altri linguaggi di programmazione

.net languages ​​(C #, VB, ecc.)

Con .net, non si dovrebbe inviare P / Invoke a GetLastError() direttamente. Questo perché il runtime .net farà altre chiamate API di Windows sullo stesso thread dietro la schiena. Ad esempio, il garbage collector potrebbe chiamare VirtualFree() se trova memoria sufficiente che non sta più utilizzando, e ciò può accadere tra la chiamata alla funzione desiderata e la chiamata a GetLastError() .

Invece, .net fornisce la funzione Marshal.GetLastWin32Error() , che recupererà l'ultimo errore dall'ultima chiamata P / Invoke creata dall'utente. Utilizzare questo invece di chiamare direttamente GetLastError() .

(.net non sembra GetLastError() importare GetLastError() ogni caso, non sono sicuro del perché.)

Partire

Le varie funzioni fornite da Go per chiamare le funzioni DLL (che risiedono in entrambi i pacchetti syscall e pacchetto golang.org/x/sys/windows ) restituiscono tre valori: r1 , r2 ed err . r2 non è mai usato; puoi usare l'identificatore vuoto lì. r1 è il valore di ritorno della funzione. err è il risultato della chiamata a GetLastError() ma convertita in un tipo che implementa l' error , quindi è possibile passarlo a funzioni di chiamata da gestire.

Perché Go non sa quando chiamare GetLastError() e quando non lo fa, restituirà sempre un errore non nil . Pertanto, il tipico idioma di gestione degli errori Go

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

non funzionerà. Invece, è necessario controllare r1 , esattamente come si farebbe in C, e utilizzare err solo se questo indica che la funzione ha restituito un errore:

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

Errore segnalato con ulteriori informazioni su errore e successo

Alcune chiamate API possono avere successo o fallire in più di un modo. Le API in genere restituiscono informazioni aggiuntive sia per invocazioni riuscite che per errori (ad esempio, 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.
    }
}

Errore segnalato come valore HRESULT

I valori HRESULT sono valori numerici a 32 bit, in cui bit o intervalli di bit codificano informazioni ben definite. L'MSB è un flag di errore / successo, con i bit rimanenti che memorizzano informazioni aggiuntive. Il fallimento o il successo possono essere determinati usando le macro FAILED o SUCCEEDED . HRESULT sono comunemente usati con COM, ma appaiono anche in implementazioni non COM (ad es. 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).
        ...
    }
}

Conversione di un codice di errore in una stringa di messaggio

GetLastError restituisce un codice di errore numerico. Per ottenere un messaggio di errore descrittivo ( ad esempio , per visualizzare un utente), è possibile chiamare 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);
}

In C ++ , puoi semplificare notevolmente l'interfaccia usando 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.");
    }
}

NOTA: queste funzioni funzionano anche con valori HRESULT . Basta cambiare il primo parametro da DWORD dwErrorCode a HRESULT hResult . Il resto del codice può rimanere invariato.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow