Win32 API
Foutrapportage en afhandeling
Zoeken…
Opmerkingen
Elke thread heeft zijn eigen laatste foutcode. De Windows API stelt de laatste foutcode in op de oproepende thread.
GetLastError
de GetLastError
functie altijd onmiddellijk aan nadat u de GetLastError
van een Windows API-functie hebt gecontroleerd.
De meeste Windows API-functies stellen de laatste foutcode in wanneer ze mislukken. Sommigen zullen ook de laatste foutcode instellen wanneer ze slagen. Er zijn een aantal functies die niet de laatste foutcode instellen. Raadpleeg altijd de documentatie van de Windows API-functie.
Het is onveilig om FORMAT_MESSAGE_FROM_SYSTEM
zonder FORMAT_MESSAGE_IGNORE_INSERTS
wanneer u de functie FormatMessage
gebruikt om een beschrijving van een foutcode te krijgen.
Invoering
De Windows API wordt geleverd door middel van een C-opvraagbare interface. Succes of falen van een API-aanroep wordt strikt gerapporteerd via retourwaarden. Uitzonderingen maken geen deel uit van het gedocumenteerde contract (hoewel sommige API- implementaties SEH- uitzonderingen kunnen veroorzaken, bijvoorbeeld wanneer een alleen-lezen lpCommandLine- argument wordt doorgegeven aan CreateProcess ).
Foutrapportage valt grofweg in een van de vier categorieën:
- Retourwaarde alleen
- Retourwaarde met aanvullende informatie over mislukking
- Retourwaarde met aanvullende informatie over mislukking en succes
-
HRESULT
retourwaarde
In de documentatie voor elke API-aanroep wordt expliciet aangegeven hoe fouten worden gerapporteerd. Raadpleeg altijd de documentatie.
Fout alleen gerapporteerd door retourwaarde
Sommige API-aanroepen retourneren een enkele fout- / succesvlag , zonder aanvullende informatie (bijv. GetObject ):
if ( GetObjectW( obj, 0, NULL ) == 0 ) {
// Failure: no additional information available.
}
Fout gemeld met aanvullende informatie over mislukking
Naast een mislukte / succesvolle retourwaarde stellen sommige API-aanroepen ook de laatste fout bij mislukken in (bijv. CreateWindow ). De documentatie bevat meestal de volgende standaardformulering voor dit geval:
Als de functie slaagt, is de retourwaarde <API-specifieke succeswaarde> .
Als de functie mislukt, is de retourwaarde <API-specifieke foutwaarde> . Bel GetLastError om uitgebreide foutinformatie te krijgen.
if ( CreateWindowW( ... ) == NULL ) {
// Failure: get additional information.
DWORD dwError = GetLastError();
} else {
// Success: must not call GetLastError.
}
Het is van vitaal belang dat u GetLastError()
ONMIDDELLIJK GetLastError()
. De laatste foutcode kan worden overschreven door elke andere functie, dus als er een extra functieaanroep is tussen de functie die is mislukt en de aanroep van GetLastError()
, is de terugkeer van GetLastError()
niet langer betrouwbaar. Wees extra voorzichtig met C ++ constructors.
Zodra u een foutcode krijgt, moet u deze interpreteren. U kunt een uitgebreide lijst met foutcodes op MSDN krijgen op de pagina Systeemfoutcodes (Windows) . U kunt ook in de koptekstbestanden van uw systeem kijken; het bestand met alle foutcodeconstanten is winerror.h
. (Als u de officiële SDK van Microsoft voor Windows 8 of nieuwer hebt, bevindt deze zich in de shared
submap van de map include.)
Opmerkingen over het aanroepen van GetLastError()
in andere programmeertalen
.net-talen (C #, VB, enz.)
Met .net mag u niet rechtstreeks naar GetLastError()
/ GetLastError()
. Dit komt omdat de .net-runtime andere Windows API-aanroepen doet op dezelfde thread achter uw rug. De garbage collector kan bijvoorbeeld VirtualFree()
aanroepen als hij voldoende geheugen vindt dat hij niet meer gebruikt, en dit kan gebeuren tussen uw beoogde functieaanroep en uw aanroep naar GetLastError()
.
In plaats daarvan biedt .net de functie Marshal.GetLastWin32Error()
, waarmee de laatste fout wordt opgehaald uit de laatste P / Invoke-aanroep die u zelf hebt gedaan. Gebruik dit in plaats van GetLastError()
rechtstreeks aan te roepen.
(.net lijkt je toch niet te stoppen GetLastError()
importeren; ik weet niet zeker waarom.)
Gaan
De verschillende voorzieningen die door Go worden geboden voor het aanroepen van DLL-functies (die zich bevinden in zowel pakket syscall
als pakket golang.org/x/sys/windows
) retourneren drie waarden: r1
, r2
en err
. r2
wordt nooit gebruikt; u kunt daar de blanco ID gebruiken. r1
is de retourwaarde van de functie. err
is het resultaat van het aanroepen van GetLastError()
maar geconverteerd naar een type dat een error
implementeert, zodat u het kunt doorgeven aan aan te roepen functies.
Omdat Go niet weet wanneer GetLastError()
moet worden GetLastError()
en wanneer niet, retourneert het altijd een niet- nil
. Daarom is het typische Go-foutafhandelingsidioom
r1, _, err := syscall.Syscall12(CreateWindowW.Addr(), ...)
if err != nil {
// handle err
}
// use r1
zal niet werken. In plaats daarvan moet u r1
controleren, precies zoals u zou doen in C, en alleen err
als dat aangeeft dat de functie een fout heeft geretourneerd:
r1, _, err := syscall.Syscall12(CreateWindowW.Addr(), ...)
if r1 == 0 {
// handle err
}
// use r1
Fout gemeld met aanvullende informatie over mislukking en succes
Sommige API-aanroepen kunnen op meer dan één manier slagen of mislukken. De API's retourneren meestal aanvullende informatie voor zowel succesvolle aanroepen als fouten (bijvoorbeeld 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.
}
}
Fout gerapporteerd als HRESULT-waarde
HRESULT s zijn numerieke 32-bits waarden, waarbij bits of bitbereiken goed gedefinieerde informatie coderen. De MSB is een vlag voor mislukking / succes, waarbij de resterende bits extra informatie opslaan. Mislukking of succes kan worden vastgesteld met de FAILED of SUCCEEDED macro's. HRESULT
s worden vaak gebruikt met COM, maar verschijnen ook in niet-COM-implementaties (bijv. 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).
...
}
}
Een foutcode omzetten in een berichtreeks
GetLastError
retourneert een numerieke foutcode. Om een beschrijvend foutbericht te krijgen ( bijvoorbeeld om aan een gebruiker weer te geven), kunt u 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 ++ kunt u de interface aanzienlijk vereenvoudigen door de std::string
klasse te gebruiken:
#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.");
}
}
OPMERKING: deze functies werken ook voor HRESULT
waarden . Wijzig gewoon de eerste parameter van DWORD dwErrorCode
in HRESULT hResult
. De rest van de code kan ongewijzigd blijven.