如何获取 WinRT / Windows 10 存储代码的 HRESULT 错误代码的说明?

How to get descriptions for HRESULT error codes for WinRT / Windows 10 Store code?

我正在将我的 Win32 应用程序转换为 UWP,现在正在使用 Windows.Services.Store 命名空间编写 Windows 商店集成代码。对于 Win32 C++,它主要是通过 COM 接口方法实现的,这些方法似乎通过 HRESULT 错误代码 return 它们的失败。

所以我认为将这些 HRESULT 代码转换为可以显示给最终用户的描述会很好。

我尝试了以下方法:

int nOSError = (int)hresult;

LPVOID lpMsgBuf;
if(FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
    NULL,
    nOSError,
    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
    (LPTSTR) &lpMsgBuf, 0, NULL))
{
    //Success

    LocalFree(lpMsgBuf);
}

但不幸的是,它并不总是有效。例如,FormatMessage returns FALSE 和错误代码 ERROR_MR_MID_NOT_FOUND 以下 HRESULT 值是我从 运行 我的 [=44] 实验得到的=] 商店集成代码:

您是否知道如何获取现在在 Windows 10 中使用的大多数 HRESULT 代码的描述?

编辑: 按照下面 Sunius 的建议,我想出了以下代码来检索 WinRT 错误代码的描述:

HRESULT hr;

ComPtr<IRestrictedErrorInfo> pErrInfo;
if (SUCCEEDED(hr = ::GetRestrictedErrorInfo(&pErrInfo)) &&
    pErrInfo)
{
    HRESULT hrErr;
    CComBSTR strErrDesc, strErrRestr, strSid;
    if (SUCCEEDED(hr = pErrInfo->GetErrorDetails(&strErrDesc, &hrErr, &strErrRestr, &strSid)))
    {
        //Set empty message
        ::RoOriginateError(-1, NULL);

        //Get empty error message text
        ComPtr<IRestrictedErrorInfo> pEmptyErrInfo;
        if (SUCCEEDED(hr = ::GetRestrictedErrorInfo(&pEmptyErrInfo)) &&
            pEmptyErrInfo)
        {
            HRESULT hrDummy;
            CComBSTR strEmptyErrDesc, strDummy1, strDummy2;
            if (SUCCEEDED(hr = pEmptyErrInfo->GetErrorDetails(&strEmptyErrDesc, &hrDummy, &strDummy1, &strDummy2)))
            {
                //Remove "The text associated with this error code could not be found" messages
                if (strErrDesc.ByteLength() == strEmptyErrDesc.ByteLength() &&
                    memcmp(strErrDesc.operator LPWSTR(), strEmptyErrDesc.operator LPWSTR(), strErrDesc.ByteLength()) == 0)
                {
                    strErrDesc.Empty();
                }

                if (strErrRestr.ByteLength() == strEmptyErrDesc.ByteLength() &&
                    memcmp(strErrRestr.operator LPWSTR(), strEmptyErrDesc.operator LPWSTR(), strErrRestr.ByteLength()) == 0)
                {
                    strErrRestr.Empty();
                }
            }
        }


        LPCTSTR pS_ErrDesc = strErrDesc.operator LPWSTR();
        LPCTSTR pS_Restr = strErrRestr.operator LPWSTR();

        TCHAR buff[1024];
        if(SUCCEEDED(::StringCchPrintf(buff,
            1024,
            L"ERROR hr=0x%X\n"
            L"desc=\"%s\"\n"
            L"restr=\"%s\""
            ,
            hrErr,
            pS_ErrDesc,
            pS_Restr
            )))
        {
            //Get message in 'buff'

        }
    }
    else
        ASSERT(NULL);
}
else
    ASSERT(NULL);

MSDN描述了HRESULT的一般格式:

Structure of COM Error Codes

以及如何将 HRESULT 分解为其组成部分值:

Using Macros for Error Handling

在描述的格式下:

  • 0x80072EFF 是一个失败代码,其设施为 FACILITY_WIN32 (7),scode 为 12031。FACILITY_WIN32 表示包含在一个 Win32 错误代码中HRESULT。这些值是使用 HRESULT_FROM_WIN32() macro. Win32 error codes in the 12000-12175 range are reserved for Internet Error Codes 创建的。在这种情况下,Win32 错误代码 12031 是 ERROR_INTERNET_CONNECTION_RESET ("The connection with the server has been reset").

  • 0x803F6107是failure为FACILITY_WINDOWS_STORE(63),scode为24839的failure code,貌似没有公开定义,但可能与应用程序许可问题。

当使用 FormatMessage() 检索 HRESULT 的文本描述时,您 通常 需要启用 FORMAT_MESSAGE_FROM_HMODULE 标志并设置lpSource 参数到定义错误代码的库的 HMODULE。但是,在 FACILITY_WIN32 的转换中,大多数 Win32 错误代码都可以使用 FORMAT_MESSAGE_FROM_SYSTEM 标志检索。

其他错误代码往往是由库定义的,因此您必须追踪哪个库属于哪个设施,然后加载该库以便将其传递给 FormatMessage()

在 Internet 错误的情况下,它们在 WinInet.dll 中定义,如 WinInet 的 Handling Errors 文档所述:

To get the error text for an [Internet] error, call the FormatMessage function, passing it an HMODULE handle to Wininet.dll, which can be obtained using the GetModuleHandle function.

MSDN 关于 System Error Codes (12000-15999) 的文档似乎与此矛盾(除非 FormatMessage() 在内部为您处理,这不会遵循 WinInet 文档):

The following list describes system error codes (errors 12000 to 15999). They are returned by the GetLastError function when many functions fail. To retrieve the description text for the error in your application, use the FormatMessage function with the FORMAT_MESSAGE_FROM_SYSTEM flag.

FACILITY_ITF(4)错误的情况下,HRESULT是接口定义的,所以根本不能使用FormatMessage()。但是您也许可以使用 GetErrorInfo() instead, particularly if the implementing object supports the ISupportErrorInfo 界面。

windows 运行时 API 上的

HRESULT 的工作方式类似于 SetLastError()/GetLastError() mechanism, except that rather than storing the error code in the thread local storage, these APIs instead return the error code directly and store extended error information in thread local storage. Call GetRestrictedErrorInfo() to retrieve IRestrictedErrorInfo interface, and then call GetErrorDetails() 获取错误字符串。

使用它时要小心 - 您不得在接收失败的 HRESULT 和检索 IRestrictedErrorInfo 接口之间调用任何 Windows 运行时 API,因为它可以随时被覆盖(GetLastError() 存在相同的限制) . 设置受限错误信息的函数有两个:RoOriginateLanguageException() and RoOriginateError().

我不建议使用 FormatMessage(),除非您在没有可用的 IRestrictedErrorInfo 时将其用作后备:在大多数情况下,您不会从中获得合理的消息。