error codes in Windows
Differences between and best practices for WINERROR
, HRESULT
, RPC_STATUS
, SECURITY_STATUS
and NTSTATUS
?
Why would a developer care?
These error types and their values are an often ignored part of the API contract. Using the return value incorrectly has the same type of consequences as using a cast to pass the wrong data type to a parameter of an API, code just doesn't work, and often fails first in the wild.
I recently began running into code like this:
// ERROR_SUCCESS is a WINERROR value
NTSTATUS status = ERROR_SUCCESS;
WCHAR* buffy;
buffy = (WCHAR*)malloc(1);
if (buffy == NULL)
{
// this is an HRESULT value
status = E_OUTOFMEMORY;
}
// not only is this a WINERROR value, but HRESULT and
// NTSTATUS have many success values, not just one
if (status != ERROR_SUCCESS)
{
return status;
}
I went searching for information about what each type is and the relationships between them and how to use them correctly. Here is what I learned.
They are all 32bit values, some signed some unsigned. Some are defined in terms of bit fields and require macros or inline-functions to check their current state. Others are straight enumerations.
One of the complexities is best demonstrated with the HRESULT
type. Lets look at the out of memory condition. Here are some of the possible encodings:
Expression | HRESULT value |
---|---|
E_OUTOFMEMORY |
0x8007000E |
HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY) |
0x8007000E |
HRESULT_FROM_NT(STATUS_NO_MEMORY) |
0xD0000017 |
HRESULT_FROM_NT( |
0xD007000E |
STG_E_INSUFFICIENTMEMORY |
0x80030008 |
NTE_NO_MEMORY |
0x8009000E |
There was some effort to make the values agree across the different expressions.
Many Api's will pick a set of return codes and then build internal maps from all the errors they receive from other Api's to the set they have decided to return. Thus most code calling Api's can expect that only one encoding will be returned.
Primary Windows Error Types
There will be subsequent posts that expand on each of these types. Here is a summary:WINERROR
Include | winerror.h |
---|---|
Type | DWORD |
Structure | values in the range 0-16384 |
Success | value == ERROR_SUCCESS |
Failure | value != ERROR_SUCCESS |
Conversions | HRESULT_FROM_WIN32(value) NTSTATUS_FROM_WIN32(value) |
HRESULT
Include | winerror.h |
---|---|
Type | HRESULT |
Structure | [1bit Severity] [1bit Reserved] [1bit Customer] [1bit FACILITY_NT_BIT] [12bit Facility] [16bit Code] |
Success | SUCCEEDED(value) |
Failure | FAILED(value) |
Conversions | HRESULT is a superset of all the other error types. They all have lossless conversions to HRESULT but it doesn't have lossless conversions to anything else |
NTSTATUS
Include |
|
---|---|
Type | NTSTATUS |
Structure | [2bit Severity] [1bit Customer] [1bit Reserved] [12bit Facility] [16bit Code] |
Success | NT_SUCCESS(value) |
Failure | !NT_SUCCESS(value) |
Other Tests |
NT_INFORMATIONAL(value) NT_WARNING(value) NT_ERROR(value) |
Conversions | HRESULT_FROM_NT(value) |
Derived/Subset Windows Error Types
RPC_STATUS
Include | rpcnterr.h |
---|---|
Type | RPC_STATUS |
Structure | A subset of the WINERROR values, redefined as RPC_S_... |
Success | value == RPC_S_OK |
Failure | value != RPC_S_OK |
Conversions | HRESULT_FROM_WIN32(value) NTSTATUS_FROM_WIN32(value) |
SECURITY_STATUS
Include | winerror.h |
---|---|
Type | SECURITY_STATUS or HRESULT |
Structure | A subset of the HRESULT values (from FACILITY_NULL and FACILITY_SECURITY) |
Success | SUCCEEDED(value) |
Failure | FAILED(value) |
Conversions |