Thursday, September 8, 2011

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:

ExpressionHRESULT value
E_OUTOFMEMORY0x8007000E
HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY)0x8007000E
HRESULT_FROM_NT(STATUS_NO_MEMORY)0xD0000017
HRESULT_FROM_NT(
NTSTATUS_FROM_WIN32(ERROR_OUTOFMEMORY))
0xD007000E
STG_E_INSUFFICIENTMEMORY0x80030008
NTE_NO_MEMORY0x8009000E

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

Includewinerror.h
TypeDWORD
Structurevalues in the range 0-16384
Successvalue == ERROR_SUCCESS
Failurevalue != ERROR_SUCCESS
ConversionsHRESULT_FROM_WIN32(value)
NTSTATUS_FROM_WIN32(value)

HRESULT

Includewinerror.h
TypeHRESULT
Structure[1bit Severity]
[1bit Reserved]
[1bit Customer]
[1bit FACILITY_NT_BIT]
[12bit Facility]
[16bit Code]
SuccessSUCCEEDED(value)
FailureFAILED(value)
ConversionsHRESULT 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>
TypeNTSTATUS
Structure[2bit Severity]
[1bit Customer]
[1bit Reserved]
[12bit Facility]
[16bit Code]
SuccessNT_SUCCESS(value)
Failure!NT_SUCCESS(value)
Other
Tests
NT_INFORMATIONAL(value)
NT_WARNING(value)
NT_ERROR(value)
ConversionsHRESULT_FROM_NT(value)

Derived/Subset Windows Error Types

RPC_STATUS

Includerpcnterr.h
TypeRPC_STATUS
StructureA subset of the WINERROR values, redefined as RPC_S_...
Successvalue == RPC_S_OK
Failurevalue != RPC_S_OK
ConversionsHRESULT_FROM_WIN32(value)
NTSTATUS_FROM_WIN32(value)

SECURITY_STATUS

Includewinerror.h
TypeSECURITY_STATUS or HRESULT
StructureA subset of the HRESULT values (from FACILITY_NULL and FACILITY_SECURITY)
SuccessSUCCEEDED(value)
FailureFAILED(value)
Conversions

No comments:

Post a Comment