commit dd7867cc8b81c1ce7c388f7015b9cc557c9127ea parent 12f100c7920c0d971fbcdd8279f4162b132a18cb Author: JohnMcPMS <johnmcp@microsoft.com> Date: Mon, 17 May 2021 13:25:32 -0700 Fix a few polish items (#966) Fixes #723 and #728 , as well as an issue with what is displayed when multiple matches are found (add source) and anonymizes the error messages that can end up in telemetry. Specifically, sometimes file paths are included in an STL exception message so we are replacing instances of the `%USERPROFILE%` path with that literal value. Diffstat:
16 files changed, 253 insertions(+), 113 deletions(-)
diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt @@ -4,6 +4,7 @@ addmanifest addstore admins alloc +anonymize api appdata appinst @@ -488,6 +489,7 @@ uri url urlmon userguide +USERPROFILE usersources utext utf diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt @@ -1,6 +1,7 @@ abcd adml admx +affle AFX agg aicli diff --git a/src/AppInstallerCLICore/ExecutionReporter.cpp b/src/AppInstallerCLICore/ExecutionReporter.cpp @@ -9,8 +9,8 @@ namespace AppInstaller::CLI::Execution using namespace Settings; using namespace VirtualTerminal; - const Sequence& HelpCommandEmphasis = TextFormat::Foreground::BrightWhite; - const Sequence& HelpArgumentEmphasis = TextFormat::Foreground::BrightWhite; + const Sequence& HelpCommandEmphasis = TextFormat::Foreground::Bright; + const Sequence& HelpArgumentEmphasis = TextFormat::Foreground::Bright; const Sequence& NameEmphasis = TextFormat::Foreground::BrightCyan; const Sequence& IdEmphasis = TextFormat::Foreground::BrightCyan; const Sequence& UrlEmphasis = TextFormat::Foreground::BrightBlue; diff --git a/src/AppInstallerCLICore/VTSupport.cpp b/src/AppInstallerCLICore/VTSupport.cpp @@ -129,6 +129,9 @@ namespace AppInstaller::CLI::VirtualTerminal namespace Foreground { + const Sequence Bright{ AICLI_VT_TEXTFORMAT(1) }; + const Sequence NoBright{ AICLI_VT_TEXTFORMAT(22) }; + const Sequence BrightRed{ AICLI_VT_TEXTFORMAT(91) }; const Sequence BrightGreen{ AICLI_VT_TEXTFORMAT(92) }; const Sequence BrightYellow{ AICLI_VT_TEXTFORMAT(93) }; diff --git a/src/AppInstallerCLICore/VTSupport.h b/src/AppInstallerCLICore/VTSupport.h @@ -110,6 +110,11 @@ namespace AppInstaller::CLI::VirtualTerminal namespace Foreground { + // Applies brightness/intensity flag to foreground color + extern const Sequence Bright; + // Removes brightness/intensity flag from foreground color + extern const Sequence NoBright; + extern const Sequence BrightRed; extern const Sequence BrightGreen; extern const Sequence BrightYellow; diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp b/src/AppInstallerCLICore/Workflows/WorkflowBase.cpp @@ -363,7 +363,7 @@ namespace AppInstaller::CLI::Workflow { auto& searchResult = context.Get<Execution::Data::SearchResult>(); - Execution::TableOutput<5> table(context.Reporter, + Execution::TableOutput<2> table(context.Reporter, { Resource::String::SearchName, Resource::String::SearchId @@ -387,6 +387,47 @@ namespace AppInstaller::CLI::Workflow } } + void ReportMultiplePackageFoundResultWithSource(Execution::Context& context) + { + auto& searchResult = context.Get<Execution::Data::SearchResult>(); + + Execution::TableOutput<3> table(context.Reporter, + { + Resource::String::SearchName, + Resource::String::SearchId, + Resource::String::SearchSource + }); + + for (size_t i = 0; i < searchResult.Matches.size(); ++i) + { + auto package = searchResult.Matches[i].Package; + + std::string sourceName; + auto latest = package->GetLatestAvailableVersion(); + if (latest) + { + auto source = latest->GetSource(); + if (source) + { + sourceName = source->GetDetails().Name; + } + } + + table.OutputLine({ + package->GetProperty(PackageProperty::Name), + package->GetProperty(PackageProperty::Id), + std::move(sourceName) + }); + } + + table.Complete(); + + if (searchResult.Truncated) + { + context.Reporter.Info() << '<' << Resource::String::SearchTruncated << '>' << std::endl; + } + } + void ReportListResult::operator()(Execution::Context& context) const { auto& searchResult = context.Get<Execution::Data::SearchResult>(); @@ -481,14 +522,14 @@ namespace AppInstaller::CLI::Workflow if (m_isFromInstalledSource) { context.Reporter.Warn() << Resource::String::MultipleInstalledPackagesFound << std::endl; + context << ReportMultiplePackageFoundResult; } else { context.Reporter.Warn() << Resource::String::MultiplePackagesFound << std::endl; + context << ReportMultiplePackageFoundResultWithSource; } - context << ReportMultiplePackageFoundResult; - AICLI_TERMINATE_CONTEXT(APPINSTALLER_CLI_ERROR_MULTIPLE_APPLICATIONS_FOUND); } diff --git a/src/AppInstallerCLICore/Workflows/WorkflowBase.h b/src/AppInstallerCLICore/Workflows/WorkflowBase.h @@ -170,6 +170,12 @@ namespace AppInstaller::CLI::Workflow // Outputs: None void ReportMultiplePackageFoundResult(Execution::Context& context); + // Outputs the search results when multiple packages found but only one expected. + // Required Args: None + // Inputs: SearchResult + // Outputs: None + void ReportMultiplePackageFoundResultWithSource(Execution::Context& context); + // Ensures that there is at least one result in the search. // Required Args: bool indicating if the search result is from installed source // Inputs: SearchResult diff --git a/src/AppInstallerCLITests/Strings.cpp b/src/AppInstallerCLITests/Strings.cpp @@ -164,3 +164,12 @@ TEST_CASE("PathOutput", "[strings]") std::string output = out.str(); REQUIRE(output.substr(output.size() - original.size()) == original); } + +TEST_CASE("ReplaceWhileCopying", "[strings]") +{ + REQUIRE(ReplaceWhileCopying(L"A red apple", L"red", L"green") == L"A green apple"); + REQUIRE(ReplaceWhileCopying(L"A red, red apple", L"red", L"green") == L"A green, green apple"); + REQUIRE(ReplaceWhileCopying(L"A red, red apple", L"ed", L"ad") == L"A rad, rad apple"); + REQUIRE(ReplaceWhileCopying(L"A red apple", L"p", L"f") == L"A red affle"); + REQUIRE(ReplaceWhileCopying(L"A red apple", L"", L"green") == L"A red apple"); +} diff --git a/src/AppInstallerCommonCore/AppInstallerStrings.cpp b/src/AppInstallerCommonCore/AppInstallerStrings.cpp @@ -387,6 +387,39 @@ namespace AppInstaller::Utility return result; } + std::wstring ReplaceWhileCopying(std::wstring_view input, std::wstring_view token, std::wstring_view value) + { + if (token.empty()) + { + return std::wstring{ input }; + } + + std::wstring result; + result.reserve(input.size()); + + std::wstring::size_type pos = 0u; + do + { + std::wstring::size_type findPos = input.find(token, pos); + + if (findPos == std::wstring::npos) + { + result.append(input.substr(pos)); + } + else + { + result.append(input.substr(pos, findPos - pos)); + result.append(value); + findPos += token.length(); + } + + pos = findPos; + } + while (pos != std::wstring::npos); + + return result; + } + std::string& Trim(std::string& str) { if (!str.empty()) diff --git a/src/AppInstallerCommonCore/AppInstallerTelemetry.cpp b/src/AppInstallerCommonCore/AppInstallerTelemetry.cpp @@ -9,6 +9,7 @@ #include "winget/UserSettings.h" #define AICLI_TraceLoggingStringView(_sv_,_name_) TraceLoggingCountedUtf8String(_sv_.data(), static_cast<ULONG>(_sv_.size()), _name_) +#define AICLI_TraceLoggingWStringView(_sv_,_name_) TraceLoggingCountedWideString(_sv_.data(), static_cast<ULONG>(_sv_.size()), _name_) // Helper to print a GUID std::ostream& operator<<(std::ostream& out, const GUID& guid) @@ -39,6 +40,8 @@ namespace AppInstaller::Logging std::atomic_uint32_t s_subExecutionId{ s_RootExecutionId }; + constexpr std::wstring_view s_UserProfileReplacement = L"%USERPROFILE%"sv; + void __stdcall wilResultLoggingCallback(const wil::FailureInfo& info) noexcept { Telemetry().LogFailure(info); @@ -64,6 +67,7 @@ namespace AppInstaller::Logging RegisterTraceLogging(); m_isSettingEnabled = !Settings::User().Get<Settings::Setting::TelemetryDisable>(); + m_userProfile = Runtime::GetPathTo(Runtime::PathName::UserProfile).wstring(); } TelemetryTraceLogger::~TelemetryTraceLogger() @@ -97,7 +101,7 @@ namespace AppInstaller::Logging nullptr, TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), TraceLoggingHResult(failure.hr, "HResult"), - TraceLoggingWideString(failure.pszMessage, "Message"), + AICLI_TraceLoggingWStringView(AnonymizeString(failure.pszMessage), "Message"), TraceLoggingString(failure.pszModule, "Module"), TraceLoggingUInt32(failure.threadId, "ThreadId"), TraceLoggingUInt32(static_cast<uint32_t>(failure.type), "Type"), @@ -209,7 +213,7 @@ namespace AppInstaller::Logging TraceLoggingUInt32(s_subExecutionId, "SubExecutionId"), AICLI_TraceLoggingStringView(commandName, "Command"), AICLI_TraceLoggingStringView(type, "Type"), - AICLI_TraceLoggingStringView(message, "Message"), + AICLI_TraceLoggingWStringView(AnonymizeString(Utility::ConvertToUTF16(message)), "Message"), TraceLoggingUInt32(s_executionStage, "ExecutionStage"), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); @@ -511,6 +515,12 @@ namespace AppInstaller::Logging return g_IsTelemetryProviderEnabled && m_isSettingEnabled && m_isRuntimeEnabled; } + std::wstring TelemetryTraceLogger::AnonymizeString(std::wstring_view input) const noexcept try + { + return Utility::ReplaceWhileCopying(input, m_userProfile, s_UserProfileReplacement); + } + catch (...) { return std::wstring{ input }; } + #ifndef AICLI_DISABLE_TEST_HOOKS static std::shared_ptr<TelemetryTraceLogger> s_TelemetryTraceLogger_TestOverride; #endif diff --git a/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h b/src/AppInstallerCommonCore/Public/AppInstallerRuntime.h @@ -41,6 +41,8 @@ namespace AppInstaller::Runtime UserFileSettings, // The location where secure settings data is stored. SecureSettings, + // The value of %USERPROFILE%. + UserProfile, }; // Gets the path to the requested location. diff --git a/src/AppInstallerCommonCore/Public/AppInstallerStrings.h b/src/AppInstallerCommonCore/Public/AppInstallerStrings.h @@ -121,6 +121,9 @@ namespace AppInstaller::Utility // Returns a value indicating whether a replacement occurred. bool FindAndReplace(std::string& inputStr, std::string_view token, std::string_view value); + // Replaces the token in the input string with value while copying to a new result. + std::wstring ReplaceWhileCopying(std::wstring_view input, std::wstring_view token, std::wstring_view value); + // Removes whitespace from the beginning and end of the string. std::string& Trim(std::string& str); std::string Trim(std::string&& str); diff --git a/src/AppInstallerCommonCore/Public/AppInstallerTelemetry.h b/src/AppInstallerCommonCore/Public/AppInstallerTelemetry.h @@ -119,8 +119,15 @@ namespace AppInstaller::Logging bool IsTelemetryEnabled() const noexcept; + // Used to anonymize a string to the best of our ability. + // Should primarily be used on failure messages or paths if needed. + std::wstring AnonymizeString(std::wstring_view input) const noexcept; + bool m_isSettingEnabled = true; std::atomic_bool m_isRuntimeEnabled{ true }; + + // Data that is needed by AnonymizeString + std::wstring m_userProfile; }; // Helper to make the call sites look clean. diff --git a/src/AppInstallerCommonCore/Registry.cpp b/src/AppInstallerCommonCore/Registry.cpp @@ -12,7 +12,7 @@ namespace AppInstaller::Registry { std::wstring_view ConvertBytesToWideStringView(const std::vector<BYTE>& data) { - THROW_HR_IF(E_NOT_VALID_STATE, (data.size() % sizeof(wchar_t)) != 0); + // Remove any extra bytes because the data could just be dirty; better to not have a bad character than outright fail. std::wstring_view result{ reinterpret_cast<const wchar_t*>(data.data()), data.size() / sizeof(wchar_t) }; // Registry values may or may not be null terminated; we will remove any trailing nulls diff --git a/src/AppInstallerCommonCore/Runtime.cpp b/src/AppInstallerCommonCore/Runtime.cpp @@ -281,6 +281,10 @@ namespace AppInstaller::Runtime result /= GetPackageName(); create = false; break; + case PathName::UserProfile: + result = GetKnownFolderPath(FOLDERID_Profile); + create = false; + break; default: THROW_HR(E_UNEXPECTED); } @@ -319,6 +323,10 @@ namespace AppInstaller::Runtime result /= s_SecureSettings_Relative_Unpackaged; create = false; break; + case PathName::UserProfile: + result = GetKnownFolderPath(FOLDERID_Profile); + create = false; + break; default: THROW_HR(E_UNEXPECTED); } diff --git a/src/AppInstallerRepositoryCore/Microsoft/ARPHelper.cpp b/src/AppInstallerRepositoryCore/Microsoft/ARPHelper.cpp @@ -211,134 +211,144 @@ namespace AppInstaller::Repository::Microsoft for (const auto& arpEntry : key) { - std::string productCode = arpEntry.Name(); + std::string productCode; - Manifest::Manifest manifest; - manifest.DefaultLocalization.Add<Manifest::Localization::Tags>({ "ARP" }); + try + { + productCode = arpEntry.Name(); - // Use the key name as the Id, as it is supposed to be unique. - // TODO: We probably want something better here, like constructing the value as - // `Publisher.DisplayName`. We would need to ensure that there are no matches - // against the rest of the data however (might happen if same package is - // installed for multiple architectures/languages). - manifest.Id = productCode; + Manifest::Manifest manifest; + manifest.DefaultLocalization.Add<Manifest::Localization::Tags>({ "ARP" }); - manifest.Installers.emplace_back(); - // TODO: This likely needs some cleanup applied, as it looks like INNO tends to append an "_is#" - // that might vary across machines/installs. There may be other things we want to clean up as well, - // like trimming spaces at the ends, or removing the version string from the product code - // if it is present. - manifest.Installers[0].ProductCode = productCode; + // Use the key name as the Id, as it is supposed to be unique. + // TODO: We probably want something better here, like constructing the value as + // `Publisher.DisplayName`. We would need to ensure that there are no matches + // against the rest of the data however (might happen if same package is + // installed for multiple architectures/languages). + manifest.Id = productCode; - Registry::Key arpKey = arpEntry.Open(); + manifest.Installers.emplace_back(); + // TODO: This likely needs some cleanup applied, as it looks like INNO tends to append an "_is#" + // that might vary across machines/installs. There may be other things we want to clean up as well, + // like trimming spaces at the ends, or removing the version string from the product code + // if it is present. + manifest.Installers[0].ProductCode = productCode; - // Ignore entries that are listed as SystemComponent - if (GetBoolValue(arpKey, SystemComponent)) - { - AICLI_LOG(Repo, Verbose, << "Skipping " << productCode << " because it is a SystemComponent"); - continue; - } + Registry::Key arpKey = arpEntry.Open(); - // If no name is provided, ignore this entry - auto displayName = arpKey[DisplayName]; - if (!displayName || displayName->GetType() != Registry::Value::Type::String) - { - AICLI_LOG(Repo, Verbose, << "Skipping " << productCode << " because DisplayName is not a REG_SZ value"); - continue; - } - auto displayNameValue = displayName->GetValue<Registry::Value::Type::String>(); - manifest.DefaultLocalization.Add<Manifest::Localization::PackageName>(displayNameValue); - if (displayNameValue.empty()) - { - AICLI_LOG(Repo, Verbose, << "Skipping " << productCode << " because DisplayName is empty"); - continue; - } + // Ignore entries that are listed as SystemComponent + if (GetBoolValue(arpKey, SystemComponent)) + { + AICLI_LOG(Repo, Verbose, << "Skipping " << productCode << " because it is a SystemComponent"); + continue; + } - // If no version can be determined, ignore this entry - manifest.Version = DetermineVersion(arpKey); - if (manifest.Version.empty()) - { - AICLI_LOG(Repo, Verbose, << "Skipping " << productCode << " because a version could not be determined"); - continue; - } + // If no name is provided, ignore this entry + auto displayName = arpKey[DisplayName]; + if (!displayName || displayName->GetType() != Registry::Value::Type::String) + { + AICLI_LOG(Repo, Verbose, << "Skipping " << productCode << " because DisplayName is not a REG_SZ value"); + continue; + } + auto displayNameValue = displayName->GetValue<Registry::Value::Type::String>(); + manifest.DefaultLocalization.Add<Manifest::Localization::PackageName>(displayNameValue); + if (displayNameValue.empty()) + { + AICLI_LOG(Repo, Verbose, << "Skipping " << productCode << " because DisplayName is empty"); + continue; + } - auto publisher = arpKey[Publisher]; - if (publisher && publisher->GetType() == Registry::Value::Type::String) - { - manifest.DefaultLocalization.Add<Manifest::Localization::Publisher>(publisher->GetValue<Registry::Value::Type::String>()); - - // If Publisher is set, change the Id using name normalization - // TODO: Figure out how to actually make this work since there are often instances of the same - // data in x64 and x86 entries that will collide. - //auto normalizedName = index.NormalizeName( - // manifest.DefaultLocalization.Get<Manifest::Localization::PackageName>(), - // manifest.DefaultLocalization.Get<Manifest::Localization::Publisher>()); - //manifest.Id = normalizedName.Publisher() + '.' + normalizedName.Name(); - } + // If no version can be determined, ignore this entry + manifest.Version = DetermineVersion(arpKey); + if (manifest.Version.empty()) + { + AICLI_LOG(Repo, Verbose, << "Skipping " << productCode << " because a version could not be determined"); + continue; + } - // TODO: If we want to keep the constructed manifest around to allow for `show` type commands - // against installed packages, we should use URLInfoAbout/HelpLink for the Homepage. + auto publisher = arpKey[Publisher]; + if (publisher && publisher->GetType() == Registry::Value::Type::String) + { + manifest.DefaultLocalization.Add<Manifest::Localization::Publisher>(publisher->GetValue<Registry::Value::Type::String>()); + + // If Publisher is set, change the Id using name normalization + // TODO: Figure out how to actually make this work since there are often instances of the same + // data in x64 and x86 entries that will collide. + //auto normalizedName = index.NormalizeName( + // manifest.DefaultLocalization.Get<Manifest::Localization::PackageName>(), + // manifest.DefaultLocalization.Get<Manifest::Localization::Publisher>()); + //manifest.Id = normalizedName.Publisher() + '.' + normalizedName.Name(); + } - // TODO: Determine the best way to handle duplicates; sometimes the same package will be listed under - // both x64 and x86 locations for ARP. - // For now, we will attempt to insert and catch. - std::optional<SQLiteIndex::IdType> manifestIdOpt; + // TODO: If we want to keep the constructed manifest around to allow for `show` type commands + // against installed packages, we should use URLInfoAbout/HelpLink for the Homepage. - try - { - // Use the ProductCode as a unique key for the path - manifestIdOpt = index.AddManifest(manifest, Utility::ConvertToUTF16(manifest.Installers[0].ProductCode)); - } - catch (...) - { - // Ignore errors if they occur, they are most likely a duplicate value - } + // TODO: Determine the best way to handle duplicates; sometimes the same package will be listed under + // both x64 and x86 locations for ARP. + // For now, we will attempt to insert and catch. + std::optional<SQLiteIndex::IdType> manifestIdOpt; - if (!manifestIdOpt) - { - AICLI_LOG(Repo, Warning, - << "Ignoring duplicate ARP entry " << scope << '|' << architecture << '|' << productCode << " [" << manifest.DefaultLocalization.Get<Manifest::Localization::PackageName>() << "]"); - continue; - } + try + { + // Use the ProductCode as a unique key for the path + manifestIdOpt = index.AddManifest(manifest, Utility::ConvertToUTF16(manifest.Installers[0].ProductCode)); + } + catch (...) + { + // Ignore errors if they occur, they are most likely a duplicate value + } - SQLiteIndex::IdType manifestId = manifestIdOpt.value(); + if (!manifestIdOpt) + { + AICLI_LOG(Repo, Warning, + << "Ignoring duplicate ARP entry " << scope << '|' << architecture << '|' << productCode << " [" << manifest.DefaultLocalization.Get<Manifest::Localization::PackageName>() << "]"); + continue; + } - // Pass scope along to metadata. - index.SetMetadataByManifestId(manifestId, PackageVersionMetadata::InstalledScope, scope); + SQLiteIndex::IdType manifestId = manifestIdOpt.value(); - // TODO: Pass along architecture, although there are cases where it is not clear what architecture the package - // is from it's ARP location, despite it very clearly being a specific architecture. And note that user - // scope does not have separate ARP locations, so every architecture would appear as native. + // Pass scope along to metadata. + index.SetMetadataByManifestId(manifestId, PackageVersionMetadata::InstalledScope, scope); - // Publisher is needed for certain scenarios but we don't store it from the manifest - if (manifest.DefaultLocalization.Contains(Manifest::Localization::Publisher)) - { - index.SetMetadataByManifestId( - manifestId, PackageVersionMetadata::Publisher, - manifest.DefaultLocalization.Get<Manifest::Localization::Publisher>()); - } + // TODO: Pass along architecture, although there are cases where it is not clear what architecture the package + // is from it's ARP location, despite it very clearly being a specific architecture. And note that user + // scope does not have separate ARP locations, so every architecture would appear as native. + + // Publisher is needed for certain scenarios but we don't store it from the manifest + if (manifest.DefaultLocalization.Contains(Manifest::Localization::Publisher)) + { + index.SetMetadataByManifestId( + manifestId, PackageVersionMetadata::Publisher, + manifest.DefaultLocalization.Get<Manifest::Localization::Publisher>()); + } - // Pick up InstallLocation when upgrade supports remove/install to enable this location - // to survive across the removal. - AddMetadataIfPresent(arpKey, InstallLocation, index, manifestId, PackageVersionMetadata::InstalledLocation); + // Pick up InstallLocation when upgrade supports remove/install to enable this location + // to survive across the removal. + AddMetadataIfPresent(arpKey, InstallLocation, index, manifestId, PackageVersionMetadata::InstalledLocation); - // Pick up UninstallString and QuietUninstallString for uninstall. - AddMetadataIfPresent(arpKey, UninstallString, index, manifestId, PackageVersionMetadata::StandardUninstallCommand); - AddMetadataIfPresent(arpKey, QuietUninstallString, index, manifestId, PackageVersionMetadata::SilentUninstallCommand); + // Pick up UninstallString and QuietUninstallString for uninstall. + AddMetadataIfPresent(arpKey, UninstallString, index, manifestId, PackageVersionMetadata::StandardUninstallCommand); + AddMetadataIfPresent(arpKey, QuietUninstallString, index, manifestId, PackageVersionMetadata::SilentUninstallCommand); - // Pick up Language to enable proper selection of language for upgrade. - AddMetadataIfPresent(arpKey, Language, index, manifestId, PackageVersionMetadata::InstalledLocale); + // Pick up Language to enable proper selection of language for upgrade. + AddMetadataIfPresent(arpKey, Language, index, manifestId, PackageVersionMetadata::InstalledLocale); - // Pick up WindowsInstaller to determine if this is an MSI install. - // TODO: Could also determine Inno (and maybe other types) through detecting other keys here. - auto installedType = Manifest::InstallerTypeEnum::Exe; + // Pick up WindowsInstaller to determine if this is an MSI install. + // TODO: Could also determine Inno (and maybe other types) through detecting other keys here. + auto installedType = Manifest::InstallerTypeEnum::Exe; - if (GetBoolValue(arpKey, WindowsInstaller)) + if (GetBoolValue(arpKey, WindowsInstaller)) + { + installedType = Manifest::InstallerTypeEnum::Msi; + } + + index.SetMetadataByManifestId(manifestId, PackageVersionMetadata::InstalledType, Manifest::InstallerTypeToString(installedType)); + } + catch (...) { - installedType = Manifest::InstallerTypeEnum::Msi; + AICLI_LOG(Repo, Warning, << "Failed to read ARP entry, ignoring it: " << scope << '|' << architecture << '|' << productCode); + LOG_CAUGHT_EXCEPTION(); } - - index.SetMetadataByManifestId(manifestId, PackageVersionMetadata::InstalledType, Manifest::InstallerTypeToString(installedType)); } } }