Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert specific NTSTATUS value to the Hresult?

Tags:

c#

winapi

I am know NTSTATUS that i will get in case of specific error, but i got hresult, not ntstatus from pinvoke. So how to convert specific NTSTATUS value to the Hresult.

I tried with no success:

class Program
{
    private const int FacilityNtBit = 0x10000000;

    //#define STATUS_DUPLICATE_OBJECTID        ((NTSTATUS)0xC000022AL)
    private const int STATUS_DUPLICATE_OBJECTID = unchecked((int) (0xC000022A));

    // HResult that is returned for the STATUS_DUPLICATE_OBJECTID
    private const int CorrectHrStatusDuplicateObjectid = -2147019886;

    static void Main(string[] args)
    {
        int res = HRESULT_FROM_NT(STATUS_DUPLICATE_OBJECTID);
        Debug.Assert(res == CorrectHrStatusDuplicateObjectid, "Must be the same");
    }

    private static int HRESULT_FROM_NT(int ntStatus)
    {
        //#define HRESULT_FROM_NT(x)      ((HRESULT) ((x) | FACILITY_NT_BIT))
        return ntStatus | FacilityNtBit;
    }
}
like image 320
Brans Ds Avatar asked Aug 29 '14 10:08

Brans Ds


3 Answers

The NTSTATUS you have

  • 0xC000022A: STATUS_DUPLICATE_OBJECTID - The attempt to insert the ID in the index failed because the ID is already in the index.)

    • Severity: 1 (1=Failure)
    • Reserved: 1 (1=NTSTATUS)
    • Customer: 0 (0=Microsoft defined)
    • N: 0 (0=not an NTSTATUS HRESULT)
    • Reserved: 0
    • Facility: 0
    • Code: 554

The HRESULT you want

  • 0x80071392:

    • Severity: 1 (1=Failure)
    • R Reserved: 0
    • Customer: 0 (0=Microsoft defined)
    • N: 0 (0=not an NTSTATUS HRESULT)
    • X Reserved: 0
    • Facility: 7 (FACILITY_WIN32 - This region is reserved to map undecorated error codes into HRESULTs.)
    • Code: 5010 (ERROR_OBJECT_ALREADY_EXISTS - The object already exists.)

Multiple ways to represent the same error

The problem is that there are multiple representations of the same error:

  • NTSTATUS: 0xC000022A
  • Win32: 5010
  • HRESULT: 0xD000022A (converting the NSTATUS to an HRESULT)
  • HRESULT: 0x80071392 (converting the Win32 error to an HRESULT)

enter image description here

Not all NTSTATUS codes can be converted to Win32. Iin that case, trying to go through RtlNtstatusToDosError will give you the error code ERROR_MR_MID_NOT_FOUND:

The system cannot find message text for message number 0x%1 in the message file for %2.

This is why it's best to keep the real error message.

Getting the error message

The real issue i assume being experienced is how to convert an NTSTATUS to an error message that can be displayed to the user. For that you want Microsoft Knowledge Base article:

KB259693 - How to translate NTSTATUS error codes to message strings

Most Kernel Mode API functions return NTSTATUS values. To translate these status values to messages by using the FormatMessage API function, you must reference the NtDLL.dll module in the parameter list.

void DisplayError(DWORD NTStatusMessage)
{
   LPVOID lpMessageBuffer;
   HMODULE Hand = LoadLibrary("NTDLL.DLL");

   FormatMessage( 
       FORMAT_MESSAGE_ALLOCATE_BUFFER | 
       FORMAT_MESSAGE_FROM_SYSTEM | 
       FORMAT_MESSAGE_FROM_HMODULE,
       Hand, 
       Err,  
       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
       (LPTSTR) &lpMessageBuffer,  
       0,  
       NULL );

   // Now display the string.

   // Free the buffer allocated by the system.
   LocalFree( lpMessageBuffer ); 
   FreeLibrary(Hand);
}
like image 70
Ian Boyd Avatar answered Oct 01 '22 21:10

Ian Boyd


The mapping of native OS error codes to the winapi layer error codes is non-trivial. There's just no correspondence whatsoever between 5010 and 0xc000022a. The mental image to use is a giant switch statement hidden inside ntdll.dll that translates from one to the other. Reluctantly exposed by Microsoft, you'd normally have to jump through hoops to use it. Actually easier from pinvoke code since it already uses GetProcAddress() to find exported functions.

But as long as you are making a winapi call, you should only expect to get a winapi error code and make no attempt to translate it yourself. It can be wrapped in an HRESULT, simply 0x80070000 + error. The native OS error code does bleed through sometimes, particularly for SEH exception codes, but is always easy to recognize.

This question would have been easier to answer accurately if you had mentioned the winapi function you are trying to use btw.

like image 33
Hans Passant Avatar answered Oct 01 '22 21:10

Hans Passant


As an alternative to RtlNtStatusToDosError(Status) to convert an NTSTATUS to a Win32 error code, I've abused GetOverlappedResult() (from kernel32.dll) as follows:

DWORD
ConvertNtStatusToWin32Error(NTSTATUS ntstatus)
{
    DWORD oldError;
    DWORD result;
    DWORD br;
    OVERLAPPED o;
 
    o.Internal = ntstatus;
    o.InternalHigh = 0;
    o.Offset = 0;
    o.OffsetHigh = 0;
    o.hEvent = 0;
    oldError = GetLastError();
    GetOverlappedResult(NULL, &o, &br, FALSE);
    result = GetLastError();
    SetLastError(oldError);
    return result;
}

Then the Win32 error code can be converted to a HRESULT using HRESULT_FROM_WIN32(error).

like image 27
Ian Abbott Avatar answered Oct 01 '22 21:10

Ian Abbott