Suche…


Bemerkungen

Jeder Thread hat seinen eigenen letzten Fehlercode. Die Windows-API setzt den letzten Fehlercode im aufrufenden Thread.

Sie sollten die GetLastError Funktion immer unmittelbar nach der Überprüfung des Rückgabewerts einer Windows-API-Funktion aufrufen.

Die meisten Windows-API-Funktionen legen den letzten Fehlercode fest, wenn sie fehlschlagen. Einige setzen auch den letzten Fehlercode, wenn sie erfolgreich sind. Es gibt eine Reihe von Funktionen, die den letzten Fehlercode nicht festlegen. Beziehen Sie sich immer auf die Dokumentation der Windows-API-Funktion.

Es ist nicht sicher, FORMAT_MESSAGE_FROM_SYSTEM ohne FORMAT_MESSAGE_IGNORE_INSERTS wenn Sie die Funktion FormatMessage verwenden, um eine Beschreibung eines Fehlercodes FormatMessage .

Einführung

Die Windows-API wird über eine C-aufrufbare Schnittstelle bereitgestellt. Erfolg oder Misserfolg eines API-Aufrufs wird ausschließlich über Rückgabewerte gemeldet. Ausnahmen sind nicht Teil des dokumentierten Vertrags (obwohl einige API- Implementierungen SEH- Ausnahmen verursachen können, z. B. wenn ein schreibgeschütztes Argument LpCommandLine an CreateProcess übergeben wird ).

Fehlerberichte lassen sich grob in eine von vier Kategorien einteilen:

In der Dokumentation für jeden API-Aufruf wird explizit angegeben, wie Fehler gemeldet werden. Lesen Sie immer die Dokumentation.

Fehler nur durch Rückgabewert gemeldet

Einige API-Aufrufe geben ein einzelnes Failure / Success-Flag ohne zusätzliche Informationen zurück (z. B. GetObject ):

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

Fehler mit zusätzlichen Informationen zum Fehler gemeldet

Zusätzlich zu einem Rückgabewert für Fehler / Erfolg setzen einige API-Aufrufe auch den letzten Fehler bei einem Fehler (z. B. CreateWindow ). Die Dokumentation enthält normalerweise den folgenden Standardwortlaut für diesen Fall:

Wenn die Funktion erfolgreich ist, lautet der Rückgabewert <API-spezifischer Erfolgswert> .
Wenn die Funktion fehlschlägt, lautet der Rückgabewert <API-spezifischer Fehlerwert> . Rufen Sie GetLastError auf, um erweiterte Fehlerinformationen abzurufen .

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

Es ist wichtig, dass Sie GetLastError() SOFORT aufrufen. Der letzte Fehlercode kann von jeder anderen Funktion überschrieben werden. Wenn also zwischen der fehlgeschlagenen Funktion und dem Aufruf von GetLastError() ein zusätzlicher Funktionsaufruf besteht, ist die Rückgabe von GetLastError() nicht mehr zuverlässig. Seien Sie besonders vorsichtig beim Umgang mit C ++ - Konstruktoren.

Sobald Sie einen Fehlercode erhalten, müssen Sie ihn interpretieren. Sie finden eine umfassende Liste der Fehlercodes auf MSDN auf der Seite System Error Codes (Windows) . Alternativ können Sie in Ihren System-Header-Dateien nachsehen. Die Datei mit allen Fehlercodekonstanten lautet winerror.h . (Wenn Sie das offizielle SDK von Microsoft für Windows 8 oder neuer verwenden, befindet sich dieses im shared Unterordner des Include-Ordners.)

Hinweise zum Aufruf von GetLastError() in anderen Programmiersprachen

.net-Sprachen (C #, VB usw.)

Mit .net sollten Sie nicht direkt auf GetLastError() . Dies liegt daran, dass die .net-Laufzeitumgebung andere Windows-API-Aufrufe in demselben Thread hinter Ihrem Rücken durchführt. Beispielsweise könnte der Garbage Collector VirtualFree() aufrufen, wenn er genügend Speicher findet, den er nicht mehr benötigt, und dies zwischen dem beabsichtigten Funktionsaufruf und dem Aufruf von GetLastError() .

Stattdessen stellt .net die Funktion Marshal.GetLastWin32Error() , die den letzten Fehler des letzten von Ihnen ausgeführten P / Invoke-Aufrufs abruft. Verwenden Sie dies, anstatt GetLastError() direkt GetLastError() .

(.net scheint Sie sowieso nicht davon GetLastError() , GetLastError() importieren; ich bin mir nicht sicher, warum.)

Gehen

Die verschiedenen von Go bereitgestellten Funktionen zum Aufrufen von DLL-Funktionen (die sich sowohl im Paket syscall als auch im Paket golang.org/x/sys/windows ) geben drei Werte zurück: r1 , r2 und err . r2 wird nie verwendet; Sie können dort den leeren Bezeichner verwenden. r1 ist der Rückgabewert der Funktion. err ist das Ergebnis des Aufrufs von GetLastError() jedoch in einen Typ konvertiert, der error implementiert, sodass Sie ihn an aufrufende Funktionen übergeben können.

Da Go nicht weiß, wann GetLastError() und wann nicht, wird immer ein nicht- nil Fehler zurückgegeben. Daher die typische Go-Fehlerbehandlungssprache

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

wird nicht funktionieren. Stattdessen müssen Sie prüfen r1 , genau wie Sie es in C, und verwenden Sie nur err wenn das die Funktion zeigt einen Fehler zurückgegeben:

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

Fehler mit zusätzlichen Informationen zu Fehler und Erfolg gemeldet

Einige API-Aufrufe können auf mehrere Arten erfolgreich sein oder fehlschlagen. Die APIs geben normalerweise zusätzliche Informationen für erfolgreiche Aufrufe sowie Fehler (z. B. CreateMutex ) zurück.

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.
    }
}

Fehler als HRESULT-Wert gemeldet

HRESULT -Werte sind numerische 32-Bit-Werte, bei denen Bits oder Bitbereiche genau definierte Informationen kodieren. Das MSB ist ein Fehler- / Erfolgskennzeichen, wobei die verbleibenden Bits zusätzliche Informationen speichern. Fehler oder Erfolg können mit den Makros FAILED oder SUCCEEDED ermittelt werden. HRESULT werden häufig mit COM verwendet, werden jedoch auch in Nicht-COM-Implementierungen angezeigt (z. B. 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).
        ...
    }
}

Konvertieren eines Fehlercodes in eine Nachrichtenzeichenfolge

GetLastError gibt einen numerischen Fehlercode zurück. Um eine beschreibende Fehlermeldung zu erhalten ( z. B. für einen Benutzer anzuzeigen), können Sie 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 ++ können Sie die Schnittstelle durch Verwendung der Klasse std::string erheblich vereinfachen:

#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.");
    }
}

HINWEIS: Diese Funktionen funktionieren auch für HRESULT Werte . Ändern Sie einfach den ersten Parameter von DWORD dwErrorCode in HRESULT hResult . Der Rest des Codes kann unverändert bleiben.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow