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(
NTSTATUS_FROM_WIN32(ERROR_OUTOFMEMORY))
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

#define WIN32_NO_STATUS
#include <windows.h>
#undef WIN32_NO_STATUS

#include <winternl.h>
#include <ntstatus.h>
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