How to convert specific NTSTATUS value to the Hresult?
Asked Answered
P

5

9

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;
    }
}
Paradiddle answered 29/8, 2014 at 10:17 Comment(0)
P
2

This works:

internal static class NativeMethods
{
    [DllImport("ntdll.dll")]
    public static extern int RtlNtStatusToDosError(int status);
}

internal static class Program
{
    //#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;

    private const int HresultWin32Prefix = unchecked((int)0x80070000);

    static void Main(string[] args)
    {
        int code = NativeMethods.RtlNtStatusToDosError(STATUS_DUPLICATE_OBJECTID);
        int hresult = code | HresultWin32Prefix;
        Debug.Assert(hresult == CorrectHrStatusDuplicateObjectid, "Must be the same");
    }
Paradiddle answered 29/8, 2014 at 12:20 Comment(0)
E
10

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);
}
Epigenous answered 17/7, 2015 at 18:52 Comment(0)
M
7

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).

Merrymaking answered 25/8, 2015 at 13:40 Comment(0)
S
6

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.

Snip answered 29/8, 2014 at 11:2 Comment(1)
@thank you, your answer helped, but we can use RtlNtStatusToDosError. The reason i need this, because i send statuses from file system minifilter to user mode lib as reply to the FilterSendMessage msdn.microsoft.com/en-us/library/windows/hardware/…, it returns hresult.Paradiddle
S
2

You can use RtlNtStatusToDosError from ntdll to convert the NTSTATUS value to a Win32 error, and then convert that one to HRESULT.

#define FACILITY_WIN32 0x0007

#define HRESULT_FROM_WIN32(x) ((HRESULT)(x) <= 0 ? ((HRESULT)(x)) :\
 ((HRESULT) (((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | 0x80000000)))

HRESULT NtStatusToHresult(NTSTATUS Status)
{
    return HRESULT_FROM_WIN32(RtlNtStatusToDosError(Status));
}

P.S. note that RtlNtStatusToDosError returns ERROR_MR_MID_NOT_FOUND if it cannot map the value.

Selfdelusion answered 29/8, 2014 at 11:57 Comment(2)
@Yes Igor.. thank you, that is correct answer, i found the same solution in c#, litle simpler because we know that facility is win32 we can simplify.Paradiddle
Your answer is fine, but just FYI RtlNtStatusToDosError does a lousy job converting NTSTATUS to "dos errors". It got a couple that I spot checked correct, but most of them it returns some random-looking 0x8... value that isn't even in the range of documented win32 error codes when there is an obvious exact match it should have made.Houdini
P
2

This works:

internal static class NativeMethods
{
    [DllImport("ntdll.dll")]
    public static extern int RtlNtStatusToDosError(int status);
}

internal static class Program
{
    //#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;

    private const int HresultWin32Prefix = unchecked((int)0x80070000);

    static void Main(string[] args)
    {
        int code = NativeMethods.RtlNtStatusToDosError(STATUS_DUPLICATE_OBJECTID);
        int hresult = code | HresultWin32Prefix;
        Debug.Assert(hresult == CorrectHrStatusDuplicateObjectid, "Must be the same");
    }
Paradiddle answered 29/8, 2014 at 12:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.