From 20e48c7922c4e145cc7a502cd42b8911179b5b19 Mon Sep 17 00:00:00 2001 From: starSumi Date: Tue, 23 Jun 2026 11:19:31 +0800 Subject: [PATCH 1/2] fix: decode Win32 HRESULT messages Decode HRESULT_FROM_WIN32 values before asking std::system_category for user-facing text so WinINet errors do not render as unknown error. Adds a regression test for ERROR_INTERNET_CANNOT_CONNECT. --- src/AppInstallerCLITests/Errors.cpp | 16 ++++++++++++++++ src/AppInstallerSharedLib/Errors.cpp | 5 +++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/AppInstallerCLITests/Errors.cpp b/src/AppInstallerCLITests/Errors.cpp index 57a7494451..1654f8d38d 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,18 @@ TEST_CASE("EnsureSortedErrorList", "[errors]") REQUIRE(errors[i]->Value() > errors[i - 1]->Value()); } } + +TEST_CASE("Win32HResultMessageUsesWin32Code", "[errors]") +{ + constexpr HRESULT internetCannotConnect = HRESULT_FROM_WIN32(ERROR_INTERNET_CANNOT_CONNECT); + const std::string message = GetUserPresentableMessage(internetCannotConnect); + const std::string expectedSystemMessage = std::system_category().message(ERROR_INTERNET_CANNOT_CONNECT); + const std::string hresultSystemMessage = std::system_category().message(internetCannotConnect); + + INFO(message); + INFO(expectedSystemMessage); + INFO(hresultSystemMessage); + REQUIRE(message.find("0x80072efd") != std::string::npos); + REQUIRE(message.find(expectedSystemMessage) != std::string::npos); + REQUIRE(message.find(hresultSystemMessage) == std::string::npos); +} \ No newline at end of file diff --git a/src/AppInstallerSharedLib/Errors.cpp b/src/AppInstallerSharedLib/Errors.cpp index f91e2fc621..e0407f9027 100644 --- a/src/AppInstallerSharedLib/Errors.cpp +++ b/src/AppInstallerSharedLib/Errors.cpp @@ -361,7 +361,8 @@ namespace AppInstaller } else { - strstr << std::system_category().message(hr); + strstr << std::system_category().message( + HRESULT_FACILITY(hr) == FACILITY_WIN32 ? HRESULT_CODE(hr) : hr); } } } @@ -498,4 +499,4 @@ namespace AppInstaller return result; } } -} +} \ No newline at end of file From eefe8e7adfc6b9b6a082b6d78b83ac6fb755c7ac Mon Sep 17 00:00:00 2001 From: star Date: Wed, 22 Jul 2026 18:07:55 +0800 Subject: [PATCH 2/2] fix: format WinINet HRESULT messages --- src/AppInstallerCLITests/Errors.cpp | 20 ++++++---- src/AppInstallerSharedLib/Errors.cpp | 57 ++++++++++++++++++++++++++-- 2 files changed, 65 insertions(+), 12 deletions(-) diff --git a/src/AppInstallerCLITests/Errors.cpp b/src/AppInstallerCLITests/Errors.cpp index 1654f8d38d..162cca769d 100644 --- a/src/AppInstallerCLITests/Errors.cpp +++ b/src/AppInstallerCLITests/Errors.cpp @@ -19,17 +19,21 @@ TEST_CASE("EnsureSortedErrorList", "[errors]") } } -TEST_CASE("Win32HResultMessageUsesWin32Code", "[errors]") +TEST_CASE("WinInetHResultMessageUsesWinInetMessage", "[errors]") { constexpr HRESULT internetCannotConnect = HRESULT_FROM_WIN32(ERROR_INTERNET_CANNOT_CONNECT); const std::string message = GetUserPresentableMessage(internetCannotConnect); - const std::string expectedSystemMessage = std::system_category().message(ERROR_INTERNET_CANNOT_CONNECT); - const std::string hresultSystemMessage = std::system_category().message(internetCannotConnect); + const std::string fallbackSystemMessage = std::system_category().message(internetCannotConnect); INFO(message); - INFO(expectedSystemMessage); - INFO(hresultSystemMessage); + INFO(fallbackSystemMessage); REQUIRE(message.find("0x80072efd") != std::string::npos); - REQUIRE(message.find(expectedSystemMessage) != std::string::npos); - REQUIRE(message.find(hresultSystemMessage) == std::string::npos); -} \ No newline at end of file + 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 e0407f9027..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,8 +411,7 @@ namespace AppInstaller } else { - strstr << std::system_category().message( - HRESULT_FACILITY(hr) == FACILITY_WIN32 ? HRESULT_CODE(hr) : hr); + strstr << GetSystemErrorMessage(hr); } } } @@ -438,7 +487,7 @@ namespace AppInstaller } else { - return Utility::LocIndString{ std::system_category().message(m_value) }; + return Utility::LocIndString{ GetSystemErrorMessage(m_value) }; } } @@ -499,4 +548,4 @@ namespace AppInstaller return result; } } -} \ No newline at end of file +}