Reputation: 4117
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;
}
}
Upvotes: 9
Views: 8917
Reputation: 17438
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)
.
Upvotes: 7
Reputation: 256891
NTSTATUS
you have0xC000022A
: STATUS_DUPLICATE_OBJECTID
- The attempt to insert the ID in the index failed because the ID is already in the index.)
HRESULT
you want0x80071392
:
FACILITY_WIN32
- This region is reserved to map undecorated error codes into HRESULTs.)ERROR_OBJECT_ALREADY_EXISTS
- The object already exists.)The problem is that there are multiple representations of the same error:
0xC000022A
5010
0xD000022A
(converting the NSTATUS to an HRESULT)0x80071392
(converting the Win32 error to an HRESULT)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.
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); }
Upvotes: 10
Reputation: 942000
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.
Upvotes: 6
Reputation: 4117
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");
}
Upvotes: 2
Reputation: 25308
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.
Upvotes: 2