diff --git a/src/AppInstallerCLITests/Errors.cpp b/src/AppInstallerCLITests/Errors.cpp index 57a7494451..162cca769d 100644 --- a/src/AppInstallerCLITests/Errors.cpp +++ b/src/AppInstallerCLITests/Errors.cpp @@ -3,6 +3,7 @@ #include "pch.h" #include "TestCommon.h" #include +#include using namespace AppInstaller; using namespace AppInstaller::Utility; @@ -17,3 +18,22 @@ TEST_CASE("EnsureSortedErrorList", "[errors]") REQUIRE(errors[i]->Value() > errors[i - 1]->Value()); } } + +TEST_CASE("WinInetHResultMessageUsesWinInetMessage", "[errors]") +{ + constexpr HRESULT internetCannotConnect = HRESULT_FROM_WIN32(ERROR_INTERNET_CANNOT_CONNECT); + const std::string message = GetUserPresentableMessage(internetCannotConnect); + const std::string fallbackSystemMessage = std::system_category().message(internetCannotConnect); + + INFO(message); + INFO(fallbackSystemMessage); + REQUIRE(message.find("0x80072efd") != std::string::npos); + REQUIRE(message.find(fallbackSystemMessage) == std::string::npos); + + auto hresultInfo = Errors::HResultInformation::Find(internetCannotConnect); + REQUIRE(hresultInfo); + + const auto description = hresultInfo->GetDescription(); + INFO(description); + REQUIRE(description.get().find(fallbackSystemMessage) == std::string::npos); +} diff --git a/src/AppInstallerSharedLib/Errors.cpp b/src/AppInstallerSharedLib/Errors.cpp index f91e2fc621..2ac42a8de2 100644 --- a/src/AppInstallerSharedLib/Errors.cpp +++ b/src/AppInstallerSharedLib/Errors.cpp @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" +#include #include "Public/AppInstallerErrors.h" #include "Public/AppInstallerLogging.h" #include "Public/AppInstallerStrings.h" @@ -343,6 +344,55 @@ namespace AppInstaller UnknownHResultInformation(hr).GetDescription(); } + int GetSystemErrorCode(HRESULT hr) + { + return static_cast(HRESULT_FACILITY(hr) == FACILITY_WIN32 ? HRESULT_CODE(hr) : hr); + } + + // WinINet errors are not present in the system message table. + std::optional GetWinInetErrorMessage(int errorCode) + { + if (errorCode < ERROR_INTERNET_OUT_OF_HANDLES || errorCode > INTERNET_ERROR_LAST) + { + return std::nullopt; + } + + wil::unique_hmodule module{ LoadLibraryExW(L"wininet.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32) }; + if (!module) + { + return std::nullopt; + } + + LPWSTR buffer = nullptr; + const DWORD messageLength = FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, + module.get(), errorCode, 0, reinterpret_cast(&buffer), 0, nullptr); + if (!messageLength) + { + return std::nullopt; + } + + auto freeBuffer = wil::scope_exit([&]() { LocalFree(buffer); }); + std::string message = Utility::ConvertToUTF8(std::wstring_view{ buffer, messageLength }); + Utility::Trim(message); + return message.empty() ? std::nullopt : std::optional{ std::move(message) }; + } + + std::string GetSystemErrorMessage(HRESULT hr) + { + const int errorCode = GetSystemErrorCode(hr); + if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + { + auto winInetMessage = GetWinInetErrorMessage(errorCode); + if (winInetMessage) + { + return std::move(winInetMessage).value(); + } + } + + return std::system_category().message(errorCode); + } + void GetUserPresentableMessageForHR(std::ostringstream& strstr, HRESULT hr) { strstr << "0x" << Logging::SetHRFormat << hr << " : "; @@ -361,7 +411,7 @@ namespace AppInstaller } else { - strstr << std::system_category().message(hr); + strstr << GetSystemErrorMessage(hr); } } } @@ -437,7 +487,7 @@ namespace AppInstaller } else { - return Utility::LocIndString{ std::system_category().message(m_value) }; + return Utility::LocIndString{ GetSystemErrorMessage(m_value) }; } }