Szukaj…


Uwagi

Każdy wątek będzie miał swój ostatni kod błędu. Interfejs API systemu Windows ustawi ostatni kod błędu w wątku wywołującym.

Należy zawsze wywoływać funkcję GetLastError natychmiast po sprawdzeniu wartości zwracanej przez funkcję Windows API.

Większość funkcji Windows API ustawia ostatni kod błędu, gdy zawodzą. Niektórzy ustawią również ostatni kod błędu, gdy się powiedzie. Istnieje wiele funkcji, które nie ustawiają ostatniego kodu błędu. Zawsze zapoznaj się z dokumentacją funkcji Windows API.

Używanie FORMAT_MESSAGE_FROM_SYSTEM bez FORMAT_MESSAGE_IGNORE_INSERTS podczas korzystania z funkcji FormatMessage celu uzyskania opisu kodu błędu jest niebezpieczne.

Wprowadzenie

Interfejs API systemu Windows jest dostarczany za pomocą interfejsu C-callable. Powodzenie lub niepowodzenie wywołania interfejsu API jest zgłaszane wyłącznie za pomocą zwracanych wartości. Wyjątki nie są częścią udokumentowanej umowy (chociaż niektóre implementacje API mogą zgłaszać wyjątki SEH , np. Przy przekazywaniu argumentu lpCommandLine tylko do odczytu do CreateProcess ).

Zgłaszanie błędów z grubsza należy do jednej z czterech kategorii:

Dokumentacja każdego wywołania interfejsu API wyraźnie wzywa, w jaki sposób zgłaszane są błędy. Zawsze zapoznaj się z dokumentacją.

Błąd zgłaszany tylko przez wartość zwracaną

Niektóre wywołania API zwracają pojedynczą flagę niepowodzenia / sukcesu, bez żadnych dodatkowych informacji (np. GetObject ):

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

Zgłoszony błąd z dodatkowymi informacjami o awarii

Oprócz wartości zwracanej w przypadku niepowodzenia / powodzenia, niektóre wywołania API również ustawiają ostatni błąd w przypadku awarii (np. CreateWindow ). Dokumentacja zwykle zawiera następujące standardowe sformułowanie dla tego przypadku:

Jeśli funkcja się powiedzie, zwracana wartość to <wartość sukcesu specyficzna dla API> .
Jeśli funkcja zawiedzie, zwracana wartość to <wartość błędu specyficzna dla API> . Aby uzyskać rozszerzone informacje o błędzie, wywołaj GetLastError .

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

Ważne jest, aby wywołać NATYCHMIAST GetLastError() . Ostatni kod błędu może zostać zastąpiony przez dowolną inną funkcję, więc jeśli istnieje dodatkowe wywołanie funkcji między funkcją, która zakończyła się niepowodzeniem, a wywołaniem GetLastError() , powrót z GetLastError() nie będzie już wiarygodny. Zachowaj szczególną ostrożność podczas pracy z konstruktorami C ++.

Gdy otrzymasz kod błędu, musisz go zinterpretować. Pełną listę kodów błędów można uzyskać w witrynie MSDN na stronie System Error Codes (Windows) . Alternatywnie możesz zajrzeć do plików nagłówkowych systemu; plik ze wszystkimi stałymi kodami błędów to winerror.h . (Jeśli masz oficjalny zestaw SDK firmy Microsoft dla systemu Windows 8 lub nowszego, znajduje się on w shared podfolderze folderu dołączanego).

Uwagi na temat wywoływania funkcji GetLastError() w innych językach programowania

Języki .net (C #, VB itp.)

Dzięki .net nie powinieneś P / Invoke bezpośrednio w GetLastError() . Wynika to z faktu, że środowisko wykonawcze .net wykona inne wywołania API systemu Windows w tym samym wątku za twoimi plecami. Na przykład śmieciarz może wywołać VirtualFree() jeśli znajdzie wystarczającą ilość pamięci, której już nie używa, i może się to zdarzyć między zamierzonym wywołaniem funkcji a wywołaniem GetLastError() .

Zamiast tego .net zapewnia funkcję Marshal.GetLastWin32Error() , która pobierze ostatni błąd z ostatniego wywołania P / Invoke, które sam Marshal.GetLastWin32Error() . Użyj tego zamiast wywoływać bezpośrednio GetLastError() .

(.net wydaje się nie przeszkadzać w importowaniu GetLastError() ; nie jestem pewien, dlaczego).

Udać się

Różne funkcje Go do wywoływania funkcji DLL (które znajdują się zarówno w pakiecie syscall i pakiecie golang.org/x/sys/windows ) zwracają trzy wartości: r1 , r2 i err . r2 nigdy nie jest używane; możesz tam użyć pustego identyfikatora. r1 jest wartością zwracaną przez funkcję. err jest wynikiem wywołania GetLastError() ale przekształcony w typ, który implementuje error , dzięki czemu można przekazać go do funkcji wywołujących do obsługi.

Ponieważ Go nie wie, kiedy wywołać GetLastError() a kiedy nie, zawsze zwróci błąd inny niż nil . Dlatego typowy id obsługi błędów Go

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

nie będzie działać. Zamiast tego musisz sprawdzić r1 , dokładnie tak, jak w C, i użyć err , tylko jeśli oznacza to , że funkcja zwróciła błąd:

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

Zgłoszony błąd z dodatkowymi informacjami o niepowodzeniu i sukcesie

Niektóre wywołania interfejsu API mogą zakończyć się powodzeniem lub niepowodzeniem na więcej niż jeden sposób. Interfejsy API zwykle zwracają dodatkowe informacje zarówno dla udanych wywołań, jak i błędów (np. 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.
    }
}

Błąd zgłoszony jako wartość HRESULT

HRESULT to 32-bitowe wartości liczbowe, w których bity lub zakresy bitów kodują dobrze zdefiniowane informacje. MSB jest flagą niepowodzenia / sukcesu, a pozostałe bity przechowują dodatkowe informacje. Niepowodzenie lub sukces można określić za pomocą makr FAILED lub SUCCEEDED . HRESULT są powszechnie używane z COM, ale pojawiają się również w implementacjach innych niż COM (np. 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).
        ...
    }
}

Konwertowanie kodu błędu na ciąg komunikatu

GetLastError zwraca numeryczny kod błędu. Aby uzyskać opisowy komunikat o błędzie ( np. Wyświetlany użytkownikowi), możesz wywoł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);
}

W C ++ można znacznie uprościć interfejs, używając klasy 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.");
    }
}

UWAGA: Te funkcje działają również dla wartości HRESULT . Wystarczy zmienić pierwszy parametr z DWORD dwErrorCode na HRESULT hResult . Reszta kodu może pozostać niezmieniona.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow