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

4

I'm converting my Win32 app to UWP and am now writing Windows Store integration code using Windows.Services.Store namespace. For Win32 C++ it is mostly implemented via COM interface methods that seem to return their failures via HRESULT error codes.

So I thought that it would be nice to convert those HRESULT codes into descriptions that can be displayed for end-users.

I tried the following method:

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);
}

But unfortunately it doesn't always work. For instance, FormatMessage returns FALSE and error code ERROR_MR_MID_NOT_FOUND for the following HRESULT values that I got experimentally from running my Windows Store integration code:

  • 0x80072EFF
  • 0x803F6107

Do you have any idea how to get descriptions for most HRESULT codes that are now used in Windows 10?

EDIT: Following what was suggested by Sunius below I came up with the following code to retrieve descriptions for WinRT error codes:

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);
c++
winapi
windows-runtime
uwp
windows-10
asked on Stack Overflow Nov 13, 2016 by c00000fd • edited May 23, 2017 by Community

2 Answers

3

HRESULTs on windows runtime APIs work similar to 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() on it to get the error string.

Be careful when using this - you must not call any Windows Runtime APIs between receiving failed HRESULT and retrieving the IRestrictedErrorInfo interface, as it can be overwritten at any time (the same limitation exists with GetLastError(). There are two functions to set restricted error info: RoOriginateLanguageException() and RoOriginateError().

I don't recommend using FormatMessage() unless you're using it as fallback when there's no IRestrictedErrorInfo available: in most cases, you will not get a reasonable message from it.

answered on Stack Overflow Nov 14, 2016 by Sunius
2

MSDN describes the general format of HRESULT:

Structure of COM Error Codes

And how to break up an HRESULT into its constituent component values:

Using Macros for Error Handling

Under the described format:

  • 0x80072EFF is a failure code with a facility of FACILITY_WIN32 (7) and an scode of 12031. FACILITY_WIN32 represents a Win32 error code wrapped in an HRESULT. Such values are created using the HRESULT_FROM_WIN32() macro. Win32 error codes in the 12000-12175 range are reserved for Internet Error Codes. In this case, Win32 error code 12031 is ERROR_INTERNET_CONNECTION_RESET ("The connection with the server has been reset").

  • 0x803F6107 is a failure code with a facility of FACILITY_WINDOWS_STORE (63) and an scode of 24839, which does not seem to be publicly defined, but may be related application licencing issues.

When using FormatMessage() to retrieve the text description of an HRESULT, you usually need to enable the FORMAT_MESSAGE_FROM_HMODULE flag and set the lpSource parameter to an HMODULE of a library that defines the error code. However, in the cast of FACILITY_WIN32, most Win32 error codes can be retrieved using the FORMAT_MESSAGE_FROM_SYSTEM flag.

Other error codes tend to be library-defined instead, so you have to track down which library belongs to which facility, and then load that library so you can pass it to FormatMessage().

In the case of Internet errors, they are defined in WinInet.dll, as described in WinInet's Handling Errors documentation:

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's documentation about System Error Codes (12000-15999) seems to contradict this (unless FormatMessage() handles this internally for you, which would not follow the WinInet documentation):

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.

In the case of FACILITY_ITF (4) errors, the HRESULT is interface-defined, so you can't use FormatMessage() at all. But you might be able to use GetErrorInfo() instead, particularly if the implementing object supports the ISupportErrorInfo interface.

answered on Stack Overflow Nov 14, 2016 by Remy Lebeau • edited Nov 14, 2016 by Remy Lebeau

User contributions licensed under CC BY-SA 3.0