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);
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.
MSDN describes the general format of HRESULT
:
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.
User contributions licensed under CC BY-SA 3.0