Tuesday, September 20, 2011

NTSTATUS

NTSTATUS was defined for code native to the Windows NT operating system. It was a part of the very clean separations built between the core OS and the various subsystems where user code was expected to run (POSIX, WIN32, WIN16/DOS, OS/2). Over time it has begun to leak into code in the WIN32 subsystem. One example of leakage is the BCrypt Api's.

Just including ntstatus.h and windows.h together produces errors:

main.cpp
#include <windows.h>
#include <winternl.h>
#include <ntstatus.h>

int main()
{
  return 0;
}

>cl main.cpp
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.30319.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

main.cpp
[redacted]ntstatus.h(147) 
    : warning C4005: 'STATUS_WAIT_0' 
        : macro redefinition
    [redacted]winnt.h(1983) 
        : see previous definition of 'STATUS_WAIT_0'
...

I spent some hours recently tracking down an incantation that would allow ntstatus.h to be included without the errors, the following was the result:

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

#include <winternl.h>
#include <ntstatus.h>

NTSTATUS defines 2bits to hold four severity levels, SUCCESS, INFORMATION, WARNING, ERROR.
There are 12bits set aside for Facilities, which are used to create feature-specific error codes. Within each facility is 16bits to define codes specific to that facility.
FACILITY_NULL contains generic codes like STATUS_SUCCESS.
FACILITY_NTWIN32 contains all the WINERROR codes.

Checking NTSTATUS values must be done via the NT_SUCCESS(ntstatus), NT_INFORMATION(ntstatus), NT_WARNING(ntstatus) or NT_ERROR(ntstatus) macros.

NTSTATUS Usage

NTSTATUS ApiReturningNtStatus(HANDLE handle)
{
  if (handle == INVALID_HANDLE_VALUE) {
    return STATUS_INVALID_PARAMETER;
  }
  return STATUS_SUCCESS;
}

NTSTATUS ntstatus = STATUS_SUCCESS;
HANDLE handle = INVALID_HANDLE_VALUE;
ntstatus = ApiReturningNtStatus(handle);
if (!NT_SUCCESS(ntstatus))
{
  printf("ApiReturningNtStatus returned %x", ntstatus);
  exit();
}

Conversions to NTSTATUS

HRESULT = S_OK;
NTSTATUS ntstatus = STATUS_SUCCESS;
if (hr & FACILITY_NT_BIT)
{
  ntstatus = hr & ~FACTILITY_NT_BIT;
} else if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
{
  ntstatus = NTSTATUS_FROM_WIN32(HRESULT_CODE(hr));
} else if (SUCCEEDED(hr))
{
  ntstatus = STATUS_SUCCESS;
} else
{
  ntstatus = STATUS_UNSUCCESSFUL;
}

DWORD winerror = ERROR_SUCCESS;
NTSTATUS ntstatus = STATUS_SUCCESS;
ntstatus = NTSTATUS_FROM_WIN32(winerror);

Conversions from NTSTATUS

NTSTATUS ntstatus = STATUS_SUCCESS;
HRESULT hr = HRESULT_FROM_NT(ntstatus);

NTSTATUS ntstatus = STATUS_SUCCESS;
DWORD winerror = RtlNtStatusToDosError(ntstatus);
if (winerror == ERROR_MR_MID_NOT_FOUND)
{
  // there is no conversion
  return ERROR_UNIDENTIFIED_ERROR;
#if 0
  // The following is common, but doesn't work out well
  // especially when combined with HRESULT_FROM_WIN32
  // and NTSTATUS_FROM_WIN32
  return ntstatus;
#endif
}

1 comment:

  1. I know this is an old post, but I am working on a WIN32 project that uses Bcrypt and I've stumbled upon this problem.

    Thanks for the useful information!

    ReplyDelete