Win32 API
Fel rapportering och hantering
Sök…
Anmärkningar
Varje tråd har sin sista felkod. Windows API kommer att ställa in den sista felkoden på den ringande tråden.
Du bör alltid ringa GetLastError
funktionen omedelbart efter att ha kontrollerat en återgångsvärde för en Windows API-funktion.
Majoriteten av Windows API-funktioner ställer in den sista felkoden när de misslyckas. Vissa ställer också in den sista felkoden när de lyckas. Det finns ett antal funktioner som inte anger den sista felkoden. Se alltid dokumentationen till Windows API-funktionen.
Det är osäkert att använda FORMAT_MESSAGE_FROM_SYSTEM
utan FORMAT_MESSAGE_IGNORE_INSERTS
när du använder funktionen FormatMessage
att få en beskrivning av en felkod.
Introduktion
Windows API tillhandahålls med hjälp av ett C-kallbart gränssnitt. Framgång eller misslyckande med ett API-samtal rapporteras strikt genom returvärden. Undantag ingår inte i det dokumenterade kontraktet (även om vissa API- implementationer kan höja SEH- undantag, t.ex. när man skickar ett skrivskyddat lpCommandLine- argument till CreateProcess ).
Felrapportering faller ungefär i en av fyra kategorier:
- Endast returvärde
- Returvärde med ytterligare information om fel
- Returnera värde med ytterligare information om misslyckande och framgång
-
HRESULT
returvärde
Dokumentationen för varje API-samtal visar uttryckligen hur fel rapporteras. Se alltid dokumentationen.
Fel rapporterade endast med returvärde
Vissa API-samtal returnerar en enda misslyckande / framgångsflagg, utan ytterligare information (t.ex. GetObject ):
if ( GetObjectW( obj, 0, NULL ) == 0 ) {
// Failure: no additional information available.
}
Fel rapporterat med ytterligare information om fel
Förutom ett fel / återgångsvärde ställer vissa API-samtal också det sista felet vid fel (t.ex. CreateWindow ). Dokumentationen innehåller vanligtvis följande standardformulering för detta fall:
Om funktionen lyckas är returvärdet <API-specifikt framgångsvärde> .
Om funktionen misslyckas är returvärdet <API-specifikt felvärde> . För att få utökad felinformation , ring GetLastError .
if ( CreateWindowW( ... ) == NULL ) {
// Failure: get additional information.
DWORD dwError = GetLastError();
} else {
// Success: must not call GetLastError.
}
Det är viktigt att du GetLastError()
. Den sista felkoden kan skrivas över av någon annan funktion, så om det finns ett extrafunktionssamtal mellan funktionen som misslyckades och samtalet till GetLastError()
kommer returen från GetLastError()
inte längre att vara tillförlitlig. Var extra försiktig när du arbetar med C ++ konstruktörer.
När du har fått en felkod måste du tolka den. Du kan få en omfattande lista över felkoder på MSDN på sidan Systemfelkoder (Windows) . Alternativt kan du titta i dina systemhuvudfiler; filen med alla winerror.h
är winerror.h
. (Om du har Microsofts officiella SDK för Windows 8 eller nyare finns det i den shared
undermappen i inkluderingsmappen.)
Anteckningar om att ringa GetLastError()
på andra programmeringsspråk
.net-språk (C #, VB, etc.)
Med .net bör du inte P / Invoke till GetLastError()
direkt. Detta beror på att .net-runtime kommer att ringa andra Windows API-samtal på samma tråd bakom ryggen. Till exempel kan VirtualFree()
ringa VirtualFree()
om den hittar tillräckligt med minne som den inte längre använder, och det kan hända mellan ditt avsedda funktionssamtal och ditt samtal till GetLastError()
.
I stället tillhandahåller Marshal.GetLastWin32Error()
funktionen Marshal.GetLastWin32Error()
, som kommer att hämta det sista felet från det senaste P / Invoke-samtalet som du själv gjorde. Använd detta istället för att ringa GetLastError()
direkt.
(.net verkar inte hindra dig från att importera GetLastError()
ändå; jag är inte säker på varför.)
Gå
De olika faciliteterna som Go tillhandahåller för att ringa DLL-funktioner (som finns i både paketets syscall
och paketet golang.org/x/sys/windows
) returnerar tre värden: r1
, r2
och err
. r2
används aldrig; Du kan använda den tomma identifieraren där. r1
är funktionens returvärde. err
är resultatet av att man ringer GetLastError()
men konverteras till en typ som implementerar error
, så att du kan överföra det till samtalsfunktioner att hantera.
Eftersom Go inte vet när man ska ringa GetLastError()
och när inte, kommer det alltid att returnera ett icke- nil
fel. Därför är det typiska Go-felhanteringspråket
r1, _, err := syscall.Syscall12(CreateWindowW.Addr(), ...)
if err != nil {
// handle err
}
// use r1
kommer inte att fungera. Istället måste du kontrollera r1
, precis som i C, och bara använda err
om det indikerar att funktionen returnerade ett fel:
r1, _, err := syscall.Syscall12(CreateWindowW.Addr(), ...)
if r1 == 0 {
// handle err
}
// use r1
Fel rapporterade med ytterligare information om misslyckande och framgång
Vissa API-samtal kan lyckas eller misslyckas på mer än ett sätt. API: erna returnerar vanligtvis ytterligare information för både framgångsrika invokationer såväl som för fel (t.ex. 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.
}
}
Fel rapporterat som HRESULT-värde
HRESULTs är numeriska 32-bitarsvärden, där bitar eller bitintervall kodar för väl definierad information. MSB är en misslyckad / framgångsflagg, med de återstående bitarna lagrar ytterligare information. Misslyckande eller framgång kan bestämmas med hjälp av FAILED- eller SUCCEEDED- makron. HRESULT
används vanligtvis med COM, men visas också i icke-COM-implementeringar (t.ex. 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).
...
}
}
Konvertera en felkod till en meddelandesträng
GetLastError
returnerar en numerisk felkod. För att få ett beskrivande felmeddelande ( t.ex. att visa till en användare) kan du ringa 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);
}
I C ++ kan du förenkla gränssnittet avsevärt genom att använda klassen 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.");
}
}
OBS: Dessa funktioner fungerar också för HRESULT
värden . DWORD dwErrorCode
bara den första parametern från DWORD dwErrorCode
till HRESULT hResult
. Resten av koden kan förbli oförändrad.