winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

ConfigurationProcessor.cpp (50842B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include "pch.h"
      4 #include "ConfigurationProcessor.h"
      5 #include "ConfigurationProcessor.g.cpp"
      6 #include "ConfigurationSet.h"
      7 #include "OpenConfigurationSetResult.h"
      8 #include "ConfigurationSetParser.h"
      9 #include "DiagnosticInformationInstance.h"
     10 #include "ApplyConfigurationSetResult.h"
     11 #include "ApplyConfigurationUnitResult.h"
     12 #include "TestConfigurationSetResult.h"
     13 #include "TestConfigurationUnitResult.h"
     14 #include "ConfigurationUnitResultInformation.h"
     15 #include "GetConfigurationUnitSettingsResult.h"
     16 #include "GetAllConfigurationUnitSettingsResult.h"
     17 #include "GetAllConfigurationUnitsResult.h"
     18 #include "ExceptionResultHelpers.h"
     19 #include "ConfigurationSetChangeData.h"
     20 #include "GetConfigurationUnitDetailsResult.h"
     21 #include "GetConfigurationSetDetailsResult.h"
     22 #include "DefaultSetGroupProcessor.h"
     23 #include "ConfigurationSequencer.h"
     24 #include "ConfigurationStatus.h"
     25 
     26 #include <AppInstallerErrors.h>
     27 #include <AppInstallerStrings.h>
     28 #include <AppInstallerSHA256.h>
     29 #include <winget/GroupPolicy.h>
     30 
     31 using namespace std::chrono_literals;
     32 
     33 namespace winrt::Microsoft::Management::Configuration::implementation
     34 {
     35     namespace
     36     {
     37         AppInstaller::Logging::Level ConvertLevel(DiagnosticLevel level)
     38         {
     39             switch (level)
     40             {
     41             case DiagnosticLevel::Verbose: return AppInstaller::Logging::Level::Verbose;
     42             case DiagnosticLevel::Informational: return AppInstaller::Logging::Level::Info;
     43             case DiagnosticLevel::Warning: return AppInstaller::Logging::Level::Warning;
     44             case DiagnosticLevel::Error: return AppInstaller::Logging::Level::Error;
     45             case DiagnosticLevel::Critical: return AppInstaller::Logging::Level::Crit;
     46             default: return AppInstaller::Logging::Level::Warning;
     47             }
     48         }
     49 
     50         DiagnosticLevel ConvertLevel(AppInstaller::Logging::Level level)
     51         {
     52             switch (level)
     53             {
     54             case AppInstaller::Logging::Level::Verbose: return DiagnosticLevel::Verbose;
     55             case AppInstaller::Logging::Level::Info: return DiagnosticLevel::Informational;
     56             case AppInstaller::Logging::Level::Warning: return DiagnosticLevel::Warning;
     57             case AppInstaller::Logging::Level::Error: return DiagnosticLevel::Error;
     58             case AppInstaller::Logging::Level::Crit: return DiagnosticLevel::Critical;
     59             default: return DiagnosticLevel::Warning;
     60             }
     61         }
     62 
     63         // ILogger that sends data back to the Diagnostics event of the ConfigurationProcessor.
     64         struct ConfigurationProcessorDiagnosticsLogger : public AppInstaller::Logging::ILogger
     65         {
     66             ConfigurationProcessorDiagnosticsLogger(ConfigurationProcessor& processor) : m_processor(processor) {}
     67 
     68             std::string GetName() const override
     69             {
     70                 return "ConfigurationProcessorDiagnosticsLogger";
     71             }
     72 
     73             void Write(AppInstaller::Logging::Channel channel, AppInstaller::Logging::Level level, std::string_view message) noexcept override try
     74             {
     75                 std::ostringstream strstr;
     76                 strstr << '[' << AppInstaller::Logging::GetChannelName(channel) << "] " << message;
     77                 m_processor.SendDiagnostics(ConvertLevel(level), strstr.str());
     78             }
     79             catch (...) {}
     80 
     81             void WriteDirect(AppInstaller::Logging::Channel, AppInstaller::Logging::Level level, std::string_view message) noexcept override try
     82             {
     83                 m_processor.SendDiagnostics(ConvertLevel(level), message);
     84             }
     85             catch (...) {}
     86 
     87         private:
     88             ConfigurationProcessor& m_processor;
     89         };
     90 
     91         // Helper to ensure a one-time callback attach
     92         struct AttachWilFailureCallback
     93         {
     94             AttachWilFailureCallback()
     95             {
     96                 wil::SetResultLoggingCallback(wilResultLoggingCallback);
     97             }
     98 
     99             ~AttachWilFailureCallback() = default;
    100 
    101             static void __stdcall wilResultLoggingCallback(const wil::FailureInfo& info) noexcept
    102             {
    103                 AICLI_LOG(Fail, Error, << [&]() {
    104                     wchar_t message[2048];
    105                     GetFailureLogString(message, ARRAYSIZE(message), info);
    106                     return AppInstaller::Utility::ConvertToUTF8(message);
    107                     }());
    108             }
    109 
    110             static void Ensure()
    111             {
    112                 static AttachWilFailureCallback s_callbackAttach;
    113             }
    114         };
    115     }
    116 
    117     ConfigurationProcessor::ConfigurationProcessor()
    118     {
    119         THROW_HR_IF(APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY, !::AppInstaller::Settings::GroupPolicies().IsEnabled(::AppInstaller::Settings::TogglePolicy::Policy::WinGet));
    120         THROW_HR_IF(APPINSTALLER_CLI_ERROR_BLOCKED_BY_POLICY, !::AppInstaller::Settings::GroupPolicies().IsEnabled(::AppInstaller::Settings::TogglePolicy::Policy::Configuration));
    121 
    122         AppInstaller::Logging::DiagnosticLogger& logger = m_threadGlobals.GetDiagnosticLogger();
    123         logger.SetEnabledChannels(AppInstaller::Logging::Channel::All);
    124         logger.SetLevel(AppInstaller::Logging::Level::Verbose);
    125         logger.AddLogger(std::make_unique<ConfigurationProcessorDiagnosticsLogger>(*this));
    126     }
    127 
    128     ConfigurationProcessor::ConfigurationProcessor(const IConfigurationSetProcessorFactory& factory) : ConfigurationProcessor()
    129     {
    130         ConfigurationSetProcessorFactory(factory);
    131     }
    132 
    133     event_token ConfigurationProcessor::Diagnostics(const Windows::Foundation::EventHandler<IDiagnosticInformation>& handler)
    134     {
    135         AttachWilFailureCallback::Ensure();
    136         return m_diagnostics.add(handler);
    137     }
    138 
    139     void ConfigurationProcessor::Diagnostics(const event_token& token) noexcept
    140     {
    141         m_diagnostics.remove(token);
    142     }
    143 
    144     DiagnosticLevel ConfigurationProcessor::MinimumLevel()
    145     {
    146         return m_minimumLevel;
    147     }
    148 
    149     void ConfigurationProcessor::MinimumLevel(DiagnosticLevel value)
    150     {
    151         m_minimumLevel = value;
    152         m_threadGlobals.GetDiagnosticLogger().SetLevel(ConvertLevel(value));
    153         if (m_factory)
    154         {
    155             m_factory.MinimumLevel(value);
    156         }
    157     }
    158 
    159     hstring ConfigurationProcessor::Caller() const
    160     {
    161         return hstring{ AppInstaller::Utility::ConvertToUTF16(m_threadGlobals.GetTelemetryLogger().GetCaller()) };
    162     }
    163 
    164     void ConfigurationProcessor::Caller(hstring value)
    165     {
    166         m_threadGlobals.GetTelemetryLogger().SetCaller(AppInstaller::Utility::ConvertToUTF8(value));
    167     }
    168 
    169     guid ConfigurationProcessor::ActivityIdentifier()
    170     {
    171         return *m_threadGlobals.GetTelemetryLogger().GetActivityId();
    172     }
    173 
    174     void ConfigurationProcessor::ActivityIdentifier(const guid& value)
    175     {
    176         m_threadGlobals.GetTelemetryLogger().SetActivityId(value);
    177     }
    178 
    179     bool ConfigurationProcessor::GenerateTelemetryEvents()
    180     {
    181         return m_threadGlobals.GetTelemetryLogger().IsEnabled();
    182     }
    183 
    184     void ConfigurationProcessor::GenerateTelemetryEvents(bool value)
    185     {
    186         std::ignore = m_threadGlobals.GetTelemetryLogger().EnableRuntime(value);
    187     }
    188 
    189     event_token ConfigurationProcessor::ConfigurationChange(const Windows::Foundation::TypedEventHandler<Configuration::ConfigurationSet, Configuration::ConfigurationChangeData>& handler)
    190     {
    191         if (!m_configurationChange)
    192         {
    193             auto status = ConfigurationStatus::Instance();
    194             std::atomic_store(&m_changeRegistration, status->RegisterForChange(*this));
    195         }
    196 
    197         return m_configurationChange.add(handler);
    198     }
    199 
    200     void ConfigurationProcessor::ConfigurationChange(const event_token& token) noexcept
    201     {
    202         m_configurationChange.remove(token);
    203 
    204         if (!m_configurationChange)
    205         {
    206             std::atomic_store(&m_changeRegistration, {});
    207         }
    208     }
    209 
    210     void ConfigurationProcessor::ConfigurationChange(const Configuration::ConfigurationSet& set, const Configuration::ConfigurationChangeData& data) try
    211     {
    212         m_configurationChange(set, data);
    213     }
    214     CATCH_LOG();
    215 
    216     Windows::Foundation::Collections::IVector<Configuration::ConfigurationSet> ConfigurationProcessor::GetConfigurationHistory()
    217     {
    218         return GetConfigurationHistoryImpl();
    219     }
    220 
    221     Windows::Foundation::IAsyncOperation<Windows::Foundation::Collections::IVector<Configuration::ConfigurationSet>> ConfigurationProcessor::GetConfigurationHistoryAsync()
    222     {
    223         auto strong_this{ get_strong() };
    224         co_await winrt::resume_background();
    225         co_return GetConfigurationHistoryImpl({ co_await winrt::get_cancellation_token() });
    226     }
    227 
    228     Configuration::OpenConfigurationSetResult ConfigurationProcessor::OpenConfigurationSet(const Windows::Storage::Streams::IInputStream& stream)
    229     {
    230         return OpenConfigurationSetAsync(stream).get();
    231     }
    232 
    233     Windows::Foundation::IAsyncOperation<Configuration::OpenConfigurationSetResult> ConfigurationProcessor::OpenConfigurationSetAsync(const Windows::Storage::Streams::IInputStream& stream)
    234     {
    235         auto strong_this{ get_strong() };
    236         Windows::Storage::Streams::IInputStream localStream = stream;
    237 
    238         co_await winrt::resume_background();
    239         auto cancellation = co_await winrt::get_cancellation_token();
    240 
    241         auto threadGlobals = m_threadGlobals.SetForCurrentThread();
    242         auto result = make_self<wil::details::module_count_wrapper<OpenConfigurationSetResult>>();
    243 
    244         if (!localStream)
    245         {
    246             result->Initialize(E_POINTER, {});
    247             co_return *result;
    248         }
    249 
    250         try
    251         {
    252             // Read the entire file into memory as we expect them to be small and
    253             // our YAML parser doesn't support streaming at this time.
    254             // This is done here to enable easy cancellation propagation to the stream reads.
    255             uint32_t bufferSize = 1 << 20;
    256             Windows::Storage::Streams::Buffer buffer(bufferSize);
    257 
    258             // Memory stream in mixed elevation does not support InputStreamOptions as flags.
    259             Windows::Storage::Streams::InputStreamOptions readOptions = Windows::Storage::Streams::InputStreamOptions::Partial;
    260             std::string inputString;
    261 
    262             for (;;)
    263             {
    264                 auto asyncOperation = localStream.ReadAsync(buffer, bufferSize, readOptions);
    265 
    266                 // Manually poll status and propagate cancellation to stay on this thread for thread globals
    267                 while (asyncOperation.Status() == Windows::Foundation::AsyncStatus::Started)
    268                 {
    269                     if (cancellation())
    270                     {
    271                         asyncOperation.Cancel();
    272                     }
    273 
    274                     std::this_thread::sleep_for(100ms);
    275                 }
    276 
    277                 Windows::Storage::Streams::IBuffer readBuffer = asyncOperation.GetResults();
    278 
    279                 size_t readSize = static_cast<size_t>(readBuffer.Length());
    280                 if (readSize)
    281                 {
    282                     static_assert(sizeof(char) == sizeof(*readBuffer.data()));
    283                     inputString.append(reinterpret_cast<char*>(readBuffer.data()), readSize);
    284                 }
    285                 else
    286                 {
    287                     break;
    288                 }
    289             }
    290 
    291             std::unique_ptr<ConfigurationSetParser> parser = ConfigurationSetParser::Create(inputString);
    292 
    293             if (FAILED(parser->Result()))
    294             {
    295                 result->Initialize(parser->Result(), parser->Field(), parser->Value(), parser->Line(), parser->Column());
    296                 co_return *result;
    297             }
    298 
    299             parser->Parse();
    300             if (FAILED(parser->Result()))
    301             {
    302                 result->Initialize(parser->Result(), parser->Field(), parser->Value(), parser->Line(), parser->Column());
    303                 co_return *result;
    304             }
    305 
    306             auto configurationSet = parser->GetConfigurationSet();
    307             PropagateLifetimeWatcher(configurationSet.as<Windows::Foundation::IUnknown>());
    308             configurationSet->SetInputHash(AppInstaller::Utility::SHA256::ConvertToString(AppInstaller::Utility::SHA256::ComputeHash(inputString)));
    309 
    310             result->Initialize(*configurationSet);
    311         }
    312         catch (const wil::ResultException& resultException)
    313         {
    314             result->Initialize(resultException.GetErrorCode());
    315         }
    316         catch (...)
    317         {
    318             result->Initialize(WINGET_CONFIG_ERROR_INVALID_CONFIGURATION_FILE);
    319             LOG_CAUGHT_EXCEPTION();
    320         }
    321 
    322         co_return *result;
    323     }
    324 
    325     Windows::Foundation::Collections::IVector<ConfigurationConflict> ConfigurationProcessor::CheckForConflicts(
    326         const Windows::Foundation::Collections::IVectorView<Configuration::ConfigurationSet>& configurationSets,
    327         bool includeConfigurationHistory)
    328     {
    329         UNREFERENCED_PARAMETER(configurationSets);
    330         UNREFERENCED_PARAMETER(includeConfigurationHistory);
    331         THROW_HR(E_NOTIMPL);
    332     }
    333 
    334     Windows::Foundation::IAsyncOperation<Windows::Foundation::Collections::IVector<ConfigurationConflict>> ConfigurationProcessor::CheckForConflictsAsync(
    335         const Windows::Foundation::Collections::IVectorView<Configuration::ConfigurationSet>& configurationSets,
    336         bool includeConfigurationHistory)
    337     {
    338         co_return CheckForConflicts(configurationSets, includeConfigurationHistory);
    339     }
    340 
    341     Configuration::GetConfigurationSetDetailsResult ConfigurationProcessor::GetSetDetails(const Configuration::ConfigurationSet& configurationSet, ConfigurationUnitDetailFlags detailFlags)
    342     {
    343         THROW_HR_IF(E_NOT_VALID_STATE, !m_factory);
    344         return GetSetDetailsImpl(configurationSet, detailFlags);
    345     }
    346 
    347     Windows::Foundation::IAsyncOperationWithProgress<Configuration::GetConfigurationSetDetailsResult, Configuration::GetConfigurationUnitDetailsResult> ConfigurationProcessor::GetSetDetailsAsync(
    348         const Configuration::ConfigurationSet& configurationSet,
    349         ConfigurationUnitDetailFlags detailFlags)
    350     {
    351         THROW_HR_IF(E_NOT_VALID_STATE, !m_factory);
    352 
    353         auto strong_this{ get_strong() };
    354         Configuration::ConfigurationSet localSet = configurationSet;
    355 
    356         co_await winrt::resume_background();
    357 
    358         co_return GetSetDetailsImpl(localSet, detailFlags, { co_await winrt::get_progress_token(), co_await winrt::get_cancellation_token()});
    359     }
    360 
    361     Windows::Foundation::Collections::IVector<Configuration::ConfigurationSet> ConfigurationProcessor::GetConfigurationHistoryImpl(ShutdownAwareAsyncCancellation cancellation)
    362     {
    363         auto threadGlobals = m_threadGlobals.SetForCurrentThread();
    364 
    365         m_database.EnsureOpened(false);
    366         cancellation.ThrowIfCancelled();
    367 
    368         std::vector<Configuration::ConfigurationSet> result;
    369         for (const auto& set : m_database.GetSetHistory())
    370         {
    371             PropagateLifetimeWatcher(*set);
    372             result.emplace_back(*set);
    373         }
    374 
    375         return multi_threaded_vector(std::move(result));
    376     }
    377 
    378     Configuration::GetConfigurationSetDetailsResult ConfigurationProcessor::GetSetDetailsImpl(
    379         const Configuration::ConfigurationSet& configurationSet,
    380         ConfigurationUnitDetailFlags detailFlags,
    381         ShutdownAwareAsyncProgress<GetConfigurationSetDetailsResult, GetConfigurationUnitDetailsResult> progress)
    382     {
    383         auto threadGlobals = m_threadGlobals.SetForCurrentThread();
    384 
    385         IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(configurationSet);
    386 
    387         auto result = make_self<wil::details::module_count_wrapper<implementation::GetConfigurationSetDetailsResult>>();
    388         progress.Result(*result);
    389 
    390         for (const auto& unit : configurationSet.Units())
    391         {
    392             progress.ThrowIfCancelled();
    393 
    394             auto unitResult = make_self<wil::details::module_count_wrapper<implementation::GetConfigurationUnitDetailsResult>>();
    395             auto unitResultInformation = make_self<wil::details::module_count_wrapper<implementation::ConfigurationUnitResultInformation>>();
    396             unitResult->Unit(unit);
    397             unitResult->ResultInformation(*unitResultInformation);
    398 
    399             try
    400             {
    401                 IConfigurationUnitProcessorDetails details = setProcessor.GetUnitProcessorDetails(unit, detailFlags);
    402                 unitResult->Details(details);
    403                 get_self<implementation::ConfigurationUnit>(unit)->Details(std::move(details));
    404             }
    405             catch (...)
    406             {
    407                 ExtractUnitResultInformation(std::current_exception(), unitResultInformation);
    408             }
    409 
    410             result->UnitResultsVector().Append(*unitResult);
    411             progress.Progress(*unitResult);
    412         }
    413 
    414         return *result;
    415     }
    416 
    417     Configuration::GetConfigurationUnitDetailsResult ConfigurationProcessor::GetUnitDetails(const ConfigurationUnit& unit, ConfigurationUnitDetailFlags detailFlags)
    418     {
    419         THROW_HR_IF(E_NOT_VALID_STATE, !m_factory);
    420         return GetUnitDetailsImpl(unit, detailFlags);
    421     }
    422 
    423     Windows::Foundation::IAsyncOperation<Configuration::GetConfigurationUnitDetailsResult> ConfigurationProcessor::GetUnitDetailsAsync(const ConfigurationUnit& unit, ConfigurationUnitDetailFlags detailFlags)
    424     {
    425         THROW_HR_IF(E_NOT_VALID_STATE, !m_factory);
    426 
    427         auto strong_this{ get_strong() };
    428         ConfigurationUnit localUnit = unit;
    429 
    430         co_await winrt::resume_background();
    431 
    432         co_return GetUnitDetailsImpl(localUnit, detailFlags);
    433     }
    434 
    435     Configuration::GetConfigurationUnitDetailsResult ConfigurationProcessor::GetUnitDetailsImpl(const ConfigurationUnit& unit, ConfigurationUnitDetailFlags detailFlags)
    436     {
    437         auto threadGlobals = m_threadGlobals.SetForCurrentThread();
    438 
    439         IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(nullptr);
    440 
    441         auto unitResult = make_self<wil::details::module_count_wrapper<implementation::GetConfigurationUnitDetailsResult>>();
    442         auto unitResultInformation = make_self<wil::details::module_count_wrapper<implementation::ConfigurationUnitResultInformation>>();
    443         unitResult->Unit(unit);
    444         unitResult->ResultInformation(*unitResultInformation);
    445 
    446         try
    447         {
    448             IConfigurationUnitProcessorDetails details = setProcessor.GetUnitProcessorDetails(unit, detailFlags);
    449             unitResult->Details(details);
    450             get_self<implementation::ConfigurationUnit>(unit)->Details(std::move(details));
    451         }
    452         catch (...)
    453         {
    454             ExtractUnitResultInformation(std::current_exception(), unitResultInformation);
    455         }
    456 
    457         return *unitResult;
    458     }
    459 
    460     Configuration::ApplyConfigurationSetResult ConfigurationProcessor::ApplySet(const Configuration::ConfigurationSet& configurationSet, ApplyConfigurationSetFlags flags)
    461     {
    462         THROW_HR_IF(E_NOT_VALID_STATE, !m_factory);
    463         return ApplySetImpl(configurationSet, flags);
    464     }
    465 
    466     Windows::Foundation::IAsyncOperationWithProgress<Configuration::ApplyConfigurationSetResult, Configuration::ConfigurationSetChangeData> ConfigurationProcessor::ApplySetAsync(
    467         const Configuration::ConfigurationSet& configurationSet,
    468         ApplyConfigurationSetFlags flags)
    469     {
    470         THROW_HR_IF(E_NOT_VALID_STATE, !m_factory);
    471 
    472         auto strong_this{ get_strong() };
    473         Configuration::ConfigurationSet localSet = configurationSet;
    474 
    475         co_await winrt::resume_background();
    476 
    477         co_return ApplySetImpl(localSet, flags, { co_await winrt::get_progress_token(), co_await winrt::get_cancellation_token() });
    478     }
    479 
    480     Configuration::ApplyConfigurationSetResult ConfigurationProcessor::ApplySetImpl(
    481         const Configuration::ConfigurationSet& configurationSet,
    482         ApplyConfigurationSetFlags flags,
    483         ShutdownAwareAsyncProgress<ApplyConfigurationSetResult, ConfigurationSetChangeData> progress)
    484     {
    485         auto threadGlobals = m_threadGlobals.SetForCurrentThread();
    486 
    487         IConfigurationGroupProcessor groupProcessor;
    488         bool recordHistoryAndStatus = false;
    489 
    490         if (WI_IsFlagSet(flags, ApplyConfigurationSetFlags::PerformConsistencyCheckOnly))
    491         {
    492             // If performing a consistency check, always use the default processor and let it know as well
    493             auto defaultGroupProcessor = make_self<wil::details::module_count_wrapper<implementation::DefaultSetGroupProcessor>>();
    494             defaultGroupProcessor->Initialize(configurationSet, nullptr, m_threadGlobals, true);
    495             groupProcessor = *defaultGroupProcessor;
    496         }
    497         else
    498         {
    499             groupProcessor = GetSetGroupProcessor(configurationSet);
    500 
    501             // Write this set to the database history
    502             // This is a somewhat arbitrary time to write it, but it should not be done if PerformConsistencyCheckOnly is passed, so this is convenient.
    503             recordHistoryAndStatus = true;
    504             m_database.EnsureOpened();
    505             progress.ThrowIfCancelled();
    506             m_database.WriteSetHistory(configurationSet, WI_IsFlagSet(flags, ApplyConfigurationSetFlags::DoNotOverwriteMatchingOriginSet));
    507         }
    508 
    509         auto result = make_self<wil::details::module_count_wrapper<implementation::ApplyConfigurationSetResult>>();
    510 
    511         // Build out the unit results and a map to find them quickly
    512         using UnitResultType = decltype(make_self<wil::details::module_count_wrapper<implementation::ApplyConfigurationUnitResult>>());
    513         std::map<guid, UnitResultType> unitResultMap;
    514 
    515         std::function<void(const winrt::Windows::Foundation::Collections::IVector<Configuration::ConfigurationUnit>&)> createUnitResults =
    516             [&](const winrt::Windows::Foundation::Collections::IVector<Configuration::ConfigurationUnit>& units)
    517             {
    518                 for (const Configuration::ConfigurationUnit& unit : units)
    519                 {
    520                     // Add to result
    521                     UnitResultType applyUnitResult = make_self<wil::details::module_count_wrapper<implementation::ApplyConfigurationUnitResult>>();
    522                     applyUnitResult->Unit(unit);
    523                     result->UnitResultsVector().Append(*applyUnitResult);
    524 
    525                     // Add to map
    526                     unitResultMap.emplace(unit.InstanceIdentifier(), applyUnitResult);
    527 
    528                     // Handle members if present
    529                     if (unit.IsGroup())
    530                     {
    531                         createUnitResults(unit.Units());
    532                     }
    533                 }
    534             };
    535 
    536         createUnitResults(configurationSet.Units());
    537 
    538         progress.Result(*result);
    539 
    540         try
    541         {
    542             ConfigurationSequencer sequencer{ m_database };
    543             auto status = ConfigurationStatus::Instance();
    544             guid setInstanceIdentifier = configurationSet.InstanceIdentifier();
    545             auto updateState = [&](ConfigurationSetState state)
    546                 {
    547                     try
    548                     {
    549                         progress.Progress(implementation::ConfigurationSetChangeData::Create(state));
    550                     }
    551                     CATCH_LOG();
    552 
    553                     if (recordHistoryAndStatus)
    554                     {
    555                         status->UpdateSetState(setInstanceIdentifier, state);
    556                     }
    557                 };
    558 
    559             if (!WI_IsFlagSet(flags, ApplyConfigurationSetFlags::PerformConsistencyCheckOnly))
    560             {
    561                 if (sequencer.Enqueue(configurationSet))
    562                 {
    563                     updateState(ConfigurationSetState::Pending);
    564                     sequencer.Wait(progress.GetCancellation());
    565                 }
    566             }
    567 
    568             progress.ThrowIfCancelled();
    569 
    570             updateState(ConfigurationSetState::InProgress);
    571 
    572             // Forward unit result progress to caller
    573             auto applyOperation = groupProcessor.ApplyGroupSettingsAsync([&](const auto&, const IApplyGroupMemberSettingsResult& unitResult)
    574                 {
    575                     auto itr = unitResultMap.find(unitResult.Unit().InstanceIdentifier());
    576                     if (itr != unitResultMap.end())
    577                     {
    578                         itr->second->Initialize(unitResult);
    579                     }
    580 
    581                     // Create progress object
    582                     auto applyResult = make_self<implementation::ConfigurationSetChangeData>();
    583                     applyResult->Initialize(unitResult);
    584                     progress.Progress(*applyResult);
    585 
    586                     if (recordHistoryAndStatus)
    587                     {
    588                         status->UpdateUnitState(setInstanceIdentifier, applyResult);
    589                     }
    590                 });
    591 
    592             // Cancel the inner operation if we are cancelled
    593             progress.Callback([applyOperation]() { applyOperation.Cancel(); });
    594 
    595             IApplyGroupSettingsResult applyResult = applyOperation.get();
    596 
    597             // Place all results from the processor into our result
    598             if (applyResult.ResultInformation())
    599             {
    600                 result->ResultCode(applyResult.ResultInformation().ResultCode());
    601             }
    602 
    603             for (const IApplyGroupMemberSettingsResult& unitResult : applyResult.UnitResults())
    604             {
    605                 // Update overall result
    606                 auto itr = unitResultMap.find(unitResult.Unit().InstanceIdentifier());
    607                 if (itr == unitResultMap.end())
    608                 {
    609                     continue;
    610                 }
    611 
    612                 itr->second->Initialize(unitResult);
    613 
    614                 m_threadGlobals.GetTelemetryLogger().LogConfigUnitRunIfAppropriate(
    615                     configurationSet.InstanceIdentifier(),
    616                     itr->second->Unit(),
    617                     ConfigurationUnitIntent::Apply,
    618                     TelemetryTraceLogger::ApplyAction,
    619                     itr->second->ResultInformation());
    620             }
    621 
    622             updateState(ConfigurationSetState::Completed);
    623 
    624             m_threadGlobals.GetTelemetryLogger().LogConfigProcessingSummaryForApply(*winrt::get_self<implementation::ConfigurationSet>(configurationSet), *result);
    625             return *result;
    626         }
    627         catch (...)
    628         {
    629             m_threadGlobals.GetTelemetryLogger().LogConfigProcessingSummaryForApplyException(
    630                 *winrt::get_self<implementation::ConfigurationSet>(configurationSet),
    631                 LOG_CAUGHT_EXCEPTION(),
    632                 *result);
    633             throw;
    634         }
    635     }
    636 
    637     Configuration::TestConfigurationSetResult ConfigurationProcessor::TestSet(const Configuration::ConfigurationSet& configurationSet)
    638     {
    639         THROW_HR_IF(E_NOT_VALID_STATE, !m_factory);
    640         return TestSetImpl(configurationSet);
    641     }
    642 
    643     Windows::Foundation::IAsyncOperationWithProgress<Configuration::TestConfigurationSetResult, Configuration::TestConfigurationUnitResult> ConfigurationProcessor::TestSetAsync(const Configuration::ConfigurationSet& configurationSet)
    644     {
    645         THROW_HR_IF(E_NOT_VALID_STATE, !m_factory);
    646 
    647         auto strong_this{ get_strong() };
    648         Configuration::ConfigurationSet localSet = configurationSet;
    649 
    650         co_await winrt::resume_background();
    651 
    652         co_return TestSetImpl(localSet, { co_await winrt::get_progress_token(), co_await winrt::get_cancellation_token() });
    653     }
    654 
    655     Configuration::TestConfigurationSetResult ConfigurationProcessor::TestSetImpl(
    656         const Configuration::ConfigurationSet& configurationSet,
    657         ShutdownAwareAsyncProgress<TestConfigurationSetResult, TestConfigurationUnitResult> progress)
    658     {
    659         auto threadGlobals = m_threadGlobals.SetForCurrentThread();
    660 
    661         IConfigurationGroupProcessor groupProcessor = GetSetGroupProcessor(configurationSet);
    662         auto result = make_self<wil::details::module_count_wrapper<implementation::TestConfigurationSetResult>>();
    663         result->TestResult(ConfigurationTestResult::NotRun);
    664         progress.Result(*result);
    665 
    666         try
    667         {
    668             // Forward unit result progress to caller
    669             auto testOperation = groupProcessor.TestGroupSettingsAsync([&](const auto&, const ITestSettingsResult& unitResult)
    670                 {
    671                     auto testResult = make_self<wil::details::module_count_wrapper<implementation::TestConfigurationUnitResult>>();
    672                     testResult->Initialize(unitResult);
    673 
    674                     result->AppendUnitResult(*testResult);
    675                     progress.Progress(*testResult);
    676                 });
    677 
    678             // Cancel the inner operation if we are cancelled
    679             progress.Callback([testOperation]() { testOperation.Cancel(); });
    680 
    681             ITestGroupSettingsResult testResult = testOperation.get();
    682 
    683             // Send telemetry for all results
    684             for (const ITestSettingsResult& unitResult : testResult.UnitResults())
    685             {
    686                 auto testUnitResult = make_self<wil::details::module_count_wrapper<implementation::TestConfigurationUnitResult>>();
    687                 testUnitResult->Initialize(unitResult);
    688 
    689                 m_threadGlobals.GetTelemetryLogger().LogConfigUnitRunIfAppropriate(
    690                     configurationSet.InstanceIdentifier(),
    691                     testUnitResult->Unit(),
    692                     ConfigurationUnitIntent::Assert,
    693                     TelemetryTraceLogger::TestAction,
    694                     testUnitResult->ResultInformation());
    695             }
    696 
    697             m_threadGlobals.GetTelemetryLogger().LogConfigProcessingSummaryForTest(*winrt::get_self<implementation::ConfigurationSet>(configurationSet), *result);
    698             return *result;
    699         }
    700         catch (...)
    701         {
    702             m_threadGlobals.GetTelemetryLogger().LogConfigProcessingSummaryForTestException(
    703                 *winrt::get_self<implementation::ConfigurationSet>(configurationSet),
    704                 LOG_CAUGHT_EXCEPTION(),
    705                 *result);
    706             throw;
    707         }
    708     }
    709 
    710     Configuration::GetConfigurationUnitSettingsResult ConfigurationProcessor::GetUnitSettings(const ConfigurationUnit& unit)
    711     {
    712         THROW_HR_IF(E_NOT_VALID_STATE, !m_factory);
    713         return GetUnitSettingsImpl(unit);
    714     }
    715 
    716     Windows::Foundation::IAsyncOperation<Configuration::GetConfigurationUnitSettingsResult> ConfigurationProcessor::GetUnitSettingsAsync(const ConfigurationUnit& unit)
    717     {
    718         THROW_HR_IF(E_NOT_VALID_STATE, !m_factory);
    719 
    720         auto strong_this{ get_strong() };
    721         ConfigurationUnit localUnit = unit;
    722 
    723         co_await winrt::resume_background();
    724 
    725         co_return GetUnitSettingsImpl(localUnit, { co_await winrt::get_cancellation_token() });
    726     }
    727 
    728     Configuration::GetConfigurationUnitSettingsResult ConfigurationProcessor::GetUnitSettingsImpl(
    729         const ConfigurationUnit& unit,
    730         ShutdownAwareAsyncCancellation cancellation)
    731     {
    732         auto threadGlobals = m_threadGlobals.SetForCurrentThread();
    733 
    734         IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(nullptr);
    735         auto result = make_self<wil::details::module_count_wrapper<implementation::GetConfigurationUnitSettingsResult>>();
    736         auto unitResult = make_self<wil::details::module_count_wrapper<implementation::ConfigurationUnitResultInformation>>();
    737         result->ResultInformation(*unitResult);
    738 
    739         cancellation.ThrowIfCancelled();
    740 
    741         IConfigurationUnitProcessor unitProcessor;
    742 
    743         try
    744         {
    745             unitProcessor = setProcessor.CreateUnitProcessor(unit);
    746         }
    747         catch (...)
    748         {
    749             ExtractUnitResultInformation(std::current_exception(), unitResult);
    750         }
    751 
    752         cancellation.ThrowIfCancelled();
    753 
    754         if (unitProcessor)
    755         {
    756             try
    757             {
    758                 IGetSettingsResult settingsResult = unitProcessor.GetSettings();
    759                 result->Settings(settingsResult.Settings());
    760                 result->ResultInformation(settingsResult.ResultInformation());
    761             }
    762             catch (...)
    763             {
    764                 ExtractUnitResultInformation(std::current_exception(), unitResult);
    765             }
    766 
    767             m_threadGlobals.GetTelemetryLogger().LogConfigUnitRunIfAppropriate(GUID_NULL, unit, ConfigurationUnitIntent::Inform, TelemetryTraceLogger::GetAction, result->ResultInformation());
    768         }
    769 
    770         return *result;
    771     }
    772 
    773     Configuration::GetAllConfigurationUnitSettingsResult ConfigurationProcessor::GetAllUnitSettings(const ConfigurationUnit& unit)
    774     {
    775         THROW_HR_IF(E_NOT_VALID_STATE, !m_factory);
    776         return GetAllUnitSettingsImpl(unit);
    777     }
    778 
    779     Windows::Foundation::IAsyncOperation<Configuration::GetAllConfigurationUnitSettingsResult> ConfigurationProcessor::GetAllUnitSettingsAsync(const ConfigurationUnit& unit)
    780     {
    781         THROW_HR_IF(E_NOT_VALID_STATE, !m_factory);
    782 
    783         auto strong_this{ get_strong() };
    784         ConfigurationUnit localUnit = unit;
    785 
    786         co_await winrt::resume_background();
    787 
    788         co_return GetAllUnitSettingsImpl(localUnit, { co_await winrt::get_cancellation_token() });
    789     }
    790 
    791     Configuration::GetAllConfigurationUnitSettingsResult ConfigurationProcessor::GetAllUnitSettingsImpl(
    792         const ConfigurationUnit& unit,
    793         ShutdownAwareAsyncCancellation cancellation)
    794     {
    795         auto threadGlobals = m_threadGlobals.SetForCurrentThread();
    796 
    797         IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(nullptr);
    798         auto result = make_self<wil::details::module_count_wrapper<implementation::GetAllConfigurationUnitSettingsResult>>();
    799         auto unitResult = make_self<wil::details::module_count_wrapper<implementation::ConfigurationUnitResultInformation>>();
    800         result->ResultInformation(*unitResult);
    801 
    802         cancellation.ThrowIfCancelled();
    803 
    804         IConfigurationUnitProcessor unitProcessor;
    805 
    806         try
    807         {
    808             unitProcessor = setProcessor.CreateUnitProcessor(unit);
    809         }
    810         catch (...)
    811         {
    812             ExtractUnitResultInformation(std::current_exception(), unitResult);
    813         }
    814 
    815         cancellation.ThrowIfCancelled();
    816 
    817         IGetAllSettingsConfigurationUnitProcessor getAllSettingsUnitProcessor;
    818         if (unitProcessor.try_as<IGetAllSettingsConfigurationUnitProcessor>(getAllSettingsUnitProcessor))
    819         {
    820             cancellation.ThrowIfCancelled();
    821 
    822             try
    823             {
    824                 IGetAllSettingsResult allSettingsResult = getAllSettingsUnitProcessor.GetAllSettings();
    825                 result->Settings(allSettingsResult.Settings());
    826                 result->ResultInformation(allSettingsResult.ResultInformation());
    827             }
    828             catch (...)
    829             {
    830                 ExtractUnitResultInformation(std::current_exception(), unitResult);
    831             }
    832 
    833             m_threadGlobals.GetTelemetryLogger().LogConfigUnitRunIfAppropriate(GUID_NULL, unit, ConfigurationUnitIntent::Inform, TelemetryTraceLogger::ExportAction, result->ResultInformation());
    834         }
    835         else
    836         {
    837             AICLI_LOG(Config, Error, << "Unit Processor does not support GetAllSettings operation");
    838             unitResult->Initialize(WINGET_CONFIG_ERROR_NOT_SUPPORTED_BY_PROCESSOR, hstring{});
    839         }
    840 
    841         return *result;
    842     }
    843 
    844     Configuration::GetAllConfigurationUnitsResult ConfigurationProcessor::GetAllUnits(const ConfigurationUnit& unit)
    845     {
    846         THROW_HR_IF(E_NOT_VALID_STATE, !m_factory);
    847         return GetAllUnitsImpl(unit);
    848     }
    849 
    850     Windows::Foundation::IAsyncOperation<Configuration::GetAllConfigurationUnitsResult> ConfigurationProcessor::GetAllUnitsAsync(const ConfigurationUnit& unit)
    851     {
    852         THROW_HR_IF(E_NOT_VALID_STATE, !m_factory);
    853 
    854         auto strong_this{ get_strong() };
    855         ConfigurationUnit localUnit = unit;
    856 
    857         co_await winrt::resume_background();
    858 
    859         co_return GetAllUnitsImpl(localUnit, { co_await winrt::get_cancellation_token() });
    860     }
    861 
    862     Configuration::GetAllConfigurationUnitsResult ConfigurationProcessor::GetAllUnitsImpl(
    863         const ConfigurationUnit& unit,
    864         ShutdownAwareAsyncCancellation cancellation)
    865     {
    866         auto threadGlobals = m_threadGlobals.SetForCurrentThread();
    867 
    868         IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(nullptr);
    869         auto result = make_self<wil::details::module_count_wrapper<implementation::GetAllConfigurationUnitsResult>>();
    870         auto unitResult = make_self<wil::details::module_count_wrapper<implementation::ConfigurationUnitResultInformation>>();
    871         result->ResultInformation(*unitResult);
    872 
    873         cancellation.ThrowIfCancelled();
    874 
    875         IConfigurationUnitProcessor unitProcessor;
    876 
    877         try
    878         {
    879             unitProcessor = setProcessor.CreateUnitProcessor(unit);
    880         }
    881         catch (...)
    882         {
    883             ExtractUnitResultInformation(std::current_exception(), unitResult);
    884         }
    885 
    886         cancellation.ThrowIfCancelled();
    887 
    888         IGetAllUnitsConfigurationUnitProcessor getAllUnitsUnitProcessor;
    889         IGetAllSettingsConfigurationUnitProcessor getAllSettingsUnitProcessor;
    890 
    891         if (unitProcessor.try_as<IGetAllUnitsConfigurationUnitProcessor>(getAllUnitsUnitProcessor))
    892         {
    893             cancellation.ThrowIfCancelled();
    894 
    895             try
    896             {
    897                 IGetAllUnitsResult allUnitsResult = getAllUnitsUnitProcessor.GetAllUnits();
    898                 result->Units(allUnitsResult.Units());
    899                 result->ResultInformation(allUnitsResult.ResultInformation());
    900             }
    901             catch (...)
    902             {
    903                 ExtractUnitResultInformation(std::current_exception(), unitResult);
    904             }
    905 
    906             m_threadGlobals.GetTelemetryLogger().LogConfigUnitRunIfAppropriate(GUID_NULL, unit, ConfigurationUnitIntent::Inform, TelemetryTraceLogger::ExportAction, result->ResultInformation());
    907         }
    908         else if (unitProcessor.try_as<IGetAllSettingsConfigurationUnitProcessor>(getAllSettingsUnitProcessor))
    909         {
    910             cancellation.ThrowIfCancelled();
    911 
    912             try
    913             {
    914                 IGetAllSettingsResult allSettingsResult = getAllSettingsUnitProcessor.GetAllSettings();
    915 
    916                 auto allSettings = allSettingsResult.Settings();
    917                 if (allSettings)
    918                 {
    919                     std::vector<Configuration::ConfigurationUnit> units;
    920 
    921                     size_t index = 0;
    922                     auto currentType = unit.Type();
    923                     auto currentDetails = unit.Details();
    924 
    925                     for (const auto& settings : allSettings)
    926                     {
    927                         auto newUnit = make_self<implementation::ConfigurationUnit>();
    928 
    929                         newUnit->Type(currentType);
    930                         newUnit->Settings(settings);
    931                         newUnit->Details(currentDetails);
    932 
    933                         std::wostringstream identifierStream;
    934                         identifierStream << static_cast<std::wstring_view>(currentType) << L'-' << index++;
    935                         newUnit->Identifier(hstring{ identifierStream.str() });
    936 
    937                         units.push_back(*newUnit);
    938                     }
    939 
    940                     result->Units(single_threaded_vector(std::move(units)));
    941                 }
    942 
    943                 result->ResultInformation(allSettingsResult.ResultInformation());
    944             }
    945             catch (...)
    946             {
    947                 ExtractUnitResultInformation(std::current_exception(), unitResult);
    948             }
    949 
    950             m_threadGlobals.GetTelemetryLogger().LogConfigUnitRunIfAppropriate(GUID_NULL, unit, ConfigurationUnitIntent::Inform, TelemetryTraceLogger::ExportAction, result->ResultInformation());
    951         }
    952         else
    953         {
    954             AICLI_LOG(Config, Error, << "Unit Processor does not support GetAllUnits or GetAllSettings operation");
    955             unitResult->Initialize(WINGET_CONFIG_ERROR_NOT_SUPPORTED_BY_PROCESSOR, hstring{});
    956         }
    957 
    958         return *result;
    959     }
    960 
    961     Windows::Foundation::Collections::IVector<IConfigurationUnitProcessorDetails> ConfigurationProcessor::FindUnitProcessors(const FindUnitProcessorsOptions& findOptions)
    962     {
    963         THROW_HR_IF(E_NOT_VALID_STATE, !m_factory);
    964         return FindUnitProcessorsImpl(findOptions);
    965     }
    966 
    967     Windows::Foundation::IAsyncOperation<Windows::Foundation::Collections::IVector<IConfigurationUnitProcessorDetails>> ConfigurationProcessor::FindUnitProcessorsAsync(const FindUnitProcessorsOptions& findOptions)
    968     {
    969         THROW_HR_IF(E_NOT_VALID_STATE, !m_factory);
    970 
    971         auto strong_this{ get_strong() };
    972         FindUnitProcessorsOptions localOptions = findOptions;
    973 
    974         co_await winrt::resume_background();
    975 
    976         co_return FindUnitProcessorsImpl(localOptions, { co_await winrt::get_cancellation_token() });
    977     }
    978 
    979     Windows::Foundation::Collections::IVector<IConfigurationUnitProcessorDetails> ConfigurationProcessor::FindUnitProcessorsImpl(
    980         const FindUnitProcessorsOptions& findOptions,
    981         ShutdownAwareAsyncCancellation cancellation)
    982     {
    983         auto threadGlobals = m_threadGlobals.SetForCurrentThread();
    984 
    985         IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(nullptr);
    986 
    987         cancellation.ThrowIfCancelled();
    988 
    989         IFindUnitProcessorsSetProcessor findUnitProcessorsSetProcessor;
    990 
    991         if (setProcessor.try_as<IFindUnitProcessorsSetProcessor>(findUnitProcessorsSetProcessor))
    992         {
    993             return findUnitProcessorsSetProcessor.FindUnitProcessors(findOptions);
    994         }
    995         else
    996         {
    997             AICLI_LOG(Config, Error, << "Set Processor does not support FindUnitProcessors operation");
    998             THROW_HR(WINGET_CONFIG_ERROR_NOT_SUPPORTED_BY_PROCESSOR);
    999         }
   1000     }
   1001 
   1002     Configuration::ApplyConfigurationUnitResult ConfigurationProcessor::ApplyUnit(const ConfigurationUnit& unit)
   1003     {
   1004         THROW_HR_IF(E_NOT_VALID_STATE, !m_factory);
   1005         return ApplyUnitImpl(unit);
   1006     }
   1007 
   1008     Windows::Foundation::IAsyncOperation<Configuration::ApplyConfigurationUnitResult> ConfigurationProcessor::ApplyUnitAsync(const ConfigurationUnit& unit)
   1009     {
   1010         THROW_HR_IF(E_NOT_VALID_STATE, !m_factory);
   1011 
   1012         auto strong_this{ get_strong() };
   1013         ConfigurationUnit localUnit = unit;
   1014 
   1015         co_await winrt::resume_background();
   1016 
   1017         co_return ApplyUnitImpl(localUnit, { co_await winrt::get_cancellation_token() });
   1018     }
   1019 
   1020     Configuration::ApplyConfigurationUnitResult ConfigurationProcessor::ApplyUnitImpl(
   1021         const ConfigurationUnit& unit,
   1022         ShutdownAwareAsyncCancellation cancellation)
   1023     {
   1024         auto threadGlobals = m_threadGlobals.SetForCurrentThread();
   1025 
   1026         IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(nullptr);
   1027         auto result = make_self<wil::details::module_count_wrapper<implementation::ApplyConfigurationUnitResult>>();
   1028         auto unitResult = make_self<wil::details::module_count_wrapper<implementation::ConfigurationUnitResultInformation>>();
   1029         result->Unit(unit);
   1030         result->ResultInformation(*unitResult);
   1031 
   1032         cancellation.ThrowIfCancelled();
   1033 
   1034         IConfigurationUnitProcessor unitProcessor;
   1035 
   1036         try
   1037         {
   1038             unitProcessor = setProcessor.CreateUnitProcessor(unit);
   1039         }
   1040         catch (...)
   1041         {
   1042             ExtractUnitResultInformation(std::current_exception(), unitResult);
   1043         }
   1044 
   1045         cancellation.ThrowIfCancelled();
   1046 
   1047         if (unitProcessor)
   1048         {
   1049             try
   1050             {
   1051                 auto applyResult = unitProcessor.ApplySettings();
   1052                 result->Unit(applyResult.Unit());
   1053                 result->State(Configuration::ConfigurationUnitState::Completed);
   1054                 result->ResultInformation(applyResult.ResultInformation());
   1055                 result->RebootRequired(applyResult.RebootRequired());
   1056             }
   1057             catch (...)
   1058             {
   1059                 ExtractUnitResultInformation(std::current_exception(), unitResult);
   1060             }
   1061 
   1062             m_threadGlobals.GetTelemetryLogger().LogConfigUnitRunIfAppropriate(GUID_NULL, unit, ConfigurationUnitIntent::Apply, TelemetryTraceLogger::ApplyAction, result->ResultInformation());
   1063         }
   1064 
   1065         return *result;
   1066     }
   1067 
   1068     Configuration::TestConfigurationUnitResult ConfigurationProcessor::TestUnit(const ConfigurationUnit& unit)
   1069     {
   1070         THROW_HR_IF(E_NOT_VALID_STATE, !m_factory);
   1071         return TestUnitImpl(unit);
   1072     }
   1073 
   1074     Windows::Foundation::IAsyncOperation<Configuration::TestConfigurationUnitResult> ConfigurationProcessor::TestUnitAsync(const ConfigurationUnit& unit)
   1075     {
   1076         THROW_HR_IF(E_NOT_VALID_STATE, !m_factory);
   1077 
   1078         auto strong_this{ get_strong() };
   1079         ConfigurationUnit localUnit = unit;
   1080 
   1081         co_await winrt::resume_background();
   1082 
   1083         co_return TestUnitImpl(localUnit, { co_await winrt::get_cancellation_token() });
   1084     }
   1085 
   1086     Configuration::TestConfigurationUnitResult ConfigurationProcessor::TestUnitImpl(
   1087         const ConfigurationUnit& unit,
   1088         ShutdownAwareAsyncCancellation cancellation)
   1089     {
   1090         auto threadGlobals = m_threadGlobals.SetForCurrentThread();
   1091 
   1092         IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(nullptr);
   1093         auto result = make_self<wil::details::module_count_wrapper<implementation::TestConfigurationUnitResult>>();
   1094         auto unitResult = make_self<wil::details::module_count_wrapper<implementation::ConfigurationUnitResultInformation>>();
   1095         result->Unit(unit);
   1096         result->ResultInformation(*unitResult);
   1097 
   1098         cancellation.ThrowIfCancelled();
   1099 
   1100         IConfigurationUnitProcessor unitProcessor;
   1101 
   1102         try
   1103         {
   1104             unitProcessor = setProcessor.CreateUnitProcessor(unit);
   1105         }
   1106         catch (...)
   1107         {
   1108             ExtractUnitResultInformation(std::current_exception(), unitResult);
   1109         }
   1110 
   1111         cancellation.ThrowIfCancelled();
   1112 
   1113         if (unitProcessor)
   1114         {
   1115             try
   1116             {
   1117                 auto testResult = unitProcessor.TestSettings();
   1118                 result->Unit(testResult.Unit());
   1119                 result->TestResult(testResult.TestResult());
   1120                 result->ResultInformation(testResult.ResultInformation());
   1121             }
   1122             catch (...)
   1123             {
   1124                 ExtractUnitResultInformation(std::current_exception(), unitResult);
   1125             }
   1126 
   1127             m_threadGlobals.GetTelemetryLogger().LogConfigUnitRunIfAppropriate(GUID_NULL, unit, ConfigurationUnitIntent::Assert, TelemetryTraceLogger::TestAction, result->ResultInformation());
   1128         }
   1129 
   1130         return *result;
   1131     }
   1132 
   1133     IConfigurationGroupProcessor ConfigurationProcessor::GetSetGroupProcessor(const Configuration::ConfigurationSet& configurationSet)
   1134     {
   1135         IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(configurationSet);
   1136 
   1137         IConfigurationGroupProcessor result = setProcessor.try_as<IConfigurationGroupProcessor>();
   1138         if (!result)
   1139         {
   1140             auto groupProcessor = make_self<wil::details::module_count_wrapper<implementation::DefaultSetGroupProcessor>>();
   1141             groupProcessor->Initialize(configurationSet, setProcessor, m_threadGlobals);
   1142             result = *groupProcessor;
   1143         }
   1144 
   1145         return result;
   1146     }
   1147 
   1148     HRESULT STDMETHODCALLTYPE ConfigurationProcessor::SetLifetimeWatcher(IUnknown* watcher)
   1149     {
   1150         return AppInstaller::WinRT::LifetimeWatcherBase::SetLifetimeWatcher(watcher);
   1151     }
   1152 
   1153     void ConfigurationProcessor::ConfigurationSetProcessorFactory(const IConfigurationSetProcessorFactory& value)
   1154     {
   1155         m_factory = value;
   1156 
   1157         if (m_factory)
   1158         {
   1159             m_factoryDiagnosticsEventRevoker = m_factory.Diagnostics(winrt::auto_revoke,
   1160                 [weak_this{ get_weak() }](const IInspectable&, const IDiagnosticInformation& information)
   1161                 {
   1162                     if (auto strong_this{ weak_this.get() })
   1163                     {
   1164                         strong_this->SendDiagnostics(information);
   1165                     }
   1166                 });
   1167         }
   1168     }
   1169 
   1170     void ConfigurationProcessor::SendDiagnostics(DiagnosticLevel level, std::string_view message) try
   1171     {
   1172         if (level >= m_minimumLevel)
   1173         {
   1174             auto diagnostics = make_self<wil::details::module_count_wrapper<implementation::DiagnosticInformationInstance>>();
   1175             diagnostics->Initialize(level, AppInstaller::Utility::ConvertToUTF16(message));
   1176             SendDiagnosticsImpl(*diagnostics);
   1177         }
   1178     }
   1179     // While diagnostics can be important, a failure to send them should not cause additional issues.
   1180     catch (...) {}
   1181 
   1182     void ConfigurationProcessor::SendDiagnostics(const IDiagnosticInformation& information) try
   1183     {
   1184         if (information.Level() >= m_minimumLevel)
   1185         {
   1186             SendDiagnosticsImpl(information);
   1187         }
   1188     }
   1189     // While diagnostics can be important, a failure to send them should not cause additional issues.
   1190     catch (...) {}
   1191 
   1192     void ConfigurationProcessor::SendDiagnosticsImpl(const IDiagnosticInformation& information)
   1193     {
   1194         std::lock_guard<std::recursive_mutex> lock{ m_diagnosticsMutex };
   1195 
   1196         // Prevent a winrt/wil error recursion here by detecting that this thread failed to send a previous message.
   1197         if (m_isHandlingDiagnostics)
   1198         {
   1199             std::wstring debugMessage = L"An error occurred while trying to send a previous diagnostics message:\n";
   1200             debugMessage.append(information.Message());
   1201             OutputDebugStringW(debugMessage.c_str());
   1202             return;
   1203         }
   1204 
   1205         m_isHandlingDiagnostics = true;
   1206         auto notHandling = wil::scope_exit([&] { m_isHandlingDiagnostics = false; });
   1207 
   1208         m_diagnostics(*this, information);
   1209     }
   1210 }