如何将特定的NTSTATUS值转换为Hresult?

Bra*_* Ds 3 c# winapi

我知道NTSTATUS,我将在特定错误的情况下获得,但我得到了hresult,而不是来自pinvoke的ntstatus.那么如何将特定的NTSTATUS值转换为Hresult.

我试过没有成功:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

Ian*_*oyd 6

NTSTATUS你有

  • 0xC000022A: STATUS_DUPLICATE_OBJECTID- 尝试在索引中插入ID失败,因为ID已在索引中.)

    • 严重性:1(1 =失败)
    • 保留:1(1 = NTSTATUS)
    • 客户:0(0 = Microsoft定义)
    • N:0(0 =不是NTSTATUS HRESULT)
    • 保留:0
    • 设施:0
    • 代码:554

HRESULT你想

  • 0x80071392:

    • 严重性:1(1 =失败)
    • R保留:0
    • 客户:0(0 = Microsoft定义)
    • N:0(0 =不是NTSTATUS HRESULT)
    • X保留:0
    • 设施:7(FACILITY_WIN32- 保留此区域以将未修改的错误代码映射到HRESULT.)
    • 代码:5010(ERROR_OBJECT_ALREADY_EXISTS- 对象已经存在.)

表示相同错误的多种方式

问题是同一错误有多种表示形式:

  • NTSTATUS:0xC000022A
  • Win32:5010
  • HRESULT :( 0xD000022A 将NSTATUS转换为HRESULT)
  • HRESULT :( 0x80071392 将Win32错误转换为HRESULT)

在此输入图像描述

并非所有NTSTATUS代码都可以转换为Win32.在那种情况下,试图通过RtlNtstatusToDosError将给你错误代码ERROR_MR_MID_NOT_FOUND:

系统无法在%2的消息文件中找到消息号0x%1的消息文本.

这就是为什么最好保留真正的错误信息.

获取错误消息

我假设遇到的真正问题是如何将NTSTATUS一个错误消息转换为可以显示给用户的错误消息.为此您需要Microsoft知识库文章:

KB259693 - 如何将NTSTATUS错误代码转换为消息字符串

大多数内核模式API函数返回NTSTATUS值.要使用FormatMessage API函数将这些状态值转换为消息,必须在参数列表中引用NtDLL.dll模块.

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);
}
Run Code Online (Sandbox Code Playgroud)


Ian*_*ott 6

RtlNtStatusToDosError(Status)作为将错误代码转换NTSTATUS为 Win32 错误代码的替代方法,我滥用了GetOverlappedResult()(来自 kernel32.dll),如下所示:

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;
}
Run Code Online (Sandbox Code Playgroud)

然后可以将 Win32 错误代码转换为HRESULT使用HRESULT_FROM_WIN32(error).


Han*_*ant 5

本机操作系统错误代码到 winapi 层错误代码的映射并非易事。5010 和 0xc000022a 之间没有任何对应关系。使用的心理图像是隐藏在 ntdll.dll 中的一个巨大的 switch 语句,它可以从一个转换到另一个。 微软不情愿地公开了它,你通常必须经历重重困难才能使用它。实际上,pinvoke 代码更容易,因为它已经使用 GetProcAddress() 来查找导出的函数。

但是,只要您进行 winapi 调用,您就应该只期望得到 winapi 错误代码,而不要尝试自己翻译它。它可以包含在 HRESULT 中,即 0x80070000 + 错误。本机操作系统错误代码有时确实渗透,特别是对于 SEH 异常代码,但总是很容易识别。

如果您提到了您尝试使用的 winapi 函数,这个问题会更容易准确回答。


Bra*_* Ds 2

这有效:

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");
    }
Run Code Online (Sandbox Code Playgroud)