winget-cli

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

commit 6e43f9134e032930404e4f3ddb5ede30fb28c612
parent b0e68f1ff08d5d63d58dd779f5a87121b684ddb3
Author: JohnMcPMS <johnmcp@microsoft.com>
Date:   Tue, 14 Feb 2023 20:37:26 -0800

Config code (#2942)

Introduces a new component, `Microsoft.Management.Configuration`.  This is the API surface for controlling the configuration of the system for a user (with the intention that any user can run it, including `SYSTEM`).  There are [extensive details on the API in this document](/JohnMcPMS/winget-cli/blob/config-code/doc/specs/Configuration-COM-API.md), so I will not repeat them here.

The API is not fully implemented by this change, only the baseline functionality for reading configuration files and acting on them immediately.  None of the history functionality is present, nor any ability to serialize files.  However, this should be sufficient to create an initial, golden path implementation of user interactive surfaces.
Diffstat:
M.github/actions/spelling/allow.txt | 3+++
Mazure-pipelines.yml | 10++++++++++
Adoc/specs/Configuration-COM-API.md | 806+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/AppInstallerCLI.sln | 454++++++++++++++-----------------------------------------------------------------
Msrc/AppInstallerCLITests/Versions.cpp | 35+++++++++++++++++++++++++++++++++--
Msrc/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj | 2--
Msrc/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters | 6------
Dsrc/AppInstallerCommonCore/Public/AppInstallerVersions.h | 243-------------------------------------------------------------------------------
Dsrc/AppInstallerCommonCore/Versions.cpp | 559-------------------------------------------------------------------------------
Msrc/AppInstallerSharedLib/AppInstallerLogging.cpp | 17+++++++++--------
Msrc/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj | 14++++++++++++++
Msrc/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj.filters | 6++++++
Msrc/AppInstallerSharedLib/Public/AppInstallerErrors.h | 12++++++++++++
Msrc/AppInstallerSharedLib/Public/AppInstallerLogging.h | 1+
Msrc/AppInstallerSharedLib/Public/AppInstallerSHA256.h | 2+-
Asrc/AppInstallerSharedLib/Public/AppInstallerVersions.h | 269+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/AppInstallerSharedLib/Public/winget/SharedThreadGlobals.h | 2+-
Msrc/AppInstallerSharedLib/Public/winget/Yaml.h | 2++
Asrc/AppInstallerSharedLib/Versions.cpp | 632+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/AppInstallerSharedLib/Yaml.cpp | 5+++++
Msrc/JsonCppLib/JsonCppLib.vcxproj | 20++++++++++++++++----
Asrc/Microsoft.Management.Configuration.Projection/Microsoft.Management.Configuration.Projection.csproj | 36++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration.UnitTests/Fixtures/UnitTestCollection.cs | 21+++++++++++++++++++++
Asrc/Microsoft.Management.Configuration.UnitTests/Fixtures/UnitTestFixture.cs | 30++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration.UnitTests/Helpers/ConfigurationProcessorTestBase.cs | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration.UnitTests/Helpers/DiagnosticsEventSink.cs | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration.UnitTests/Helpers/Errors.cs | 33+++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationProcessorFactory.cs | 113+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationSetProcessor.cs | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationUnitProcessor.cs | 146+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationUnitProcessorDetails.cs | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration.UnitTests/Microsoft.Management.Configuration.UnitTests.csproj | 44++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationProcessorApplyTests.cs | 439+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationProcessorTestTests.cs | 285+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationSetAuthoringTests.cs | 111+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration.UnitTests/Tests/OpenConfigurationSetTests.cs | 283+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration.UnitTests/Tests/ProcessorGetTests.cs | 189+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ApplyConfigurationSetResult.cpp | 32++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ApplyConfigurationSetResult.h | 29+++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ApplyConfigurationUnitResult.cpp | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ApplyConfigurationUnitResult.h | 39+++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ApplySettingsResult.cpp | 29+++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ApplySettingsResult.h | 28++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ConfigThreadGlobals.cpp | 17+++++++++++++++++
Asrc/Microsoft.Management.Configuration/ConfigThreadGlobals.h | 21+++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ConfigurationChangeData.cpp | 30++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ConfigurationChangeData.h | 27+++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ConfigurationConflict.cpp | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ConfigurationConflict.h | 34++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ConfigurationConflictSetting.cpp | 30++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ConfigurationConflictSetting.h | 27+++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ConfigurationProcessor.cpp | 397+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ConfigurationProcessor.h | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ConfigurationSet.cpp | 118+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ConfigurationSet.h | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ConfigurationSetApplyProcessor.cpp | 447+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ConfigurationSetApplyProcessor.h | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ConfigurationSetChangeData.cpp | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ConfigurationSetChangeData.h | 38++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ConfigurationSetParser.cpp | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ConfigurationSetParser.h | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ConfigurationSetParserError.h | 19+++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ConfigurationSetParser_0_1.cpp | 207+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ConfigurationSetParser_0_1.h | 33+++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ConfigurationUnit.cpp | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ConfigurationUnit.h | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ConfigurationUnitResultInformation.cpp | 46++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ConfigurationUnitResultInformation.h | 30++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/DiagnosticInformation.cpp | 24++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/DiagnosticInformation.h | 25+++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/ExceptionResultHelpers.h | 27+++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/GetConfigurationUnitSettingsResult.cpp | 34++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/GetConfigurationUnitSettingsResult.h | 29+++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/GetSettingsResult.cpp | 29+++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/GetSettingsResult.h | 28++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/Microsoft.Management.Configuration.idl | 616+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj | 224+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj.filters | 177+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/Microsoft_Management_Configuration.def | 3+++
Asrc/Microsoft.Management.Configuration/MutableFlag.cpp | 12++++++++++++
Asrc/Microsoft.Management.Configuration/MutableFlag.h | 22++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/OpenConfigurationSetResult.cpp | 34++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/OpenConfigurationSetResult.h | 29+++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/PropertySheet.props | 17+++++++++++++++++
Asrc/Microsoft.Management.Configuration/TestConfigurationSetResult.cpp | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/TestConfigurationSetResult.h | 27+++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/TestConfigurationUnitResult.cpp | 39+++++++++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/TestConfigurationUnitResult.h | 32++++++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/TestSettingsResult.cpp | 29+++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/TestSettingsResult.h | 28++++++++++++++++++++++++++++
Asrc/Microsoft.Management.Configuration/packages.config | 6++++++
Asrc/Microsoft.Management.Configuration/pch.cpp | 3+++
Asrc/Microsoft.Management.Configuration/pch.h | 31+++++++++++++++++++++++++++++++
Msrc/YamlCppLib/YamlCppLib.vcxproj | 12++++++++++++
94 files changed, 7960 insertions(+), 1202 deletions(-)

diff --git a/.github/actions/spelling/allow.txt b/.github/actions/spelling/allow.txt @@ -133,6 +133,7 @@ dll dllexport dllmain Dlls +DMC docx dotnet downlevel @@ -228,6 +229,7 @@ https HWND Hyperlink IAppx +IConfiguration icu IDX IFACEMETHOD @@ -235,6 +237,7 @@ ifdef ifndef ifspec ifstream +IInspectable IIS impl inapplicabilities diff --git a/azure-pipelines.yml b/azure-pipelines.yml @@ -290,6 +290,16 @@ jobs: codeCoverageEnabled: true platform: 'Any CPU' configuration: '$(BuildConfiguration)' + + - task: VSTest@2 + displayName: 'Run tests: Microsoft.Management.Configuration.UnitTests' + inputs: + testSelector: 'testAssemblies' + testAssemblyVer2: '**\Microsoft.Management.Configuration.UnitTests.dll' + searchFolder: '$(buildOutDir)\Microsoft.Management.Configuration.UnitTests' + codeCoverageEnabled: true + platform: '$(buildPlatform)' + configuration: '$(BuildConfiguration)' - task: CopyFiles@2 displayName: 'Copy Util to artifacts folder' diff --git a/doc/specs/Configuration-COM-API.md b/doc/specs/Configuration-COM-API.md @@ -0,0 +1,805 @@ +<!-- TEMPLATE + The purpose of this spec is to describe new APIs, in a way + that will transfer to docs.microsoft.com (DMC). + + There are two audiences for the spec. The first are people that want to evaluate and + give feedback on the API, as part of the submission process. + When it's complete it will be incorporated into the public documentation at + http://docs.microsoft.com (DMC). + Hopefully we'll be able to copy it mostly verbatim. So the second audience is + everyone that reads there to learn how and why to use this API. + Some of this text also shows up in Visual Studio Intellisense. + + For example, much of the examples and descriptions in the `RadialGradientBrush` API spec + (https://github.com/microsoft/microsoft-ui-xaml-specs/blob/master/active/RadialGradientBrush/RadialGradientBrush.md) + were carried over to the public API page on DMC + (https://docs.microsoft.com/windows/winui/api/microsoft.ui.xaml.media.radialgradientbrush?view=winui-2.5) + + Once the API is on DMC, that becomes the official copy, and this spec becomes an archive. + For example if the description is updated, that only needs to happen on DMC and needn't + be duplicated here. + + Examples: + * New class (RadialGradientBrush): + https://github.com/microsoft/microsoft-ui-xaml-specs/blob/master/active/RadialGradientBrush/RadialGradientBrush.md + * New member on an existing class (UIElement.ProtectedCursor): + https://github.com/microsoft/microsoft-ui-xaml-specs/blob/master/active/UIElement/ElementCursor.md + + Style guide: + * Use second person; speak to the developer who will be learning/using this API. + (For example "you use this to..." rather than "the developer uses this to...") + * Use hard returns to keep the page width within ~100 columns. + (Otherwise it's more difficult to leave comments in a GitHub PR.) + * Talk about an API's behavior, not its implementation. + (Speak to the developer using this API, not to the team implementing it.) + * A picture is worth a thousand words. + * An example is worth a million words. + * Keep examples realistic but simple; don't add unrelated complications. + (An example that passes a stream needn't show the process of launching the File-Open dialog.) + +--> + +Microsoft.Management.Configuration API +=== + +# Background +This API is being added to enable the Developer+ configuration scenarios. It enables interacting with +configuration sets in three contexts: + +1. Loading an existing configuration set from a stream +2. Loading previously applied configuration sets from the local history +3. Authoring a new/editing an existing configuration + +These configuration sets are composed of configuration units, which describe the individual configurable +items and the values to configure. + +Configuration actions consist of: + +1. Test :: Determining whether the system state matches the described state +2. Get :: Extracting the current system state with respect to the configuration scope +3. Set :: Applying the described state to the system + +This API is also intended to support multiple processes watching for state changes, both for the +configuration set lifetimes and the individual configuration unit states. + +# Conceptual pages (How To) + +_(Add conceptual documentation that will go to docs.microsoft.com "how to" page if needed)_ + +# API Pages + +<!-- TEMPLATE + + Each of the L2 sections in this "API Pages" section corresponds to a page on DMC. + + It's not necessary to have a section for every class member though: + * If its purpose and usage is obvious from it's name/type, it's not necessary to + create a section for it. + * If its purpose and usage is fully explained by brief description, either + put it in a table in the "Other [class] members" section + put it with /// comments in the IDL section + + Create an L2 section here for each API that needs more description or examples. + For a new class with members, the members should go in their own L2 section. + + Example layout + ## MyClass + ## MyClass.Member1 + ## MyClass.Member2 + ## Other MyClass members + ## MyOtherClass + ## ... + + Notes: + * The first line of each of these sections should become that first line on the DMC page, + which then becomes the description you see in Intellisense. + * Each page can have description, examples, and remarks. + Remarks are where the documentation calls out special considerations that the developer + should be aware of. + * It can be helpful at the top of an API page (or after the Intellisense text) to add the + API signature in C# + * Add a "_Spec note: ..._" to add a note that's useful in this spec but shouldn't go to DMC. + * Show _examples_, not _samples_; an example is a snippet, a sample is a full working app. + +--> + +## ConfigurationSetState enumeration + +The state of a configuration set in the configuration history. + +| Name | Description | +|-|-| +| Unknown | Primarily used for a configuration set that has not been applied. | +| Pending | The configuration set has been recorded into the history, but has not yet begun applying. | +| InProgress | The configuration set has begun being applied to the system. | +| Completed | The configuration set has completed being applied. | + +## ConfigurationUnitState enumeration + +The state of a configuration unit in the configuration history. + +| Name | Description | +|-|-| +| Unknown | Primarily used for a configuration unit that has not been applied. | +| Pending | The configuration unit has been recorded into the history, but has not yet begun applying. | +| InProgress | The configuration unit has begun being applied to the system. | +| Completed | The configuration unit has completed being applied; the result information will contain additional details. | +| Skipped | The configuration unit was skipped; the result information will contain additional details on the reason. | + +## ConfigurationUnitDetailLevel enumeration + +Defines the level of detail probing that is allowed about a configuration unit. + +| Name | Description | +|-|-| +| Local | Only reads details from local data. | +| Catalog | Will query the catalog information for details, but will not download any modules. | +| Download | Will download modules, but not load them. | +| Load | Will download and load modules for details. | + +## ConfigurationUnitResultInformation class + +Information on a result for a single unit of configuration. + +The class is used both in reporting results through the `ConfigurationSet.ConfigurationSetChange` event as they occur +and in viewing past results via the `ConfigurationUnit.ResultInformation` property on a historical record. + +## IConfigurationUnitSettingDetails interface + +Provides information for a specific configuration unit setting. + +The properties on this interface are useful for creating a rich authoring experience. + +## IConfigurationUnitSettingDetails.Semantics schema + +> _TODO: Define the meaning/schema for this value_ + +## IConfigurationUnitProcessorDetails interface + +Provides information for a specific configuration unit within the runtime. + +The properties on this interface are useful for informing the user about the provenance of the code that is +responsible for processing the configuration unit. + +## ConfigurationUnit class + +A single unit of configuration. + +Represents the smallest actionable configuration element. + +## ConfigurationUnit constructor + +Creates an empty configuration unit for authoring purposes. + +```C# +ConfigurationUnit(); +``` + +## ConfigurationUnit properties + +| Name | Description | +|-|-| +| UnitName | The name of the unit being configured; not a name for this instance. | +| InstanceIdentifier | An identifier used to uniquely identify the instance of a configuration unit on the system. | +| Identifier | The identifier name of this instance within the set. This value is referenced by other unit's `Dependencies`. | +| Dependencies | The identifiers of the configuration units that this unit depends on. | +| Directives | Contains the values that are for use by the configuration system, related to this unit. | +| Settings | Contains the values that are for use by the configuration unit itself. | +| Details | Contains information on the origin of the configuration unit. You must call `ConfigurationProcessor.Get*DetailsAsync` to populate this value. | +| State | The current state of the configuration unit. | +| ResultInformation | Contains information on the result of the latest attempt to apply the configuration unit. | +| ShouldApply | Allows for control over whether this unit should be applied when the set containing it is applied. | + +## ConfigurationUnit.Directives known values + +> _TODO: List of well known directives_ + +## ConfigurationSetChangeEventType enumeration + +The change event type that has occurred for a configuration set change. + +| Name | Description | +|-|-| +| Unknown | For future use if the caller is not aware of newer change types. | +| SetStateChanged | The state of the configuration set has changed. | +| UnitStateChanged | The state of a configuration unit has changed. | + +## ConfigurationSetChangeData class + +The change data sent about changes to a specific set. + +This class is sent to subscribers of the `ConfigurationSet.ConfigurationSetChange` event, containing information +about the specific change that occurred. + +## ConfigurationSet class + +A configuration set contains a collection of configuration units and details about the set. + +Represents a self contained group of configuration units that are operated on together. + +## ConfigurationSet constructors + +Creates an empty configuration set for authoring purposes. + +```C# +ConfigurationSet(); +``` + +Loads a configuration set from the given stream. + +```C# +ConfigurationSet(Windows.Storage.Streams.IInputStream stream); +``` + +## ConfigurationSet.ConfigurationSetChange event + +State changes for this set and it's units are sent to subscribers of this event. + +```C# +event Windows.Foundation.TypedEventHandler<ConfigurationSet, ConfigurationSetChangeData> ConfigurationSetChange; +``` + +## ConfigurationSet.Serialize method + +Serializes the configuration set to the given output stream. + +```C# +void Serialize(Windows.Storage.Streams.IOutputStream stream); +``` + +## ConfigurationSet.Remove method + +Removes the configuration set from the recorded history, if present. + +```C# +void Remove(); +``` + +You can use this method to remove a configuration set that is no longer relevant. For example, it may have been for +a repository that is no longer being used by the user and future conflicts with it's configuration are not important. + +## ConfigurationSet properties + +| Name | Description | +|-|-| +| Name | The name of the set; if from a file this could be the file name. | +| Origin | The origin of the set; if it came from a repository it could be the remote URL (ex. https://github.com/microsoft/winget-cli.git). | +| InstanceIdentifier | An identifier used to uniquely identify the instance of a configuration set on the system. | +| State | The state that the set is in. | +| InitialIntent | The time that this set was recorded with intent to apply. | +| ApplyBegun | The time that this set was last started to be applied. | +| ApplyEnded | The time that this set was last finished being applied (does not indicate success). | +| ConfigurationUnits | The configuration units that are part of this set. | + +## IConfigurationUnitProcessor interface + +Provides access to a specific configuration unit within the runtime. + +This interface is the primary mechanism used to actually read and write configuration to the system, +but it is not expected that you would use this directly as a consumer of Microsoft.Management.Configuration. + +## IConfigurationSetProcessor interface + +Contains the lifetime of the processing action for a configuration set. + +This interface is used to contain the lifetime of a processing action, +but it is not expected that you would use this directly as a consumer of Microsoft.Management.Configuration. + +## IConfigurationProcessorFactory interface + +Allows different runtimes to provide specialized handling of configuration processing. + +It is not expected that you would use this interface directly, but rather the `ConfigurationProcessor` class. + +_Spec note: A separate binary (written by us) will contain the implementation(s) of this interface._ + +## DiagnosticLevel enumeration + +Indicates the importance of diagnostic information. + +| Name | Description | +|-|-| +| Verbose | Most useful for debugging scenarios; likely too much for general use. | +| Informational | Details that can be useful for understanding what is happening. | +| Warning | Indicates some abnormal condition, but that is not expected to impact functionality. | +| Error | An error has occurred, but this does not necessarily mean that it will halt the operation. | +| Critical | A serious, fatal condition has been encountered. | + +## DiagnosticInformation class + +Contains diagnostic information from the configuration system that can be passed along to the user or log files. +This is not intended as primary information, and is thus not localized. + +## ConfigurationConflictType enumeration + +The type of conflict between configuration sets that was detected. + +| Name | Description | +|-|-| +| Unknown | For future use if the caller is not aware of newer conflict types. | +| MatchingOrigin | Indicates that the first configuration set has a matching name and origin to the second, which has already been applied. | +| IdenticalSetApplied | Indicates that the first configuration set is identical to the second, which has already been applied. | +| SettingsConflict | Indicates a conflict between the settings of two configuration units. | + +## ConfigurationConflictSetting class + +Describes a conflict between a setting of two configuration units. + +## ConfigurationConflict class + +Describes a conflict between two configuration sets. + +## ApplyConfigurationSetFlags enumeration + +Flags to control how a configuration set should be applied to the system. + +| Name | Description | +|-|-| +| None | The configuration set should be applied in the default manner. | +| DoNotOverwriteMatchingOriginSet | Forces a new configuration set instance to be recorded when the set being applied matches a previous set's origin. The default behavior is to assume that the incoming set is an update to the existing set and overwrite it. | + +## ConfigurationChangeEventType enumeration + +The configuration set change event type that has occurred. + +| Name | Description | +|-|-| +| Unknown | For future use if the caller is not aware of newer change types. | +| SetAdded | A new configuration set was recorded in the history with the intent to be applied. | +| SetStateChanged | A configuration set has changed state. | +| SetRemoved | A configuration set has been removed from the history. | + +## ConfigurationChangeEventType class + +The change data sent about changes to sets. + +## ConfigurationProcessor class + +The configuration processor is responsible for the interactions with the system. + +You must use this class to do anything beyond reading configuration sets. It is the entrypoint for all actions that +will interact with the actual system configuration. + +## ConfigurationProcessor constructor + +Creates a configuration processor using the given configuration factory. + +```C# +ConfigurationProcessor(IConfigurationProcessorFactory factory); +``` + +> _TODO: Add details on the mechanics of creating the `IConfigurationProcessorFactory` objects that we provide._ + +## ConfigurationProcessor.CheckForConflicts(Async) method + +Checks for conflicts amongst the configuration sets provided, optionally including the configuration sets already applied to the system. + +```C# +Windows.Foundation.Collections.IVectorView<ConfigurationConflict> CheckForConflicts(Windows.Foundation.Collections.IVectorView<ConfigurationSet> configurationSets, Boolean includeConfigurationHistory); + +Windows.Foundation.IAsyncOperation< Windows.Foundation.Collections.IVectorView<ConfigurationConflict> > CheckForConflictsAsync(Windows.Foundation.Collections.IVectorView<ConfigurationSet> configurationSets, Boolean includeConfigurationHistory); +``` + +This method should be used on any configuration set that is opened in order to determine if it would cause a conflict +with previously applied configurations. It should be called *after* setting the `Name` and `Origin` in order to determine +if it is a potential update. + +## ConfigurationProcessor.GetSetDetails(Async) method + +Gets the details for all configuration units in a set. + +```C# +void GetSetDetails(ConfigurationSet configurationSet, ConfigurationUnitDetailLevel detailLevel); + +Windows.Foundation.IAsyncAction GetSetDetailsAsync(ConfigurationSet configurationSet, ConfigurationUnitDetailLevel detailLevel); +``` + +This is a convenience/optimization method that will do the same thing as calling `GetUnitDetails(Async)` on each +configuration unit in the set. See `GetUnitDetails(Async)` for more information on what it will do. + +## ConfigurationProcessor.GetUnitDetails(Async) method + +Gets the details for all configuration units in a set. + +```C# +void GetUnitDetails(ConfigurationUnit unit, ConfigurationUnitDetailLevel detailLevel); + +Windows.Foundation.IAsyncAction GetUnitDetailsAsync(ConfigurationUnit unit, ConfigurationUnitDetailLevel detailLevel); +``` + +This method will get the details about a specific configuration unit and make them available via `ConfigurationUnit.Details`. +The `detailLevel` parameter allows control over how deeply to probe for details. It is an analog for the amount of +trust to place in the configuration unit processor. + +## ConfigurationProcessor.ApplySet(Async) method + +Applies the configuration set state to the system. + +```C# +ApplyConfigurationSetResult ApplySet(ConfigurationSet configurationSet, ApplyConfigurationSetFlags flags); + +Windows.Foundation.IAsyncOperationWithProgress<ApplyConfigurationSetResult, ConfigurationSetChangeData> ApplySetAsync(ConfigurationSet configurationSet, ApplyConfigurationSetFlags flags); +``` + +Using the async method and it's progress is more efficient than subscribing to the `ConfigurationSetChange` event before calling this method. + +## ConfigurationProcessor.TestSet(Async) method + +Tests if the system state matches the state described by the configuration set. + +```C# +TestConfigurationSetResult TestSet(ConfigurationSet configurationSet); + +Windows.Foundation.IAsyncOperationWithProgress<TestConfigurationSetResult, TestConfigurationUnitResult> TestSetAsync(ConfigurationSet configurationSet); +``` + +## ConfigurationProcessor.GetSettings(Async) method + +Gets the current configuration unit settings from the system state. + +```C# +GetConfigurationUnitSettingsResult GetSettings(ConfigurationUnit unit); + +Windows.Foundation.IAsyncOperation<GetConfigurationUnitSettingsResult> GetSettingsAsync(ConfigurationUnit unit); +``` + +## ConfigurationProcessor.Diagnostics event + +Enables listening to internal diagnostics events for logging purposes. + +## ConfigurationProcessor.ConfigurationChange event + +Signals changes to the set of configuration sets in the history, as well as changes to the state of configuration sets in the history. + +## ConfigurationProcessor.GetConfigurationHistory method + +Gets the configuration sets from the recorded history. + +```C# +Windows.Foundation.Collections.IVectorView<ConfigurationSet> GetConfigurationHistory(); +``` + +Gets the configuration sets that have already been applied or those recorded with the intent to be applied. This may include in progress sets or those that are waiting to be applied. + +# API Details + +[Link to the MIDL3 file.](../../src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.idl) + +# Appendix + +<!-- TEMPLATE + Anything else that you want to write down about implementation notes and for posterity, + but that isn't necessary to understand the purpose and usage of the API. + + This or the Background section are a good place to describe alternative designs + and why they were rejected. +--> + +# Sample + +This sample illustrates some of the expected usage patterns. + +```C# +using Microsoft.Management.Configuration; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.Storage; +using Windows.Storage.Streams; + +namespace ConfigurationSample +{ + internal static class Helpers + { + internal static IConfigurationProcessorFactory CreateIConfigurationProcessorFactory() + { + throw new NotImplementedException(); + } + + internal static ConfigurationSet OpenConfigurationSet(string filePath, ConfigurationProcessor processor) + { + var fileOperation = FileRandomAccessStream.OpenAsync(filePath, FileAccessMode.Read); + fileOperation.AsTask().Wait(); + var file = fileOperation.GetResults(); + OpenConfigurationSetResult result = processor.OpenConfigurationSet(file); + + if (result.Set != null) + { + return result.Set; + } + + Console.WriteLine($"Failed opening configuration set: 0x{result.ResultCode:X} at {result.Field}"); + return null; + } + + internal static void SetWatcher(ConfigurationSet set, ConfigurationSetChangeData data) + { + Console.WriteLine($" - Set: {set.Name} [{set.InstanceIdentifier}]"); + Console.WriteLine($" Change: {data.Change}"); + Console.WriteLine($" Set State: {data.SetState}"); + switch (data.Change) + { + case ConfigurationSetChangeEventType.UnitStateChanged: + Console.WriteLine($" Unit: {data.Unit.UnitName} [{data.Unit.InstanceIdentifier}]"); + Console.WriteLine($" Unit State: {data.UnitState}"); + if (data.UnitState == ConfigurationUnitState.Completed && data.ResultInformation.ResultCode != null) + { + Console.WriteLine($" Failure: {data.ResultInformation.Description} [{data.ResultInformation.ResultCode.HResult}]"); + } + break; + } + } + } + + internal class ApplyProgressWatcher + { + private bool isFirstProgress = true; + + internal void Watcher(IAsyncOperationWithProgress<ApplyConfigurationSetResult, ConfigurationSetChangeData> operation, ConfigurationSetChangeData data) + { + if (isFirstProgress) + { + isFirstProgress = false; + + // If our first progress callback contains partial results, output them as if they had been called through progress + ApplyConfigurationSetResult partialResult = operation.GetResults(); + + foreach (ApplyConfigurationUnitResult unitResult in partialResult.UnitResults) + { + HandleUnitProgress(unitResult.Unit, unitResult.State, unitResult.ResultInformation); + } + } + + switch (data.Change) + { + case ConfigurationSetChangeEventType.SetStateChanged: + Console.WriteLine($" - Set State: {data.SetState}"); + break; + case ConfigurationSetChangeEventType.UnitStateChanged: + HandleUnitProgress(data.Unit, data.UnitState, data.ResultInformation); + break; + } + } + + private void HandleUnitProgress(ConfigurationUnit unit, ConfigurationUnitState state, ConfigurationUnitResultInformation resultInformation) + { + switch (state) + { + case ConfigurationUnitState.Pending: + break; + case ConfigurationUnitState.InProgress: + case ConfigurationUnitState.Completed: + case ConfigurationUnitState.Skipped: + Console.WriteLine($" - Unit: {unit.UnitName} [{unit.InstanceIdentifier}]"); + Console.WriteLine($" Unit State: {state}"); + if (resultInformation.ResultCode != null) + { + Console.WriteLine($" HRESULT: [0x{resultInformation.ResultCode.HResult:X8}]"); + Console.WriteLine($" Reason: {resultInformation.Description}"); + } + break; + case ConfigurationUnitState.Unknown: + break; + } + } + } + + internal class Program + { + static void LoadAndOutput(string[] args) + { + ConfigurationProcessor processor = new ConfigurationProcessor(Helpers.CreateIConfigurationProcessorFactory()); + + // Open the given configuration file + ConfigurationSet configSet = Helpers.OpenConfigurationSet(args[1], processor); + if (configSet == null) + { + return; + } + + // Output some of the information from the set + Console.WriteLine($"Configuration Set: {args[1]}"); + + foreach (ConfigurationUnit unit in configSet.ConfigurationUnits) + { + Console.WriteLine($" - Configuration Unit: {unit.UnitName}"); + if (!string.IsNullOrEmpty(unit.Identifier)) + { + Console.WriteLine($" Identifier: {unit.Identifier}"); + } + Console.WriteLine($" Intent: {unit.Intent}"); + IReadOnlyList<string> dependencies = unit.Dependencies; + if (dependencies.Count > 0) + { + Console.WriteLine(" Dependencies:"); + foreach (string dependency in dependencies) + { + Console.WriteLine($" {dependency}"); + } + } + ValueSet directives = unit.Directives; + if (directives.Count > 0) + { + Console.WriteLine(" Directives:"); + foreach (var directive in unit.Directives) + { + Console.WriteLine($" {directive.Key}: {directive.Value}"); + } + } + ValueSet settings = unit.Settings; + if (settings.Count > 0) + { + Console.WriteLine(" Settings:"); + foreach (var setting in unit.Settings) + { + Console.WriteLine($" {setting.Key}: {setting.Value}"); + } + } + } + } + + static void LoadAndCheckConflicts(string[] args) + { + // Create the factory and processor + ConfigurationProcessor processor = new ConfigurationProcessor(Helpers.CreateIConfigurationProcessorFactory()); + + // Open the given configuration file + ConfigurationSet configSet = Helpers.OpenConfigurationSet(args[1], processor); + if (configSet == null) + { + return; + } + + // Set a name and origin for this set so that we can see it in the conflict info + configSet.Name = Path.GetFileName(args[1]); + configSet.Origin = args[1]; + + // Check for conflicts with existing configurations + List<ConfigurationSet> configSets = new List<ConfigurationSet>() { configSet }; + IList<ConfigurationConflict> conflicts = processor.CheckForConflicts(configSets, true); + + Console.WriteLine($"Conflicts with Configuration Set: {args[1]}"); + + foreach (ConfigurationConflict conflict in conflicts) + { + Console.WriteLine($" - Conflict: {conflict.Conflict}"); + Console.WriteLine($" First Set: {conflict.FirstSet.Name} [{conflict.FirstSet.Origin}]"); + Console.WriteLine($" Second Set: {conflict.SecondSet.Name} [{conflict.SecondSet.Origin}]"); + if (conflict.Conflict == ConfigurationConflictType.SettingsConflict) + { + Console.WriteLine($" First Unit: {conflict.FirstUnit.UnitName} [{conflict.FirstUnit.InstanceIdentifier}]"); + Console.WriteLine($" Second Unit: {conflict.SecondUnit.UnitName} [{conflict.SecondUnit.InstanceIdentifier}]"); + foreach (ConfigurationConflictSetting setting in conflict.Settings) + { + Console.WriteLine($" - Setting: {setting.Name}"); + Console.WriteLine($" First Value: {setting.FirstValue}"); + Console.WriteLine($" Second Value: {setting.SecondValue}"); + } + } + } + } + + static void LoadAndApply(string[] args) + { + // Create the factory and processor + ConfigurationProcessor processor = new ConfigurationProcessor(Helpers.CreateIConfigurationProcessorFactory()); + + // Open the given configuration file + ConfigurationSet configSet = Helpers.OpenConfigurationSet(args[1], processor); + if (configSet == null) + { + return; + } + + Console.WriteLine($"Applying Configuration Set: {args[1]}"); + + ApplyProgressWatcher watcher = new ApplyProgressWatcher(); + + var operation = processor.ApplySetAsync(configSet, ApplyConfigurationSetFlags.None); + operation.Progress = watcher.Watcher; + operation.AsTask().Wait(); + ApplyConfigurationSetResult result = operation.GetResults(); + + Console.WriteLine($" - Done: {result.ResultCode.HResult}"); + } + + static void GetHistoryAndWatchEverything(string[] args) + { + Console.WriteLine("Watching all configuration [press Enter to stop]:"); + + // Create the factory and processor + ConfigurationProcessor processor = new ConfigurationProcessor(Helpers.CreateIConfigurationProcessorFactory()); + + List<ConfigurationSet> list = new List<ConfigurationSet>(); + + // Attach to the top level change event + processor.ConfigurationChange += (ConfigurationSet incomingSet, ConfigurationChangeData data) => + { + int existingSetIndex = -1; + + lock (list) + { + for (int i = 0; i < list.Count; ++i) + { + if (list[i].InstanceIdentifier == data.InstanceIdentifier) + { + existingSetIndex = i; + break; + } + } + + if (data.Change == ConfigurationChangeEventType.SetAdded || data.Change == ConfigurationChangeEventType.SetStateChanged) + { + if (existingSetIndex == -1) + { + incomingSet.ConfigurationSetChange += Helpers.SetWatcher; + list.Add(incomingSet); + } + } + else // Removed + { + if (existingSetIndex != -1) + { + list[existingSetIndex].ConfigurationSetChange -= Helpers.SetWatcher; + list.RemoveAt(existingSetIndex); + } + } + } + + Console.WriteLine($" - Set: {data.InstanceIdentifier}"); + Console.WriteLine($" Change: {data.Change}"); + }; + + foreach (ConfigurationSet set in processor.GetConfigurationHistory()) + { + int existingSetIndex = -1; + + lock (list) + { + for (int i = 0; i < list.Count; ++i) + { + if (list[i].InstanceIdentifier == set.InstanceIdentifier) + { + existingSetIndex = i; + break; + } + } + + if (existingSetIndex == -1) + { + set.ConfigurationSetChange += Helpers.SetWatcher; + list.Add(set); + } + } + + if (existingSetIndex == -1) + { + Console.WriteLine($" - Set: {set.Name} [{set.InstanceIdentifier}]"); + Console.WriteLine($" State: {set.State}"); + } + } + + // Wait for user to press enter + Console.ReadLine(); + } + + static void Main(string[] args) + { + var method = typeof(Program).GetMethod(args[0], BindingFlags.NonPublic | BindingFlags.Static); + + if (method != null) + { + method.Invoke(null, new object[]{ args }); + } + else + { + Console.WriteLine($"{args[0]} is not a sample"); + } + } + } +} + +```+ \ No newline at end of file diff --git a/src/AppInstallerCLI.sln b/src/AppInstallerCLI.sln @@ -149,36 +149,28 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PureLib", "PureLib\PureLib. EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AppInstallerSharedLib", "AppInstallerSharedLib\AppInstallerSharedLib.vcxproj", "{F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Management.Configuration", "Microsoft.Management.Configuration\Microsoft.Management.Configuration.vcxproj", "{CA460806-5E41-4E97-9A3D-1D74B433B663}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Management.Configuration.Projection", "Microsoft.Management.Configuration.Projection\Microsoft.Management.Configuration.Projection.csproj", "{E8454BF1-2068-4513-A525-ABF55CC8742C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Management.Configuration.UnitTests", "Microsoft.Management.Configuration.UnitTests\Microsoft.Management.Configuration.UnitTests.csproj", "{EE43C990-7789-4A60-B077-BF0ED3D093A1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|ARM = Debug|ARM Debug|ARM64 = Debug|ARM64 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 - Fuzzing|Any CPU = Fuzzing|Any CPU - Fuzzing|ARM = Fuzzing|ARM Fuzzing|ARM64 = Fuzzing|ARM64 Fuzzing|x64 = Fuzzing|x64 Fuzzing|x86 = Fuzzing|x86 - Release|Any CPU = Release|Any CPU - Release|ARM = Release|ARM Release|ARM64 = Release|ARM64 Release|x64 = Release|x64 Release|x86 = Release|x86 - TestRelease|Any CPU = TestRelease|Any CPU - TestRelease|ARM = TestRelease|ARM TestRelease|ARM64 = TestRelease|ARM64 TestRelease|x64 = TestRelease|x64 TestRelease|x86 = TestRelease|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6AA3791A-0713-4548-A357-87A323E7AC3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6AA3791A-0713-4548-A357-87A323E7AC3A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6AA3791A-0713-4548-A357-87A323E7AC3A}.Debug|Any CPU.Deploy.0 = Debug|Any CPU - {6AA3791A-0713-4548-A357-87A323E7AC3A}.Debug|ARM.ActiveCfg = Debug|ARM - {6AA3791A-0713-4548-A357-87A323E7AC3A}.Debug|ARM.Build.0 = Debug|ARM - {6AA3791A-0713-4548-A357-87A323E7AC3A}.Debug|ARM.Deploy.0 = Debug|ARM {6AA3791A-0713-4548-A357-87A323E7AC3A}.Debug|ARM64.ActiveCfg = Debug|ARM64 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Debug|ARM64.Build.0 = Debug|ARM64 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Debug|ARM64.Deploy.0 = Debug|ARM64 @@ -188,19 +180,9 @@ Global {6AA3791A-0713-4548-A357-87A323E7AC3A}.Debug|x86.ActiveCfg = Debug|x86 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Debug|x86.Build.0 = Debug|x86 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Debug|x86.Deploy.0 = Debug|x86 - {6AA3791A-0713-4548-A357-87A323E7AC3A}.Fuzzing|Any CPU.ActiveCfg = Release|Any CPU - {6AA3791A-0713-4548-A357-87A323E7AC3A}.Fuzzing|Any CPU.Build.0 = Release|Any CPU - {6AA3791A-0713-4548-A357-87A323E7AC3A}.Fuzzing|Any CPU.Deploy.0 = Release|Any CPU - {6AA3791A-0713-4548-A357-87A323E7AC3A}.Fuzzing|ARM.ActiveCfg = Release|ARM {6AA3791A-0713-4548-A357-87A323E7AC3A}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Fuzzing|x64.ActiveCfg = Release|x64 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Fuzzing|x86.ActiveCfg = Release|x86 - {6AA3791A-0713-4548-A357-87A323E7AC3A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6AA3791A-0713-4548-A357-87A323E7AC3A}.Release|Any CPU.Build.0 = Release|Any CPU - {6AA3791A-0713-4548-A357-87A323E7AC3A}.Release|Any CPU.Deploy.0 = Release|Any CPU - {6AA3791A-0713-4548-A357-87A323E7AC3A}.Release|ARM.ActiveCfg = Release|ARM - {6AA3791A-0713-4548-A357-87A323E7AC3A}.Release|ARM.Build.0 = Release|ARM - {6AA3791A-0713-4548-A357-87A323E7AC3A}.Release|ARM.Deploy.0 = Release|ARM {6AA3791A-0713-4548-A357-87A323E7AC3A}.Release|ARM64.ActiveCfg = Release|ARM64 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Release|ARM64.Build.0 = Release|ARM64 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Release|ARM64.Deploy.0 = Release|ARM64 @@ -210,297 +192,181 @@ Global {6AA3791A-0713-4548-A357-87A323E7AC3A}.Release|x86.ActiveCfg = Release|x86 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Release|x86.Build.0 = Release|x86 {6AA3791A-0713-4548-A357-87A323E7AC3A}.Release|x86.Deploy.0 = Release|x86 - {6AA3791A-0713-4548-A357-87A323E7AC3A}.TestRelease|Any CPU.ActiveCfg = Release|Any CPU - {6AA3791A-0713-4548-A357-87A323E7AC3A}.TestRelease|Any CPU.Build.0 = Release|Any CPU - {6AA3791A-0713-4548-A357-87A323E7AC3A}.TestRelease|Any CPU.Deploy.0 = Release|Any CPU - {6AA3791A-0713-4548-A357-87A323E7AC3A}.TestRelease|ARM.ActiveCfg = Release|ARM {6AA3791A-0713-4548-A357-87A323E7AC3A}.TestRelease|ARM64.ActiveCfg = Release|ARM64 {6AA3791A-0713-4548-A357-87A323E7AC3A}.TestRelease|x64.ActiveCfg = Release|x64 {6AA3791A-0713-4548-A357-87A323E7AC3A}.TestRelease|x86.ActiveCfg = Release|x86 - {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Debug|ARM.ActiveCfg = Debug|ARM - {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Debug|ARM.Build.0 = Debug|ARM {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Debug|ARM64.ActiveCfg = Debug|ARM64 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Debug|ARM64.Build.0 = Debug|ARM64 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Debug|x64.ActiveCfg = Debug|x64 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Debug|x64.Build.0 = Debug|x64 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Debug|x86.ActiveCfg = Debug|Win32 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Debug|x86.Build.0 = Debug|Win32 - {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Fuzzing|Any CPU.ActiveCfg = Release|Win32 - {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Fuzzing|Any CPU.Build.0 = Release|Win32 - {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Fuzzing|ARM.ActiveCfg = Release|ARM {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Fuzzing|x64.ActiveCfg = Release|x64 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Fuzzing|x86.ActiveCfg = Release|Win32 - {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Release|Any CPU.ActiveCfg = Release|Win32 - {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Release|ARM.ActiveCfg = Release|ARM - {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Release|ARM.Build.0 = Release|ARM {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Release|ARM64.ActiveCfg = Release|ARM64 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Release|ARM64.Build.0 = Release|ARM64 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Release|x64.ActiveCfg = Release|x64 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Release|x64.Build.0 = Release|x64 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Release|x86.ActiveCfg = Release|Win32 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.Release|x86.Build.0 = Release|Win32 - {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.TestRelease|Any CPU.ActiveCfg = Release|Win32 - {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.TestRelease|Any CPU.Build.0 = Release|Win32 - {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.TestRelease|ARM.ActiveCfg = Release|ARM {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.TestRelease|ARM64.ActiveCfg = Release|ARM64 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.TestRelease|x64.ActiveCfg = Release|x64 {5B6F90DF-FD19-4BAE-83D9-24DAD128E777}.TestRelease|x86.ActiveCfg = Release|Win32 - {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Debug|ARM.ActiveCfg = Debug|ARM - {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Debug|ARM.Build.0 = Debug|ARM {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Debug|ARM64.ActiveCfg = Debug|ARM64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Debug|ARM64.Build.0 = Debug|ARM64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Debug|x64.ActiveCfg = Debug|x64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Debug|x64.Build.0 = Debug|x64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Debug|x86.ActiveCfg = Debug|Win32 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Debug|x86.Build.0 = Debug|Win32 - {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Fuzzing|Any CPU.ActiveCfg = Release|Win32 - {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Fuzzing|Any CPU.Build.0 = Release|Win32 - {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Fuzzing|ARM.ActiveCfg = Release|ARM {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Fuzzing|x64.ActiveCfg = Release|x64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Fuzzing|x86.ActiveCfg = Release|Win32 - {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Release|Any CPU.ActiveCfg = Release|Win32 - {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Release|ARM.ActiveCfg = Release|ARM - {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Release|ARM.Build.0 = Release|ARM {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Release|ARM64.ActiveCfg = Release|ARM64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Release|ARM64.Build.0 = Release|ARM64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Release|x64.ActiveCfg = Release|x64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Release|x64.Build.0 = Release|x64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Release|x86.ActiveCfg = Release|Win32 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.Release|x86.Build.0 = Release|Win32 - {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.TestRelease|Any CPU.ActiveCfg = Release|Win32 - {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.TestRelease|Any CPU.Build.0 = Release|Win32 - {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.TestRelease|ARM.ActiveCfg = Release|ARM - {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.TestRelease|ARM.Build.0 = Release|ARM {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.TestRelease|ARM64.ActiveCfg = Release|ARM64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.TestRelease|ARM64.Build.0 = Release|ARM64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.TestRelease|x64.ActiveCfg = Release|x64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.TestRelease|x64.Build.0 = Release|x64 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.TestRelease|x86.ActiveCfg = Release|Win32 {1C6E0108-2860-4B17-9F7E-FA5C6C1F3D3D}.TestRelease|x86.Build.0 = Release|Win32 - {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Debug|ARM.ActiveCfg = Debug|Win32 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Debug|ARM64.ActiveCfg = Debug|Win32 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Debug|x64.ActiveCfg = Debug|x64 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Debug|x64.Build.0 = Debug|x64 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Debug|x86.ActiveCfg = Debug|Win32 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Debug|x86.Build.0 = Debug|Win32 - {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Fuzzing|Any CPU.ActiveCfg = Release|x64 - {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Fuzzing|Any CPU.Build.0 = Release|x64 - {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Fuzzing|ARM.ActiveCfg = Release|Win32 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Fuzzing|ARM64.ActiveCfg = Release|Win32 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Fuzzing|x64.ActiveCfg = Release|x64 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Fuzzing|x86.ActiveCfg = Release|Win32 - {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Release|Any CPU.ActiveCfg = Release|Win32 - {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Release|ARM.ActiveCfg = Release|Win32 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Release|ARM64.ActiveCfg = Release|Win32 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Release|x64.ActiveCfg = Release|x64 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Release|x64.Build.0 = Release|x64 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Release|x86.ActiveCfg = Release|Win32 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.Release|x86.Build.0 = Release|Win32 - {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.TestRelease|Any CPU.ActiveCfg = Release|x64 - {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.TestRelease|Any CPU.Build.0 = Release|x64 - {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.TestRelease|ARM.ActiveCfg = Release|x64 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.TestRelease|ARM64.ActiveCfg = Release|x64 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.TestRelease|x64.ActiveCfg = Release|x64 {89B1AAB4-2BBC-4B65-9ED7-A01D5CF88230}.TestRelease|x86.ActiveCfg = Release|Win32 - {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Debug|ARM.ActiveCfg = Debug|ARM - {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Debug|ARM.Build.0 = Debug|ARM {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Debug|ARM64.ActiveCfg = Debug|ARM64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Debug|ARM64.Build.0 = Debug|ARM64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Debug|x64.ActiveCfg = Debug|x64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Debug|x64.Build.0 = Debug|x64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Debug|x86.ActiveCfg = Debug|Win32 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Debug|x86.Build.0 = Debug|Win32 - {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Fuzzing|Any CPU.ActiveCfg = Release|Win32 - {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Fuzzing|Any CPU.Build.0 = Release|Win32 - {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Fuzzing|ARM.ActiveCfg = Release|ARM {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Fuzzing|x64.ActiveCfg = Release|x64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Fuzzing|x86.ActiveCfg = Release|Win32 - {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Release|Any CPU.ActiveCfg = Release|Win32 - {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Release|ARM.ActiveCfg = Release|ARM - {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Release|ARM.Build.0 = Release|ARM {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Release|ARM64.ActiveCfg = Release|ARM64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Release|ARM64.Build.0 = Release|ARM64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Release|x64.ActiveCfg = Release|x64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Release|x64.Build.0 = Release|x64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Release|x86.ActiveCfg = Release|Win32 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.Release|x86.Build.0 = Release|Win32 - {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.TestRelease|Any CPU.ActiveCfg = Release|Win32 - {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.TestRelease|Any CPU.Build.0 = Release|Win32 - {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.TestRelease|ARM.ActiveCfg = Release|ARM - {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.TestRelease|ARM.Build.0 = Release|ARM {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.TestRelease|ARM64.ActiveCfg = Release|ARM64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.TestRelease|ARM64.Build.0 = Release|ARM64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.TestRelease|x64.ActiveCfg = Release|x64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.TestRelease|x64.Build.0 = Release|x64 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.TestRelease|x86.ActiveCfg = Release|Win32 {5EB88068-5FB9-4E69-89B2-72DBC5E068F9}.TestRelease|x86.Build.0 = Release|Win32 - {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.Debug|ARM.ActiveCfg = Debug|ARM - {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.Debug|ARM.Build.0 = Debug|ARM {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.Debug|ARM64.ActiveCfg = Debug|ARM64 {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.Debug|ARM64.Build.0 = Debug|ARM64 {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.Debug|x64.ActiveCfg = Debug|x64 {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.Debug|x64.Build.0 = Debug|x64 {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.Debug|x86.ActiveCfg = Debug|Win32 {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.Debug|x86.Build.0 = Debug|Win32 - {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 - {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.Fuzzing|ARM.ActiveCfg = Release|ARM {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.Fuzzing|x64.Build.0 = Fuzzing|x64 {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.Fuzzing|x86.ActiveCfg = Release|Win32 - {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.Release|Any CPU.ActiveCfg = Release|Win32 - {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.Release|ARM.ActiveCfg = Release|ARM - {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.Release|ARM.Build.0 = Release|ARM {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.Release|ARM64.ActiveCfg = Release|ARM64 {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.Release|ARM64.Build.0 = Release|ARM64 {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.Release|x64.ActiveCfg = Release|x64 {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.Release|x64.Build.0 = Release|x64 {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.Release|x86.ActiveCfg = Release|Win32 {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.Release|x86.Build.0 = Release|Win32 - {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.TestRelease|Any CPU.ActiveCfg = Fuzzing|ARM - {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.TestRelease|Any CPU.Build.0 = Fuzzing|ARM - {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.TestRelease|ARM.ActiveCfg = Release|ARM - {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.TestRelease|ARM.Build.0 = Release|ARM {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.TestRelease|ARM64.ActiveCfg = Release|ARM64 {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.TestRelease|ARM64.Build.0 = Release|ARM64 {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.TestRelease|x64.ActiveCfg = Release|x64 {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.TestRelease|x64.Build.0 = Release|x64 {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.TestRelease|x86.ActiveCfg = Release|Win32 {8BB94BB8-374F-4294-BCA1-C7811514A6B7}.TestRelease|x86.Build.0 = Release|Win32 - {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Debug|ARM.ActiveCfg = Debug|ARM - {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Debug|ARM.Build.0 = Debug|ARM {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Debug|ARM64.ActiveCfg = Debug|ARM64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Debug|ARM64.Build.0 = Debug|ARM64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Debug|x64.ActiveCfg = Debug|x64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Debug|x64.Build.0 = Debug|x64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Debug|x86.ActiveCfg = Debug|Win32 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Debug|x86.Build.0 = Debug|Win32 - {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|x64 - {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Fuzzing|ARM.ActiveCfg = Release|ARM {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Fuzzing|x64.Build.0 = Fuzzing|x64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Fuzzing|x86.ActiveCfg = Release|Win32 - {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Release|Any CPU.ActiveCfg = Release|Win32 - {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Release|ARM.ActiveCfg = Release|ARM - {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Release|ARM.Build.0 = Release|ARM {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Release|ARM64.ActiveCfg = Release|ARM64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Release|ARM64.Build.0 = Release|ARM64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Release|x64.ActiveCfg = Release|x64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Release|x64.Build.0 = Release|x64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Release|x86.ActiveCfg = Release|Win32 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.Release|x86.Build.0 = Release|Win32 - {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.TestRelease|Any CPU.ActiveCfg = Release|x64 - {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.TestRelease|Any CPU.Build.0 = Release|x64 - {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.TestRelease|ARM.ActiveCfg = Release|ARM - {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.TestRelease|ARM.Build.0 = Release|ARM {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.TestRelease|ARM64.ActiveCfg = Release|ARM64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.TestRelease|ARM64.Build.0 = Release|ARM64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.TestRelease|x64.ActiveCfg = Release|x64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.TestRelease|x64.Build.0 = Release|x64 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.TestRelease|x86.ActiveCfg = Release|Win32 {5890D6ED-7C3B-40F3-B436-B54F640D9E65}.TestRelease|x86.Build.0 = Release|Win32 - {FB313532-38B0-4676-9303-AB200AA13576}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {FB313532-38B0-4676-9303-AB200AA13576}.Debug|ARM.ActiveCfg = Debug|ARM - {FB313532-38B0-4676-9303-AB200AA13576}.Debug|ARM.Build.0 = Debug|ARM {FB313532-38B0-4676-9303-AB200AA13576}.Debug|ARM64.ActiveCfg = Debug|ARM64 {FB313532-38B0-4676-9303-AB200AA13576}.Debug|ARM64.Build.0 = Debug|ARM64 {FB313532-38B0-4676-9303-AB200AA13576}.Debug|x64.ActiveCfg = Debug|x64 {FB313532-38B0-4676-9303-AB200AA13576}.Debug|x64.Build.0 = Debug|x64 {FB313532-38B0-4676-9303-AB200AA13576}.Debug|x86.ActiveCfg = Debug|Win32 {FB313532-38B0-4676-9303-AB200AA13576}.Debug|x86.Build.0 = Debug|Win32 - {FB313532-38B0-4676-9303-AB200AA13576}.Fuzzing|Any CPU.ActiveCfg = Release|Win32 - {FB313532-38B0-4676-9303-AB200AA13576}.Fuzzing|Any CPU.Build.0 = Release|Win32 - {FB313532-38B0-4676-9303-AB200AA13576}.Fuzzing|ARM.ActiveCfg = Release|ARM {FB313532-38B0-4676-9303-AB200AA13576}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {FB313532-38B0-4676-9303-AB200AA13576}.Fuzzing|x64.ActiveCfg = Release|x64 {FB313532-38B0-4676-9303-AB200AA13576}.Fuzzing|x86.ActiveCfg = Release|Win32 - {FB313532-38B0-4676-9303-AB200AA13576}.Release|Any CPU.ActiveCfg = Release|Win32 - {FB313532-38B0-4676-9303-AB200AA13576}.Release|ARM.ActiveCfg = Release|ARM - {FB313532-38B0-4676-9303-AB200AA13576}.Release|ARM.Build.0 = Release|ARM {FB313532-38B0-4676-9303-AB200AA13576}.Release|ARM64.ActiveCfg = Release|ARM64 {FB313532-38B0-4676-9303-AB200AA13576}.Release|ARM64.Build.0 = Release|ARM64 {FB313532-38B0-4676-9303-AB200AA13576}.Release|x64.ActiveCfg = Release|x64 {FB313532-38B0-4676-9303-AB200AA13576}.Release|x64.Build.0 = Release|x64 {FB313532-38B0-4676-9303-AB200AA13576}.Release|x86.ActiveCfg = Release|Win32 {FB313532-38B0-4676-9303-AB200AA13576}.Release|x86.Build.0 = Release|Win32 - {FB313532-38B0-4676-9303-AB200AA13576}.TestRelease|Any CPU.ActiveCfg = Release|Win32 - {FB313532-38B0-4676-9303-AB200AA13576}.TestRelease|Any CPU.Build.0 = Release|Win32 - {FB313532-38B0-4676-9303-AB200AA13576}.TestRelease|ARM.ActiveCfg = Release|ARM {FB313532-38B0-4676-9303-AB200AA13576}.TestRelease|ARM64.ActiveCfg = Release|ARM64 {FB313532-38B0-4676-9303-AB200AA13576}.TestRelease|x64.ActiveCfg = Release|x64 {FB313532-38B0-4676-9303-AB200AA13576}.TestRelease|x86.ActiveCfg = Release|Win32 - {6CB84692-5994-407D-B9BD-9216AF77FE83}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {6CB84692-5994-407D-B9BD-9216AF77FE83}.Debug|ARM.ActiveCfg = Debug|ARM - {6CB84692-5994-407D-B9BD-9216AF77FE83}.Debug|ARM.Build.0 = Debug|ARM {6CB84692-5994-407D-B9BD-9216AF77FE83}.Debug|ARM64.ActiveCfg = Debug|ARM64 {6CB84692-5994-407D-B9BD-9216AF77FE83}.Debug|ARM64.Build.0 = Debug|ARM64 {6CB84692-5994-407D-B9BD-9216AF77FE83}.Debug|x64.ActiveCfg = Debug|x64 {6CB84692-5994-407D-B9BD-9216AF77FE83}.Debug|x64.Build.0 = Debug|x64 {6CB84692-5994-407D-B9BD-9216AF77FE83}.Debug|x86.ActiveCfg = Debug|Win32 {6CB84692-5994-407D-B9BD-9216AF77FE83}.Debug|x86.Build.0 = Debug|Win32 - {6CB84692-5994-407D-B9BD-9216AF77FE83}.Fuzzing|Any CPU.ActiveCfg = Release|Win32 - {6CB84692-5994-407D-B9BD-9216AF77FE83}.Fuzzing|Any CPU.Build.0 = Release|Win32 - {6CB84692-5994-407D-B9BD-9216AF77FE83}.Fuzzing|ARM.ActiveCfg = Release|ARM {6CB84692-5994-407D-B9BD-9216AF77FE83}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {6CB84692-5994-407D-B9BD-9216AF77FE83}.Fuzzing|x64.ActiveCfg = Release|x64 {6CB84692-5994-407D-B9BD-9216AF77FE83}.Fuzzing|x86.ActiveCfg = Release|Win32 - {6CB84692-5994-407D-B9BD-9216AF77FE83}.Release|Any CPU.ActiveCfg = Release|Win32 - {6CB84692-5994-407D-B9BD-9216AF77FE83}.Release|ARM.ActiveCfg = Release|ARM - {6CB84692-5994-407D-B9BD-9216AF77FE83}.Release|ARM.Build.0 = Release|ARM {6CB84692-5994-407D-B9BD-9216AF77FE83}.Release|ARM64.ActiveCfg = Release|ARM64 {6CB84692-5994-407D-B9BD-9216AF77FE83}.Release|ARM64.Build.0 = Release|ARM64 {6CB84692-5994-407D-B9BD-9216AF77FE83}.Release|x64.ActiveCfg = Release|x64 {6CB84692-5994-407D-B9BD-9216AF77FE83}.Release|x64.Build.0 = Release|x64 {6CB84692-5994-407D-B9BD-9216AF77FE83}.Release|x86.ActiveCfg = Release|Win32 {6CB84692-5994-407D-B9BD-9216AF77FE83}.Release|x86.Build.0 = Release|Win32 - {6CB84692-5994-407D-B9BD-9216AF77FE83}.TestRelease|Any CPU.ActiveCfg = Release|Win32 - {6CB84692-5994-407D-B9BD-9216AF77FE83}.TestRelease|Any CPU.Build.0 = Release|Win32 - {6CB84692-5994-407D-B9BD-9216AF77FE83}.TestRelease|ARM.ActiveCfg = Release|ARM {6CB84692-5994-407D-B9BD-9216AF77FE83}.TestRelease|ARM64.ActiveCfg = Release|ARM64 {6CB84692-5994-407D-B9BD-9216AF77FE83}.TestRelease|x64.ActiveCfg = Release|x64 {6CB84692-5994-407D-B9BD-9216AF77FE83}.TestRelease|x86.ActiveCfg = Release|Win32 - {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Debug|Any CPU.ActiveCfg = Debug|x86 - {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Debug|ARM.ActiveCfg = Debug|x86 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Debug|ARM64.ActiveCfg = Debug|x86 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Debug|x64.ActiveCfg = Debug|x64 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Debug|x64.Build.0 = Debug|x64 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Debug|x86.ActiveCfg = Debug|x86 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Debug|x86.Build.0 = Debug|x86 - {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Fuzzing|Any CPU.ActiveCfg = TestOnly|x64 - {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Fuzzing|Any CPU.Build.0 = TestOnly|x64 - {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Fuzzing|ARM.ActiveCfg = Release|x86 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Fuzzing|ARM64.ActiveCfg = Release|x86 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Fuzzing|x64.ActiveCfg = Release|x64 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Fuzzing|x86.ActiveCfg = Release|x86 - {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Release|Any CPU.ActiveCfg = Release|x86 - {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Release|ARM.ActiveCfg = Release|x86 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Release|ARM64.ActiveCfg = Release|x86 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Release|x64.ActiveCfg = Release|x64 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Release|x64.Build.0 = Release|x64 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Release|x86.ActiveCfg = Release|x86 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.Release|x86.Build.0 = Release|x86 - {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.TestRelease|Any CPU.ActiveCfg = TestOnly|x64 - {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.TestRelease|Any CPU.Build.0 = TestOnly|x64 - {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.TestRelease|ARM.ActiveCfg = TestOnly|x64 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.TestRelease|ARM64.ActiveCfg = TestOnly|x64 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.TestRelease|x64.ActiveCfg = Release|x64 {3C0269FA-E582-4CA7-9E33-3881A005CA0C}.TestRelease|x86.ActiveCfg = Release|x86 - {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Debug|Any CPU.Deploy.0 = Debug|Any CPU - {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Debug|ARM.ActiveCfg = Debug|ARM - {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Debug|ARM.Build.0 = Debug|ARM - {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Debug|ARM.Deploy.0 = Debug|ARM {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Debug|ARM64.ActiveCfg = Debug|ARM64 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Debug|ARM64.Build.0 = Debug|ARM64 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Debug|ARM64.Deploy.0 = Debug|ARM64 @@ -510,19 +376,9 @@ Global {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Debug|x86.ActiveCfg = Debug|x86 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Debug|x86.Build.0 = Debug|x86 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Debug|x86.Deploy.0 = Debug|x86 - {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Fuzzing|Any CPU.ActiveCfg = Release|Any CPU - {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Fuzzing|Any CPU.Build.0 = Release|Any CPU - {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Fuzzing|Any CPU.Deploy.0 = Release|Any CPU - {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Fuzzing|ARM.ActiveCfg = Release|ARM {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Fuzzing|x64.ActiveCfg = Release|x64 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Fuzzing|x86.ActiveCfg = Release|x86 - {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Release|Any CPU.Build.0 = Release|Any CPU - {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Release|Any CPU.Deploy.0 = Release|Any CPU - {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Release|ARM.ActiveCfg = Release|ARM - {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Release|ARM.Build.0 = Release|ARM - {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Release|ARM.Deploy.0 = Release|ARM {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Release|ARM64.ActiveCfg = Release|ARM64 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Release|ARM64.Build.0 = Release|ARM64 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Release|ARM64.Deploy.0 = Release|ARM64 @@ -532,592 +388,438 @@ Global {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Release|x86.ActiveCfg = Release|x86 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Release|x86.Build.0 = Release|x86 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.Release|x86.Deploy.0 = Release|x86 - {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.TestRelease|Any CPU.ActiveCfg = Release|Any CPU - {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.TestRelease|Any CPU.Build.0 = Release|Any CPU - {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.TestRelease|Any CPU.Deploy.0 = Release|Any CPU - {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.TestRelease|ARM.ActiveCfg = Release|ARM {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.TestRelease|ARM64.ActiveCfg = Release|ARM64 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.TestRelease|x64.ActiveCfg = Release|x64 {3E2CBA31-CEBA-4D63-BF52-49C0718E19EA}.TestRelease|x86.ActiveCfg = Release|x86 - {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.Debug|Any CPU.ActiveCfg = Debug - {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.Debug|Any CPU.Build.0 = Debug - {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.Debug|ARM.ActiveCfg = Debug {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.Debug|ARM64.ActiveCfg = Debug {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.Debug|x64.ActiveCfg = Debug {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.Debug|x86.ActiveCfg = Debug - {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.Fuzzing|Any CPU.ActiveCfg = Release - {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.Fuzzing|Any CPU.Build.0 = Release - {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.Fuzzing|ARM.ActiveCfg = Release {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.Fuzzing|ARM64.ActiveCfg = Release {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.Fuzzing|x64.ActiveCfg = Release {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.Fuzzing|x86.ActiveCfg = Release - {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.Release|Any CPU.ActiveCfg = Release - {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.Release|Any CPU.Build.0 = Release - {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.Release|ARM.ActiveCfg = Release {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.Release|ARM64.ActiveCfg = Release {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.Release|x64.ActiveCfg = Release {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.Release|x86.ActiveCfg = Release - {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.TestRelease|Any CPU.ActiveCfg = Release - {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.TestRelease|Any CPU.Build.0 = Release - {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.TestRelease|ARM.ActiveCfg = Release {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.TestRelease|ARM64.ActiveCfg = Release {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.TestRelease|x64.ActiveCfg = Release {C1624B2F-2BF6-4E28-92FA-1BF85C6B62A8}.TestRelease|x86.ActiveCfg = Release - {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Debug|Any CPU.ActiveCfg = Debug|x86 - {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Debug|ARM.ActiveCfg = Debug|x86 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Debug|ARM64.ActiveCfg = Debug|x86 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Debug|x64.ActiveCfg = Debug|x64 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Debug|x64.Build.0 = Debug|x64 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Debug|x86.ActiveCfg = Debug|x86 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Debug|x86.Build.0 = Debug|x86 - {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Fuzzing|Any CPU.ActiveCfg = Release|x64 - {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Fuzzing|Any CPU.Build.0 = Release|x64 - {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Fuzzing|ARM.ActiveCfg = Release|x86 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Fuzzing|ARM64.ActiveCfg = Release|x86 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Fuzzing|x64.ActiveCfg = Release|x64 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Fuzzing|x86.ActiveCfg = Release|x86 - {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Release|Any CPU.ActiveCfg = Release|x86 - {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Release|ARM.ActiveCfg = Release|x86 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Release|ARM64.ActiveCfg = Release|x86 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Release|x64.ActiveCfg = Release|x64 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Release|x64.Build.0 = Release|x64 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Release|x86.ActiveCfg = Release|x86 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.Release|x86.Build.0 = Release|x86 - {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.TestRelease|Any CPU.ActiveCfg = Release|x64 - {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.TestRelease|Any CPU.Build.0 = Release|x64 - {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.TestRelease|ARM.ActiveCfg = Release|x64 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.TestRelease|ARM64.ActiveCfg = Release|x64 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.TestRelease|x64.ActiveCfg = Release|x64 {3B8466CF-4FDD-4329-9C80-91321C4AAC99}.TestRelease|x86.ActiveCfg = Release|x86 - {82B39FDA-E86B-4713-A873-9D56DE00247A}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {82B39FDA-E86B-4713-A873-9D56DE00247A}.Debug|ARM.ActiveCfg = Debug|ARM - {82B39FDA-E86B-4713-A873-9D56DE00247A}.Debug|ARM.Build.0 = Debug|ARM {82B39FDA-E86B-4713-A873-9D56DE00247A}.Debug|ARM64.ActiveCfg = Debug|ARM64 {82B39FDA-E86B-4713-A873-9D56DE00247A}.Debug|ARM64.Build.0 = Debug|ARM64 {82B39FDA-E86B-4713-A873-9D56DE00247A}.Debug|x64.ActiveCfg = Debug|x64 {82B39FDA-E86B-4713-A873-9D56DE00247A}.Debug|x64.Build.0 = Debug|x64 {82B39FDA-E86B-4713-A873-9D56DE00247A}.Debug|x86.ActiveCfg = Debug|Win32 {82B39FDA-E86B-4713-A873-9D56DE00247A}.Debug|x86.Build.0 = Debug|Win32 - {82B39FDA-E86B-4713-A873-9D56DE00247A}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|x64 - {82B39FDA-E86B-4713-A873-9D56DE00247A}.Fuzzing|ARM.ActiveCfg = Release|ARM {82B39FDA-E86B-4713-A873-9D56DE00247A}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {82B39FDA-E86B-4713-A873-9D56DE00247A}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 {82B39FDA-E86B-4713-A873-9D56DE00247A}.Fuzzing|x64.Build.0 = Fuzzing|x64 {82B39FDA-E86B-4713-A873-9D56DE00247A}.Fuzzing|x86.ActiveCfg = Release|Win32 - {82B39FDA-E86B-4713-A873-9D56DE00247A}.Release|Any CPU.ActiveCfg = Release|Win32 - {82B39FDA-E86B-4713-A873-9D56DE00247A}.Release|ARM.ActiveCfg = Release|ARM - {82B39FDA-E86B-4713-A873-9D56DE00247A}.Release|ARM.Build.0 = Release|ARM {82B39FDA-E86B-4713-A873-9D56DE00247A}.Release|ARM64.ActiveCfg = Release|ARM64 {82B39FDA-E86B-4713-A873-9D56DE00247A}.Release|ARM64.Build.0 = Release|ARM64 {82B39FDA-E86B-4713-A873-9D56DE00247A}.Release|x64.ActiveCfg = Release|x64 {82B39FDA-E86B-4713-A873-9D56DE00247A}.Release|x64.Build.0 = Release|x64 {82B39FDA-E86B-4713-A873-9D56DE00247A}.Release|x86.ActiveCfg = Release|Win32 {82B39FDA-E86B-4713-A873-9D56DE00247A}.Release|x86.Build.0 = Release|Win32 - {82B39FDA-E86B-4713-A873-9D56DE00247A}.TestRelease|Any CPU.ActiveCfg = Release|x64 - {82B39FDA-E86B-4713-A873-9D56DE00247A}.TestRelease|Any CPU.Build.0 = Release|x64 - {82B39FDA-E86B-4713-A873-9D56DE00247A}.TestRelease|ARM.ActiveCfg = Release|ARM - {82B39FDA-E86B-4713-A873-9D56DE00247A}.TestRelease|ARM.Build.0 = Release|ARM {82B39FDA-E86B-4713-A873-9D56DE00247A}.TestRelease|ARM64.ActiveCfg = Release|ARM64 {82B39FDA-E86B-4713-A873-9D56DE00247A}.TestRelease|ARM64.Build.0 = Release|ARM64 {82B39FDA-E86B-4713-A873-9D56DE00247A}.TestRelease|x64.ActiveCfg = Release|x64 {82B39FDA-E86B-4713-A873-9D56DE00247A}.TestRelease|x64.Build.0 = Release|x64 {82B39FDA-E86B-4713-A873-9D56DE00247A}.TestRelease|x86.ActiveCfg = Release|Win32 {82B39FDA-E86B-4713-A873-9D56DE00247A}.TestRelease|x86.Build.0 = Release|Win32 - {1622DA16-914F-4F57-A259-D5169003CC8C}.Debug|Any CPU.ActiveCfg = Debug|x64 - {1622DA16-914F-4F57-A259-D5169003CC8C}.Debug|ARM.ActiveCfg = Fuzzing|x64 {1622DA16-914F-4F57-A259-D5169003CC8C}.Debug|ARM64.ActiveCfg = Fuzzing|x64 {1622DA16-914F-4F57-A259-D5169003CC8C}.Debug|x64.ActiveCfg = Debug|x64 {1622DA16-914F-4F57-A259-D5169003CC8C}.Debug|x86.ActiveCfg = Fuzzing|x64 - {1622DA16-914F-4F57-A259-D5169003CC8C}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|x64 - {1622DA16-914F-4F57-A259-D5169003CC8C}.Fuzzing|ARM.ActiveCfg = Fuzzing|x64 {1622DA16-914F-4F57-A259-D5169003CC8C}.Fuzzing|ARM64.ActiveCfg = Fuzzing|x64 {1622DA16-914F-4F57-A259-D5169003CC8C}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 {1622DA16-914F-4F57-A259-D5169003CC8C}.Fuzzing|x64.Build.0 = Fuzzing|x64 {1622DA16-914F-4F57-A259-D5169003CC8C}.Fuzzing|x86.ActiveCfg = Fuzzing|x64 - {1622DA16-914F-4F57-A259-D5169003CC8C}.Release|Any CPU.ActiveCfg = Debug|x64 - {1622DA16-914F-4F57-A259-D5169003CC8C}.Release|Any CPU.Build.0 = Debug|x64 - {1622DA16-914F-4F57-A259-D5169003CC8C}.Release|ARM.ActiveCfg = Fuzzing|x64 {1622DA16-914F-4F57-A259-D5169003CC8C}.Release|ARM64.ActiveCfg = Fuzzing|x64 {1622DA16-914F-4F57-A259-D5169003CC8C}.Release|x64.ActiveCfg = Fuzzing|x64 {1622DA16-914F-4F57-A259-D5169003CC8C}.Release|x86.ActiveCfg = Fuzzing|x64 - {1622DA16-914F-4F57-A259-D5169003CC8C}.TestRelease|Any CPU.ActiveCfg = Debug|x64 - {1622DA16-914F-4F57-A259-D5169003CC8C}.TestRelease|Any CPU.Build.0 = Debug|x64 - {1622DA16-914F-4F57-A259-D5169003CC8C}.TestRelease|ARM.ActiveCfg = Debug|x64 {1622DA16-914F-4F57-A259-D5169003CC8C}.TestRelease|ARM64.ActiveCfg = Debug|x64 {1622DA16-914F-4F57-A259-D5169003CC8C}.TestRelease|x64.ActiveCfg = Debug|x64 {1622DA16-914F-4F57-A259-D5169003CC8C}.TestRelease|x86.ActiveCfg = Debug|x64 - {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Debug|Any CPU.ActiveCfg = Debug|x86 - {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Debug|ARM.ActiveCfg = Debug|x86 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Debug|ARM64.ActiveCfg = Debug|x86 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Debug|x64.ActiveCfg = Debug|x64 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Debug|x64.Build.0 = Debug|x64 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Debug|x86.ActiveCfg = Debug|x86 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Debug|x86.Build.0 = Debug|x86 - {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Fuzzing|Any CPU.ActiveCfg = Release|x64 - {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Fuzzing|Any CPU.Build.0 = Release|x64 - {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Fuzzing|ARM.ActiveCfg = Release|x86 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Fuzzing|ARM64.ActiveCfg = Release|x86 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Fuzzing|x64.ActiveCfg = Release|x64 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Fuzzing|x86.ActiveCfg = Release|x86 - {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Release|Any CPU.ActiveCfg = Release|x86 - {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Release|ARM.ActiveCfg = Release|x86 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Release|ARM64.ActiveCfg = Release|x86 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Release|x64.ActiveCfg = Release|x64 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Release|x64.Build.0 = Release|x64 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Release|x86.ActiveCfg = Release|x86 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.Release|x86.Build.0 = Release|x86 - {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.TestRelease|Any CPU.ActiveCfg = Release|x64 - {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.TestRelease|Any CPU.Build.0 = Release|x64 - {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.TestRelease|ARM.ActiveCfg = Release|x64 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.TestRelease|ARM64.ActiveCfg = Release|x64 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.TestRelease|x64.ActiveCfg = Release|x64 {3BAF989F-7F65-465B-ACE8-BAFE42D1017E}.TestRelease|x86.ActiveCfg = Release|x86 - {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.Debug|ARM.ActiveCfg = Debug|ARM - {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.Debug|ARM.Build.0 = Debug|ARM {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.Debug|ARM64.ActiveCfg = Debug|ARM64 {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.Debug|ARM64.Build.0 = Debug|ARM64 {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.Debug|x64.ActiveCfg = Debug|x64 {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.Debug|x64.Build.0 = Debug|x64 {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.Debug|x86.ActiveCfg = Debug|Win32 {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.Debug|x86.Build.0 = Debug|Win32 - {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.Fuzzing|Any CPU.ActiveCfg = Release|Win32 - {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.Fuzzing|Any CPU.Build.0 = Release|Win32 - {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.Fuzzing|ARM.ActiveCfg = Release|ARM {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.Fuzzing|ARM64.ActiveCfg = Release|ARM64 {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.Fuzzing|x64.ActiveCfg = Release|x64 {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.Fuzzing|x86.ActiveCfg = Release|Win32 - {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.Release|Any CPU.ActiveCfg = Release|Win32 - {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.Release|ARM.ActiveCfg = Release|ARM - {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.Release|ARM.Build.0 = Release|ARM {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.Release|ARM64.ActiveCfg = Release|ARM64 {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.Release|ARM64.Build.0 = Release|ARM64 {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.Release|x64.ActiveCfg = Release|x64 {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.Release|x64.Build.0 = Release|x64 {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.Release|x86.ActiveCfg = Release|Win32 {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.Release|x86.Build.0 = Release|Win32 - {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.TestRelease|Any CPU.ActiveCfg = Release|Win32 - {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.TestRelease|Any CPU.Build.0 = Release|Win32 - {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.TestRelease|ARM.ActiveCfg = Release|ARM - {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.TestRelease|ARM.Build.0 = Release|ARM {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.TestRelease|ARM64.ActiveCfg = Release|ARM64 {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.TestRelease|ARM64.Build.0 = Release|ARM64 {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.TestRelease|x64.ActiveCfg = Release|x64 {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.TestRelease|x64.Build.0 = Release|x64 {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.TestRelease|x86.ActiveCfg = Release|Win32 {866C3F06-636F-4BE8-BC24-5F86ECC606A1}.TestRelease|x86.Build.0 = Release|Win32 - {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Debug|ARM.ActiveCfg = Debug|ARM - {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Debug|ARM.Build.0 = Debug|ARM {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Debug|ARM64.ActiveCfg = Debug|ARM64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Debug|ARM64.Build.0 = Debug|ARM64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Debug|x64.ActiveCfg = Debug|x64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Debug|x64.Build.0 = Debug|x64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Debug|x86.ActiveCfg = Debug|Win32 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Debug|x86.Build.0 = Debug|Win32 - {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Fuzzing|Any CPU.ActiveCfg = Release|Win32 - {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Fuzzing|Any CPU.Build.0 = Release|Win32 - {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Fuzzing|ARM.ActiveCfg = Debug|ARM {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Fuzzing|ARM64.ActiveCfg = Debug|ARM64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Fuzzing|x64.ActiveCfg = Debug|x64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Fuzzing|x86.ActiveCfg = Debug|Win32 - {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Release|Any CPU.ActiveCfg = Release|Win32 - {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Release|ARM.ActiveCfg = Release|ARM - {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Release|ARM.Build.0 = Release|ARM {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Release|ARM64.ActiveCfg = Release|ARM64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Release|ARM64.Build.0 = Release|ARM64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Release|x64.ActiveCfg = Release|x64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Release|x64.Build.0 = Release|x64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Release|x86.ActiveCfg = Release|Win32 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.Release|x86.Build.0 = Release|Win32 - {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.TestRelease|Any CPU.ActiveCfg = Release|Win32 - {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.TestRelease|Any CPU.Build.0 = Release|Win32 - {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.TestRelease|ARM.ActiveCfg = Release|ARM - {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.TestRelease|ARM.Build.0 = Release|ARM {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.TestRelease|ARM64.ActiveCfg = Release|ARM64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.TestRelease|ARM64.Build.0 = Release|ARM64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.TestRelease|x64.ActiveCfg = Release|x64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.TestRelease|x64.Build.0 = Release|x64 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.TestRelease|x86.ActiveCfg = Release|Win32 {1CC41A9A-AE66-459D-9210-1E572DD7BE69}.TestRelease|x86.Build.0 = Release|Win32 - {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Debug|ARM.ActiveCfg = Debug|ARM - {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Debug|ARM.Build.0 = Debug|ARM {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Debug|ARM64.ActiveCfg = Debug|ARM64 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Debug|ARM64.Build.0 = Debug|ARM64 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Debug|x64.ActiveCfg = Debug|x64 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Debug|x64.Build.0 = Debug|x64 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Debug|x86.ActiveCfg = Debug|Win32 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Debug|x86.Build.0 = Debug|Win32 - {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Fuzzing|Any CPU.ActiveCfg = Release|Win32 - {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Fuzzing|Any CPU.Build.0 = Release|Win32 - {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Fuzzing|ARM.ActiveCfg = Debug|ARM {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Fuzzing|ARM64.ActiveCfg = Debug|ARM64 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Fuzzing|x64.ActiveCfg = Debug|x64 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Fuzzing|x86.ActiveCfg = Debug|Win32 - {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Release|Any CPU.ActiveCfg = Release|Win32 - {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Release|ARM.ActiveCfg = Release|ARM - {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Release|ARM.Build.0 = Release|ARM {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Release|ARM64.ActiveCfg = Release|ARM64 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Release|ARM64.Build.0 = Release|ARM64 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Release|x64.ActiveCfg = Release|x64 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Release|x64.Build.0 = Release|x64 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Release|x86.ActiveCfg = Release|Win32 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.Release|x86.Build.0 = Release|Win32 - {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.TestRelease|Any CPU.ActiveCfg = Release|Win32 - {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.TestRelease|Any CPU.Build.0 = Release|Win32 - {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.TestRelease|ARM.ActiveCfg = Release|ARM {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.TestRelease|ARM64.ActiveCfg = Release|ARM64 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.TestRelease|x64.ActiveCfg = Release|x64 {2B00D362-AC92-41F3-A8D2-5B1599BDCA01}.TestRelease|x86.ActiveCfg = Release|Win32 - {846FB88B-BF1B-4F33-9883-E589CEC99739}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {846FB88B-BF1B-4F33-9883-E589CEC99739}.Debug|Any CPU.Build.0 = Debug|Any CPU - {846FB88B-BF1B-4F33-9883-E589CEC99739}.Debug|ARM.ActiveCfg = Debug|Any CPU - {846FB88B-BF1B-4F33-9883-E589CEC99739}.Debug|ARM.Build.0 = Debug|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Debug|ARM64.ActiveCfg = Debug|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Debug|ARM64.Build.0 = Debug|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Debug|x64.ActiveCfg = Debug|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Debug|x64.Build.0 = Debug|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Debug|x86.ActiveCfg = Debug|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Debug|x86.Build.0 = Debug|Any CPU - {846FB88B-BF1B-4F33-9883-E589CEC99739}.Fuzzing|Any CPU.ActiveCfg = Debug|Any CPU - {846FB88B-BF1B-4F33-9883-E589CEC99739}.Fuzzing|Any CPU.Build.0 = Debug|Any CPU - {846FB88B-BF1B-4F33-9883-E589CEC99739}.Fuzzing|ARM.ActiveCfg = Debug|Any CPU - {846FB88B-BF1B-4F33-9883-E589CEC99739}.Fuzzing|ARM.Build.0 = Debug|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Fuzzing|ARM64.ActiveCfg = Debug|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Fuzzing|ARM64.Build.0 = Debug|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Fuzzing|x64.ActiveCfg = Debug|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Fuzzing|x64.Build.0 = Debug|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Fuzzing|x86.ActiveCfg = Debug|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Fuzzing|x86.Build.0 = Debug|Any CPU - {846FB88B-BF1B-4F33-9883-E589CEC99739}.Release|Any CPU.ActiveCfg = Release|Any CPU - {846FB88B-BF1B-4F33-9883-E589CEC99739}.Release|Any CPU.Build.0 = Release|Any CPU - {846FB88B-BF1B-4F33-9883-E589CEC99739}.Release|ARM.ActiveCfg = Release|Any CPU - {846FB88B-BF1B-4F33-9883-E589CEC99739}.Release|ARM.Build.0 = Release|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Release|ARM64.ActiveCfg = Release|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Release|ARM64.Build.0 = Release|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Release|x64.ActiveCfg = Release|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Release|x64.Build.0 = Release|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Release|x86.ActiveCfg = Release|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.Release|x86.Build.0 = Release|Any CPU - {846FB88B-BF1B-4F33-9883-E589CEC99739}.TestRelease|Any CPU.ActiveCfg = Release|Any CPU - {846FB88B-BF1B-4F33-9883-E589CEC99739}.TestRelease|Any CPU.Build.0 = Release|Any CPU - {846FB88B-BF1B-4F33-9883-E589CEC99739}.TestRelease|ARM.ActiveCfg = Release|Any CPU - {846FB88B-BF1B-4F33-9883-E589CEC99739}.TestRelease|ARM.Build.0 = Release|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.TestRelease|ARM64.ActiveCfg = Release|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.TestRelease|ARM64.Build.0 = Release|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.TestRelease|x64.ActiveCfg = Release|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.TestRelease|x64.Build.0 = Release|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.TestRelease|x86.ActiveCfg = Release|Any CPU {846FB88B-BF1B-4F33-9883-E589CEC99739}.TestRelease|x86.Build.0 = Release|Any CPU - {68808357-902B-406C-8C19-E8E26A69DE8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {68808357-902B-406C-8C19-E8E26A69DE8A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {68808357-902B-406C-8C19-E8E26A69DE8A}.Debug|ARM.ActiveCfg = Debug|Any CPU - {68808357-902B-406C-8C19-E8E26A69DE8A}.Debug|ARM.Build.0 = Debug|Any CPU {68808357-902B-406C-8C19-E8E26A69DE8A}.Debug|ARM64.ActiveCfg = Debug|Any CPU {68808357-902B-406C-8C19-E8E26A69DE8A}.Debug|ARM64.Build.0 = Debug|Any CPU {68808357-902B-406C-8C19-E8E26A69DE8A}.Debug|x64.ActiveCfg = Debug|Any CPU {68808357-902B-406C-8C19-E8E26A69DE8A}.Debug|x64.Build.0 = Debug|Any CPU {68808357-902B-406C-8C19-E8E26A69DE8A}.Debug|x86.ActiveCfg = Debug|Any CPU {68808357-902B-406C-8C19-E8E26A69DE8A}.Debug|x86.Build.0 = Debug|Any CPU - {68808357-902B-406C-8C19-E8E26A69DE8A}.Fuzzing|Any CPU.ActiveCfg = Debug|Any CPU - {68808357-902B-406C-8C19-E8E26A69DE8A}.Fuzzing|Any CPU.Build.0 = Debug|Any CPU - {68808357-902B-406C-8C19-E8E26A69DE8A}.Fuzzing|ARM.ActiveCfg = Debug|Any CPU - {68808357-902B-406C-8C19-E8E26A69DE8A}.Fuzzing|ARM.Build.0 = Debug|Any CPU {68808357-902B-406C-8C19-E8E26A69DE8A}.Fuzzing|ARM64.ActiveCfg = Debug|Any CPU {68808357-902B-406C-8C19-E8E26A69DE8A}.Fuzzing|ARM64.Build.0 = Debug|Any CPU {68808357-902B-406C-8C19-E8E26A69DE8A}.Fuzzing|x64.ActiveCfg = Debug|Any CPU {68808357-902B-406C-8C19-E8E26A69DE8A}.Fuzzing|x64.Build.0 = Debug|Any CPU {68808357-902B-406C-8C19-E8E26A69DE8A}.Fuzzing|x86.ActiveCfg = Debug|Any CPU {68808357-902B-406C-8C19-E8E26A69DE8A}.Fuzzing|x86.Build.0 = Debug|Any CPU - {68808357-902B-406C-8C19-E8E26A69DE8A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {68808357-902B-406C-8C19-E8E26A69DE8A}.Release|Any CPU.Build.0 = Release|Any CPU - {68808357-902B-406C-8C19-E8E26A69DE8A}.Release|ARM.ActiveCfg = Release|Any CPU - {68808357-902B-406C-8C19-E8E26A69DE8A}.Release|ARM.Build.0 = Release|Any CPU {68808357-902B-406C-8C19-E8E26A69DE8A}.Release|ARM64.ActiveCfg = Release|Any CPU {68808357-902B-406C-8C19-E8E26A69DE8A}.Release|ARM64.Build.0 = Release|Any CPU {68808357-902B-406C-8C19-E8E26A69DE8A}.Release|x64.ActiveCfg = Release|Any CPU {68808357-902B-406C-8C19-E8E26A69DE8A}.Release|x64.Build.0 = Release|Any CPU {68808357-902B-406C-8C19-E8E26A69DE8A}.Release|x86.ActiveCfg = Release|Any CPU {68808357-902B-406C-8C19-E8E26A69DE8A}.Release|x86.Build.0 = Release|Any CPU - {68808357-902B-406C-8C19-E8E26A69DE8A}.TestRelease|Any CPU.ActiveCfg = Release|Any CPU - {68808357-902B-406C-8C19-E8E26A69DE8A}.TestRelease|Any CPU.Build.0 = Release|Any CPU - {68808357-902B-406C-8C19-E8E26A69DE8A}.TestRelease|ARM.ActiveCfg = Release|Any CPU - {68808357-902B-406C-8C19-E8E26A69DE8A}.TestRelease|ARM.Build.0 = Release|Any CPU {68808357-902B-406C-8C19-E8E26A69DE8A}.TestRelease|ARM64.ActiveCfg = Release|Any CPU {68808357-902B-406C-8C19-E8E26A69DE8A}.TestRelease|ARM64.Build.0 = Release|Any CPU {68808357-902B-406C-8C19-E8E26A69DE8A}.TestRelease|x64.ActiveCfg = Release|Any CPU {68808357-902B-406C-8C19-E8E26A69DE8A}.TestRelease|x64.Build.0 = Release|Any CPU {68808357-902B-406C-8C19-E8E26A69DE8A}.TestRelease|x86.ActiveCfg = Release|Any CPU {68808357-902B-406C-8C19-E8E26A69DE8A}.TestRelease|x86.Build.0 = Release|Any CPU - {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Debug|ARM.ActiveCfg = Debug|ARM - {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Debug|ARM.Build.0 = Debug|ARM {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Debug|ARM64.ActiveCfg = Debug|ARM64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Debug|ARM64.Build.0 = Debug|ARM64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Debug|x64.ActiveCfg = Debug|x64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Debug|x64.Build.0 = Debug|x64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Debug|x86.ActiveCfg = Debug|Win32 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Debug|x86.Build.0 = Debug|Win32 - {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Fuzzing|Any CPU.ActiveCfg = Debug|Win32 - {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Fuzzing|Any CPU.Build.0 = Debug|Win32 - {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Fuzzing|ARM.ActiveCfg = Debug|ARM - {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Fuzzing|ARM.Build.0 = Debug|ARM {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Fuzzing|ARM64.ActiveCfg = Debug|ARM64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Fuzzing|ARM64.Build.0 = Debug|ARM64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Fuzzing|x64.ActiveCfg = Debug|x64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Fuzzing|x64.Build.0 = Debug|x64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Fuzzing|x86.ActiveCfg = Debug|Win32 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Fuzzing|x86.Build.0 = Debug|Win32 - {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Release|Any CPU.ActiveCfg = Release|Win32 - {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Release|ARM.ActiveCfg = Release|ARM - {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Release|ARM.Build.0 = Release|ARM {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Release|ARM64.ActiveCfg = Release|ARM64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Release|ARM64.Build.0 = Release|ARM64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Release|x64.ActiveCfg = Release|x64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Release|x64.Build.0 = Release|x64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Release|x86.ActiveCfg = Release|Win32 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.Release|x86.Build.0 = Release|Win32 - {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.TestRelease|Any CPU.ActiveCfg = Debug|Win32 - {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.TestRelease|Any CPU.Build.0 = Debug|Win32 - {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.TestRelease|ARM.ActiveCfg = Release|ARM - {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.TestRelease|ARM.Build.0 = Release|ARM {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.TestRelease|ARM64.ActiveCfg = Release|ARM64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.TestRelease|ARM64.Build.0 = Release|ARM64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.TestRelease|x64.ActiveCfg = Release|x64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.TestRelease|x64.Build.0 = Release|x64 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.TestRelease|x86.ActiveCfg = Release|Win32 {2046B5AF-666D-4CE8-8D3E-C32C57908A56}.TestRelease|x86.Build.0 = Release|Win32 - {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Debug|ARM.ActiveCfg = Debug|Win32 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Debug|ARM64.ActiveCfg = Debug|Win32 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Debug|x64.ActiveCfg = Debug|x64 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Debug|x64.Build.0 = Debug|x64 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Debug|x86.ActiveCfg = Debug|Win32 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Debug|x86.Build.0 = Debug|Win32 - {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Fuzzing|Any CPU.ActiveCfg = Debug|Win32 - {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Fuzzing|Any CPU.Build.0 = Debug|Win32 - {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Fuzzing|ARM.ActiveCfg = Debug|Win32 - {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Fuzzing|ARM.Build.0 = Debug|Win32 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Fuzzing|ARM64.ActiveCfg = Debug|Win32 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Fuzzing|ARM64.Build.0 = Debug|Win32 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Fuzzing|x64.ActiveCfg = Debug|x64 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Fuzzing|x64.Build.0 = Debug|x64 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Fuzzing|x86.ActiveCfg = Debug|Win32 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Fuzzing|x86.Build.0 = Debug|Win32 - {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Release|Any CPU.ActiveCfg = Release|Win32 - {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Release|ARM.ActiveCfg = Release|Win32 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Release|ARM64.ActiveCfg = Release|Win32 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Release|x64.ActiveCfg = Release|x64 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Release|x64.Build.0 = Release|x64 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Release|x86.ActiveCfg = Release|Win32 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.Release|x86.Build.0 = Release|Win32 - {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.TestRelease|Any CPU.ActiveCfg = Debug|Win32 - {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.TestRelease|Any CPU.Build.0 = Debug|Win32 - {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.TestRelease|ARM.ActiveCfg = Debug|Win32 - {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.TestRelease|ARM.Build.0 = Debug|Win32 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.TestRelease|ARM64.ActiveCfg = Debug|Win32 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.TestRelease|ARM64.Build.0 = Debug|Win32 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.TestRelease|x64.ActiveCfg = Release|x64 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.TestRelease|x64.Build.0 = Release|x64 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.TestRelease|x86.ActiveCfg = Release|Win32 {9AC3C6A4-1875-4D3E-BF9C-C31E81EFF6B4}.TestRelease|x86.Build.0 = Release|Win32 - {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Debug|Any CPU.ActiveCfg = Debug|x64 - {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Debug|ARM.ActiveCfg = Debug|ARM - {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Debug|ARM.Build.0 = Debug|ARM {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Debug|ARM64.ActiveCfg = Debug|ARM64 {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Debug|ARM64.Build.0 = Debug|ARM64 {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Debug|x64.ActiveCfg = Debug|x64 {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Debug|x64.Build.0 = Debug|x64 {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Debug|x86.ActiveCfg = Debug|x86 {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Debug|x86.Build.0 = Debug|x86 - {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Fuzzing|Any CPU.ActiveCfg = Debug|x64 - {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Fuzzing|ARM.ActiveCfg = Debug|ARM {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Fuzzing|ARM64.ActiveCfg = Debug|ARM64 {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Fuzzing|ARM64.Build.0 = Debug|ARM64 {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Fuzzing|x64.ActiveCfg = Debug|x64 {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Fuzzing|x86.ActiveCfg = Debug|x86 {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Fuzzing|x86.Build.0 = Debug|x86 - {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Release|Any CPU.ActiveCfg = Release|x64 - {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Release|ARM.ActiveCfg = Release|ARM - {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Release|ARM.Build.0 = Release|ARM {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Release|ARM64.ActiveCfg = Release|ARM64 {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Release|ARM64.Build.0 = Release|ARM64 {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Release|x64.ActiveCfg = Release|x64 {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Release|x64.Build.0 = Release|x64 {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Release|x86.ActiveCfg = Release|x86 {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.Release|x86.Build.0 = Release|x86 - {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.TestRelease|Any CPU.ActiveCfg = Debug|x64 - {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.TestRelease|ARM.ActiveCfg = Debug|ARM {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.TestRelease|ARM64.ActiveCfg = Release|ARM64 {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.TestRelease|ARM64.Build.0 = Release|ARM64 {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.TestRelease|x64.ActiveCfg = Release|x64 {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.TestRelease|x64.Build.0 = Release|x64 {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.TestRelease|x86.ActiveCfg = Release|x86 {463C0EF3-DF38-4C3D-8E7E-D4901E0CDC6C}.TestRelease|x86.Build.0 = Release|x86 - {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Debug|Any CPU.ActiveCfg = Debug|x86 - {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Debug|ARM.ActiveCfg = Debug|x86 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Debug|ARM64.ActiveCfg = Debug|x86 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Debug|x64.ActiveCfg = Debug|x64 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Debug|x64.Build.0 = Debug|x64 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Debug|x86.ActiveCfg = Debug|x86 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Debug|x86.Build.0 = Debug|x86 - {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Fuzzing|Any CPU.ActiveCfg = TestOnly|x64 - {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Fuzzing|Any CPU.Build.0 = TestOnly|x64 - {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Fuzzing|ARM.ActiveCfg = Release|x86 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Fuzzing|ARM64.ActiveCfg = Release|x86 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Fuzzing|x64.ActiveCfg = Release|x64 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Fuzzing|x86.ActiveCfg = Release|x86 - {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Release|Any CPU.ActiveCfg = Release|x86 - {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Release|ARM.ActiveCfg = Release|x86 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Release|ARM64.ActiveCfg = Release|x86 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Release|x64.ActiveCfg = Release|x64 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Release|x64.Build.0 = Release|x64 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Release|x86.ActiveCfg = Release|x86 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.Release|x86.Build.0 = Release|x86 - {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.TestRelease|Any CPU.ActiveCfg = TestOnly|x64 - {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.TestRelease|Any CPU.Build.0 = TestOnly|x64 - {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.TestRelease|ARM.ActiveCfg = TestOnly|x64 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.TestRelease|ARM64.ActiveCfg = TestOnly|x64 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.TestRelease|x64.ActiveCfg = Release|x64 {0B104762-5CD8-47EE-A904-71C1C3F84DCD}.TestRelease|x86.ActiveCfg = Release|x86 - {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Debug|Any CPU.ActiveCfg = Debug|x64 - {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Debug|ARM.ActiveCfg = Debug|ARM - {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Debug|ARM.Build.0 = Debug|ARM {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Debug|ARM64.ActiveCfg = Debug|ARM64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Debug|ARM64.Build.0 = Debug|ARM64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Debug|x64.ActiveCfg = Debug|x64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Debug|x64.Build.0 = Debug|x64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Debug|x86.ActiveCfg = Debug|Win32 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Debug|x86.Build.0 = Debug|Win32 - {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Fuzzing|Any CPU.ActiveCfg = Debug|x64 - {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Fuzzing|Any CPU.Build.0 = Debug|x64 - {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Fuzzing|ARM.ActiveCfg = Debug|ARM {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Fuzzing|ARM64.ActiveCfg = Debug|ARM64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Fuzzing|x64.ActiveCfg = Debug|x64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Fuzzing|x86.ActiveCfg = Debug|Win32 - {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Release|Any CPU.ActiveCfg = Release|x64 - {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Release|ARM.ActiveCfg = Release|ARM - {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Release|ARM.Build.0 = Release|ARM {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Release|ARM64.ActiveCfg = Release|ARM64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Release|ARM64.Build.0 = Release|ARM64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Release|x64.ActiveCfg = Release|x64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Release|x64.Build.0 = Release|x64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Release|x86.ActiveCfg = Release|Win32 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.Release|x86.Build.0 = Release|Win32 - {31ED69A8-5310-45A9-953F-56C351D2C3E1}.TestRelease|Any CPU.ActiveCfg = Release|x64 - {31ED69A8-5310-45A9-953F-56C351D2C3E1}.TestRelease|Any CPU.Build.0 = Release|x64 - {31ED69A8-5310-45A9-953F-56C351D2C3E1}.TestRelease|ARM.ActiveCfg = Release|ARM {31ED69A8-5310-45A9-953F-56C351D2C3E1}.TestRelease|ARM64.ActiveCfg = Release|ARM64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.TestRelease|x64.ActiveCfg = Release|x64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.TestRelease|x64.Build.0 = Release|x64 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.TestRelease|x86.ActiveCfg = Release|Win32 {31ED69A8-5310-45A9-953F-56C351D2C3E1}.TestRelease|x86.Build.0 = Release|Win32 - {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.Debug|Any CPU.ActiveCfg = Debug|x64 - {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.Debug|ARM.ActiveCfg = Debug|ARM - {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.Debug|ARM.Build.0 = Debug|ARM {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.Debug|ARM64.ActiveCfg = Debug|ARM64 {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.Debug|ARM64.Build.0 = Debug|ARM64 {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.Debug|x64.ActiveCfg = Debug|x64 {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.Debug|x64.Build.0 = Debug|x64 {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.Debug|x86.ActiveCfg = Debug|Win32 {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.Debug|x86.Build.0 = Debug|Win32 - {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.Fuzzing|Any CPU.ActiveCfg = Debug|x64 - {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.Fuzzing|Any CPU.Build.0 = Debug|x64 - {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.Fuzzing|ARM.ActiveCfg = Debug|ARM {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.Fuzzing|ARM64.ActiveCfg = Debug|ARM64 {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.Fuzzing|x64.ActiveCfg = Debug|x64 {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.Fuzzing|x86.ActiveCfg = Debug|Win32 - {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.Release|Any CPU.ActiveCfg = Release|x64 - {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.Release|ARM.ActiveCfg = Release|ARM - {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.Release|ARM.Build.0 = Release|ARM {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.Release|ARM64.ActiveCfg = Release|ARM64 {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.Release|ARM64.Build.0 = Release|ARM64 {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.Release|x64.ActiveCfg = Release|x64 {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.Release|x64.Build.0 = Release|x64 {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.Release|x86.ActiveCfg = Release|Win32 {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.Release|x86.Build.0 = Release|Win32 - {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.TestRelease|Any CPU.ActiveCfg = Release|x64 - {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.TestRelease|Any CPU.Build.0 = Release|x64 - {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.TestRelease|ARM.ActiveCfg = Release|ARM {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.TestRelease|ARM64.ActiveCfg = Release|ARM64 {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.TestRelease|x64.ActiveCfg = Release|x64 {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.TestRelease|x64.Build.0 = Release|x64 {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.TestRelease|x86.ActiveCfg = Release|Win32 {787EC629-C0FB-4BA9-9746-4A82CD06B73E}.TestRelease|x86.Build.0 = Release|Win32 - {BB14D603-F44E-4415-8770-BF3E13F4C17F}.Debug|Any CPU.ActiveCfg = Debug|x64 - {BB14D603-F44E-4415-8770-BF3E13F4C17F}.Debug|ARM.ActiveCfg = Debug|ARM - {BB14D603-F44E-4415-8770-BF3E13F4C17F}.Debug|ARM.Build.0 = Debug|ARM {BB14D603-F44E-4415-8770-BF3E13F4C17F}.Debug|ARM64.ActiveCfg = Debug|ARM64 {BB14D603-F44E-4415-8770-BF3E13F4C17F}.Debug|ARM64.Build.0 = Debug|ARM64 {BB14D603-F44E-4415-8770-BF3E13F4C17F}.Debug|x64.ActiveCfg = Debug|x64 {BB14D603-F44E-4415-8770-BF3E13F4C17F}.Debug|x64.Build.0 = Debug|x64 {BB14D603-F44E-4415-8770-BF3E13F4C17F}.Debug|x86.ActiveCfg = Debug|Win32 {BB14D603-F44E-4415-8770-BF3E13F4C17F}.Debug|x86.Build.0 = Debug|Win32 - {BB14D603-F44E-4415-8770-BF3E13F4C17F}.Fuzzing|Any CPU.ActiveCfg = Debug|x64 - {BB14D603-F44E-4415-8770-BF3E13F4C17F}.Fuzzing|ARM.ActiveCfg = Debug|ARM {BB14D603-F44E-4415-8770-BF3E13F4C17F}.Fuzzing|ARM64.ActiveCfg = Debug|ARM64 {BB14D603-F44E-4415-8770-BF3E13F4C17F}.Fuzzing|x64.ActiveCfg = Debug|x64 {BB14D603-F44E-4415-8770-BF3E13F4C17F}.Fuzzing|x86.ActiveCfg = Debug|Win32 - {BB14D603-F44E-4415-8770-BF3E13F4C17F}.Release|Any CPU.ActiveCfg = Release|x64 - {BB14D603-F44E-4415-8770-BF3E13F4C17F}.Release|ARM.ActiveCfg = Release|ARM - {BB14D603-F44E-4415-8770-BF3E13F4C17F}.Release|ARM.Build.0 = Release|ARM {BB14D603-F44E-4415-8770-BF3E13F4C17F}.Release|ARM64.ActiveCfg = Release|ARM64 {BB14D603-F44E-4415-8770-BF3E13F4C17F}.Release|ARM64.Build.0 = Release|ARM64 {BB14D603-F44E-4415-8770-BF3E13F4C17F}.Release|x64.ActiveCfg = Release|x64 {BB14D603-F44E-4415-8770-BF3E13F4C17F}.Release|x64.Build.0 = Release|x64 {BB14D603-F44E-4415-8770-BF3E13F4C17F}.Release|x86.ActiveCfg = Release|Win32 {BB14D603-F44E-4415-8770-BF3E13F4C17F}.Release|x86.Build.0 = Release|Win32 - {BB14D603-F44E-4415-8770-BF3E13F4C17F}.TestRelease|Any CPU.ActiveCfg = Release|x64 - {BB14D603-F44E-4415-8770-BF3E13F4C17F}.TestRelease|Any CPU.Build.0 = Release|x64 - {BB14D603-F44E-4415-8770-BF3E13F4C17F}.TestRelease|ARM.ActiveCfg = Release|ARM - {BB14D603-F44E-4415-8770-BF3E13F4C17F}.TestRelease|ARM.Build.0 = Release|ARM {BB14D603-F44E-4415-8770-BF3E13F4C17F}.TestRelease|ARM64.ActiveCfg = Release|ARM64 {BB14D603-F44E-4415-8770-BF3E13F4C17F}.TestRelease|ARM64.Build.0 = Release|ARM64 {BB14D603-F44E-4415-8770-BF3E13F4C17F}.TestRelease|x64.ActiveCfg = Release|x64 {BB14D603-F44E-4415-8770-BF3E13F4C17F}.TestRelease|x64.Build.0 = Release|x64 {BB14D603-F44E-4415-8770-BF3E13F4C17F}.TestRelease|x86.ActiveCfg = Release|Win32 {BB14D603-F44E-4415-8770-BF3E13F4C17F}.TestRelease|x86.Build.0 = Release|Win32 - {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Debug|Any CPU.ActiveCfg = Debug|x64 - {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Debug|Any CPU.Build.0 = Debug|x64 - {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Debug|ARM.ActiveCfg = Debug|ARM - {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Debug|ARM.Build.0 = Debug|ARM {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Debug|ARM64.ActiveCfg = Debug|ARM64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Debug|ARM64.Build.0 = Debug|ARM64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Debug|x64.ActiveCfg = Debug|x64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Debug|x64.Build.0 = Debug|x64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Debug|x86.ActiveCfg = Debug|Win32 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Debug|x86.Build.0 = Debug|Win32 - {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|x64 - {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Fuzzing|Any CPU.Build.0 = Fuzzing|x64 - {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Fuzzing|ARM.ActiveCfg = Fuzzing|x64 - {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Fuzzing|ARM.Build.0 = Fuzzing|x64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Fuzzing|ARM64.ActiveCfg = Fuzzing|x64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Fuzzing|ARM64.Build.0 = Fuzzing|x64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Fuzzing|x64.Build.0 = Fuzzing|x64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Fuzzing|x86.ActiveCfg = Fuzzing|x64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Fuzzing|x86.Build.0 = Fuzzing|x64 - {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Release|Any CPU.ActiveCfg = Release|x64 - {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Release|Any CPU.Build.0 = Release|x64 - {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Release|ARM.ActiveCfg = Release|ARM - {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Release|ARM.Build.0 = Release|ARM {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Release|ARM64.ActiveCfg = Release|ARM64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Release|ARM64.Build.0 = Release|ARM64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Release|x64.ActiveCfg = Release|x64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Release|x64.Build.0 = Release|x64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Release|x86.ActiveCfg = Release|Win32 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.Release|x86.Build.0 = Release|Win32 - {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.TestRelease|Any CPU.ActiveCfg = Release|x64 - {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.TestRelease|Any CPU.Build.0 = Release|x64 - {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.TestRelease|ARM.ActiveCfg = Release|ARM - {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.TestRelease|ARM.Build.0 = Release|ARM {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.TestRelease|ARM64.ActiveCfg = Release|ARM64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.TestRelease|ARM64.Build.0 = Release|ARM64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.TestRelease|x64.ActiveCfg = Release|x64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.TestRelease|x64.Build.0 = Release|x64 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.TestRelease|x86.ActiveCfg = Release|Win32 {F3F6E699-BC5D-4950-8A05-E49DD9EB0D51}.TestRelease|x86.Build.0 = Release|Win32 + {CA460806-5E41-4E97-9A3D-1D74B433B663}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {CA460806-5E41-4E97-9A3D-1D74B433B663}.Debug|ARM64.Build.0 = Debug|ARM64 + {CA460806-5E41-4E97-9A3D-1D74B433B663}.Debug|x64.ActiveCfg = Debug|x64 + {CA460806-5E41-4E97-9A3D-1D74B433B663}.Debug|x64.Build.0 = Debug|x64 + {CA460806-5E41-4E97-9A3D-1D74B433B663}.Debug|x86.ActiveCfg = Debug|Win32 + {CA460806-5E41-4E97-9A3D-1D74B433B663}.Debug|x86.Build.0 = Debug|Win32 + {CA460806-5E41-4E97-9A3D-1D74B433B663}.Fuzzing|ARM64.ActiveCfg = Debug|ARM64 + {CA460806-5E41-4E97-9A3D-1D74B433B663}.Fuzzing|ARM64.Build.0 = Debug|ARM64 + {CA460806-5E41-4E97-9A3D-1D74B433B663}.Fuzzing|x64.ActiveCfg = Debug|x64 + {CA460806-5E41-4E97-9A3D-1D74B433B663}.Fuzzing|x64.Build.0 = Debug|x64 + {CA460806-5E41-4E97-9A3D-1D74B433B663}.Fuzzing|x86.ActiveCfg = Debug|Win32 + {CA460806-5E41-4E97-9A3D-1D74B433B663}.Fuzzing|x86.Build.0 = Debug|Win32 + {CA460806-5E41-4E97-9A3D-1D74B433B663}.Release|ARM64.ActiveCfg = Release|ARM64 + {CA460806-5E41-4E97-9A3D-1D74B433B663}.Release|ARM64.Build.0 = Release|ARM64 + {CA460806-5E41-4E97-9A3D-1D74B433B663}.Release|x64.ActiveCfg = Release|x64 + {CA460806-5E41-4E97-9A3D-1D74B433B663}.Release|x64.Build.0 = Release|x64 + {CA460806-5E41-4E97-9A3D-1D74B433B663}.Release|x86.ActiveCfg = Release|Win32 + {CA460806-5E41-4E97-9A3D-1D74B433B663}.Release|x86.Build.0 = Release|Win32 + {CA460806-5E41-4E97-9A3D-1D74B433B663}.TestRelease|ARM64.ActiveCfg = Release|ARM64 + {CA460806-5E41-4E97-9A3D-1D74B433B663}.TestRelease|ARM64.Build.0 = Release|ARM64 + {CA460806-5E41-4E97-9A3D-1D74B433B663}.TestRelease|x64.ActiveCfg = Release|x64 + {CA460806-5E41-4E97-9A3D-1D74B433B663}.TestRelease|x64.Build.0 = Release|x64 + {CA460806-5E41-4E97-9A3D-1D74B433B663}.TestRelease|x86.ActiveCfg = Release|Win32 + {CA460806-5E41-4E97-9A3D-1D74B433B663}.TestRelease|x86.Build.0 = Release|Win32 + {E8454BF1-2068-4513-A525-ABF55CC8742C}.Debug|ARM64.ActiveCfg = Debug|arm64 + {E8454BF1-2068-4513-A525-ABF55CC8742C}.Debug|ARM64.Build.0 = Debug|arm64 + {E8454BF1-2068-4513-A525-ABF55CC8742C}.Debug|x64.ActiveCfg = Debug|x64 + {E8454BF1-2068-4513-A525-ABF55CC8742C}.Debug|x64.Build.0 = Debug|x64 + {E8454BF1-2068-4513-A525-ABF55CC8742C}.Debug|x86.ActiveCfg = Debug|x86 + {E8454BF1-2068-4513-A525-ABF55CC8742C}.Debug|x86.Build.0 = Debug|x86 + {E8454BF1-2068-4513-A525-ABF55CC8742C}.Fuzzing|ARM64.ActiveCfg = Debug|Any CPU + {E8454BF1-2068-4513-A525-ABF55CC8742C}.Fuzzing|ARM64.Build.0 = Debug|Any CPU + {E8454BF1-2068-4513-A525-ABF55CC8742C}.Fuzzing|x64.ActiveCfg = Debug|Any CPU + {E8454BF1-2068-4513-A525-ABF55CC8742C}.Fuzzing|x64.Build.0 = Debug|Any CPU + {E8454BF1-2068-4513-A525-ABF55CC8742C}.Fuzzing|x86.ActiveCfg = Debug|Any CPU + {E8454BF1-2068-4513-A525-ABF55CC8742C}.Fuzzing|x86.Build.0 = Debug|Any CPU + {E8454BF1-2068-4513-A525-ABF55CC8742C}.Release|ARM64.ActiveCfg = Release|Any CPU + {E8454BF1-2068-4513-A525-ABF55CC8742C}.Release|ARM64.Build.0 = Release|Any CPU + {E8454BF1-2068-4513-A525-ABF55CC8742C}.Release|x64.ActiveCfg = Release|Any CPU + {E8454BF1-2068-4513-A525-ABF55CC8742C}.Release|x64.Build.0 = Release|Any CPU + {E8454BF1-2068-4513-A525-ABF55CC8742C}.Release|x86.ActiveCfg = Release|Any CPU + {E8454BF1-2068-4513-A525-ABF55CC8742C}.Release|x86.Build.0 = Release|Any CPU + {E8454BF1-2068-4513-A525-ABF55CC8742C}.TestRelease|ARM64.ActiveCfg = Release|Any CPU + {E8454BF1-2068-4513-A525-ABF55CC8742C}.TestRelease|ARM64.Build.0 = Release|Any CPU + {E8454BF1-2068-4513-A525-ABF55CC8742C}.TestRelease|x64.ActiveCfg = Release|Any CPU + {E8454BF1-2068-4513-A525-ABF55CC8742C}.TestRelease|x64.Build.0 = Release|Any CPU + {E8454BF1-2068-4513-A525-ABF55CC8742C}.TestRelease|x86.ActiveCfg = Release|Any CPU + {E8454BF1-2068-4513-A525-ABF55CC8742C}.TestRelease|x86.Build.0 = Release|Any CPU + {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Debug|ARM64.ActiveCfg = Debug|arm64 + {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Debug|ARM64.Build.0 = Debug|arm64 + {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Debug|x64.ActiveCfg = Debug|x64 + {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Debug|x64.Build.0 = Debug|x64 + {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Debug|x86.ActiveCfg = Debug|x86 + {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Debug|x86.Build.0 = Debug|x86 + {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Fuzzing|ARM64.ActiveCfg = Debug|arm64 + {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Fuzzing|ARM64.Build.0 = Debug|arm64 + {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Fuzzing|x64.ActiveCfg = Debug|x64 + {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Fuzzing|x64.Build.0 = Debug|x64 + {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Fuzzing|x86.ActiveCfg = Debug|x86 + {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Fuzzing|x86.Build.0 = Debug|x86 + {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Release|ARM64.ActiveCfg = Release|arm64 + {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Release|ARM64.Build.0 = Release|arm64 + {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Release|x64.ActiveCfg = Release|x64 + {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Release|x64.Build.0 = Release|x64 + {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Release|x86.ActiveCfg = Release|x86 + {EE43C990-7789-4A60-B077-BF0ED3D093A1}.Release|x86.Build.0 = Release|x86 + {EE43C990-7789-4A60-B077-BF0ED3D093A1}.TestRelease|ARM64.ActiveCfg = Release|arm64 + {EE43C990-7789-4A60-B077-BF0ED3D093A1}.TestRelease|ARM64.Build.0 = Release|arm64 + {EE43C990-7789-4A60-B077-BF0ED3D093A1}.TestRelease|x64.ActiveCfg = Release|x64 + {EE43C990-7789-4A60-B077-BF0ED3D093A1}.TestRelease|x64.Build.0 = Release|x64 + {EE43C990-7789-4A60-B077-BF0ED3D093A1}.TestRelease|x86.ActiveCfg = Release|x86 + {EE43C990-7789-4A60-B077-BF0ED3D093A1}.TestRelease|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/AppInstallerCLITests/Versions.cpp b/src/AppInstallerCLITests/Versions.cpp @@ -334,4 +334,36 @@ TEST_CASE("GatedVersion", "[versions]") REQUIRE(GatedVersion("1.0.1"sv).IsValidVersion({ "1.0.1" })); REQUIRE_FALSE(GatedVersion("1.0.1"sv).IsValidVersion({ "1.1.1" })); -}- \ No newline at end of file +} + +TEST_CASE("SemanticVersion", "[versions]") +{ + REQUIRE_THROWS_HR(SemanticVersion("1.2.3.4"), E_INVALIDARG); + REQUIRE_THROWS_HR(SemanticVersion("1.2abc.3"), E_INVALIDARG); + + SemanticVersion version = SemanticVersion("1.2.3-alpha"); + REQUIRE(version.IsPrerelease()); + REQUIRE(version.PrereleaseVersion() == Version("alpha")); + REQUIRE(!version.HasBuildMetadata()); + REQUIRE(version.PartAt(2).Other == "-alpha"); + + version = SemanticVersion("1.2.3-4.5.6"); + REQUIRE(version.IsPrerelease()); + REQUIRE(version.PrereleaseVersion() == Version("4.5.6")); + REQUIRE(!version.HasBuildMetadata()); + REQUIRE(version.PartAt(2).Other == "-4.5.6"); + + // Really shouldn't be allowed, but we are loose here + version = SemanticVersion("1.2+build"); + REQUIRE(!version.IsPrerelease()); + REQUIRE(version.HasBuildMetadata()); + REQUIRE(version.BuildMetadata() == Version("build")); + REQUIRE(version.PartAt(2).Other == "+build"); + + version = SemanticVersion("1.2.3-beta+4.5.6"); + REQUIRE(version.IsPrerelease()); + REQUIRE(version.PrereleaseVersion() == Version("beta")); + REQUIRE(version.HasBuildMetadata()); + REQUIRE(version.BuildMetadata() == Version("4.5.6")); + REQUIRE(version.PartAt(2).Other == "-beta+4.5.6"); +} diff --git a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj @@ -299,7 +299,6 @@ <ClInclude Include="Public\AppInstallerSynchronization.h" /> <ClInclude Include="Public\AppInstallerTelemetry.h" /> <ClInclude Include="Public\AppInstallerArchitecture.h" /> - <ClInclude Include="Public\AppInstallerVersions.h" /> <ClInclude Include="Public\winget\Archive.h" /> <ClInclude Include="Public\winget\ExperimentalFeature.h" /> <ClInclude Include="Public\winget\ExtensionCatalog.h" /> @@ -398,7 +397,6 @@ <ClCompile Include="ThreadGlobals.cpp" /> <ClCompile Include="TraceLogger.cpp" /> <ClCompile Include="UserSettings.cpp" /> - <ClCompile Include="Versions.cpp" /> </ItemGroup> <ItemGroup> <None Include="packages.config" /> diff --git a/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters b/src/AppInstallerCommonCore/AppInstallerCommonCore.vcxproj.filters @@ -78,9 +78,6 @@ <ClInclude Include="Public\AppInstallerProgress.h"> <Filter>Public</Filter> </ClInclude> - <ClInclude Include="Public\AppInstallerVersions.h"> - <Filter>Public</Filter> - </ClInclude> <ClInclude Include="Public\winget\ExtensionCatalog.h"> <Filter>Public\winget</Filter> </ClInclude> @@ -233,9 +230,6 @@ <ClCompile Include="Deployment.cpp"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="Versions.cpp"> - <Filter>Source Files</Filter> - </ClCompile> <ClCompile Include="ExtensionCatalog.cpp"> <Filter>Source Files</Filter> </ClCompile> diff --git a/src/AppInstallerCommonCore/Public/AppInstallerVersions.h b/src/AppInstallerCommonCore/Public/AppInstallerVersions.h @@ -1,243 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#pragma once -#include <string> -#include <string_view> -#include <vector> - -namespace AppInstaller::Utility -{ - using namespace std::string_view_literals; - - // Creates a comparable version object from a string. - // Versions are parsed by: - // 1. Parse approximate comparator sign if applicable - // 2. Splitting the string based on the given splitChars (or DefaultSplitChars) - // 3. Parsing a leading, positive integer from each split part - // 4. Saving any remaining, non-digits as a supplemental value - // - // Versions are compared by: - // for each part in each version - // if both sides have no more parts, return equal - // else if one side has no more parts, it is less - // else if integers not equal, return comparison of integers - // else if only one side has a non-empty string part, it is less - // else if string parts not equal, return comparison of strings - // if all parts are same, use approximate comparator if applicable - // - // Note: approximate to another approximate version is invalid. - // approximate to Unknown is invalid. - struct Version - { - // Used in approximate version to indicate the relation to the base version. - enum class ApproximateComparator - { - None, - LessThan, - GreaterThan, - }; - - // The default characters to split a version string on. - constexpr static std::string_view DefaultSplitChars = "."sv; - - Version() = default; - - Version(const std::string& version, std::string_view splitChars = DefaultSplitChars) : - Version(std::string(version), splitChars) {} - Version(std::string&& version, std::string_view splitChars = DefaultSplitChars); - - // Constructing an approximate version from a base version. - Version(Version baseVersion, ApproximateComparator approximateComparator); - - // Resets the version's value to the input. - virtual void Assign(std::string version, std::string_view splitChars = DefaultSplitChars); - - // Gets the full version string used to construct the Version. - const std::string& ToString() const { return m_version; } - - bool operator<(const Version& other) const; - bool operator>(const Version& other) const; - bool operator<=(const Version& other) const; - bool operator>=(const Version& other) const; - bool operator==(const Version& other) const; - bool operator!=(const Version& other) const; - - // Determines if this version is the sentinel value defining the 'Latest' version - bool IsLatest() const; - - // Returns a Version that will return true for IsLatest - static Version CreateLatest(); - - // Determines if this version is the sentinel value defining an 'Unknown' version - bool IsUnknown() const; - - // Returns a Version that will return true for IsUnknown - static Version CreateUnknown(); - - // Gets a bool indicating whether the full version string is empty. - // Does not indicate that Parts is empty; for instance when "0.0" is given, - // this will be false while GetParts().empty() would be true. - bool IsEmpty() const { return m_version.empty(); } - - // An individual version part in between split characters. - struct Part - { - Part() = default; - Part(uint64_t integer) : Integer(integer) {} - Part(const std::string& part); - Part(uint64_t integer, std::string other); - - bool operator<(const Part& other) const; - bool operator==(const Part& other) const; - bool operator!=(const Part& other) const; - - uint64_t Integer = 0; - std::string Other; - }; - - // Gets the part breakdown for a given version. - const std::vector<Part>& GetParts() const { return m_parts; } - - // Gets the part at the given index; or the implied zero part if past the end. - const Part& PartAt(size_t index) const; - - // Returns if the version is an approximate version. - bool IsApproximate() const { return m_approximateComparator != ApproximateComparator::None; } - - // Get the base version from approximate version, or return a copy if the version is not approximate. - Version GetBaseVersion() const; - - protected: - - bool IsBaseVersionLatest() const; - bool IsBaseVersionUnknown() const; - // Called by overloaded less than operator implementation when base version already compared and equal, less than determined by approximate comparator. - bool ApproximateCompareLessThan(const Version& other) const; - - std::string m_version; - std::vector<Part> m_parts; - ApproximateComparator m_approximateComparator = ApproximateComparator::None; - - // Remove trailing empty parts (0 or empty) - void Trim(); - }; - - // Four parts version number: 16-bits.16-bits.16-bits.16-bits - struct UInt64Version : public Version - { - UInt64Version() = default; - UInt64Version(UINT64 version); - UInt64Version(std::string&& version, std::string_view splitChars = DefaultSplitChars); - UInt64Version(const std::string& version, std::string_view splitChars = DefaultSplitChars) : - UInt64Version(std::string(version), splitChars) {} - - void Assign(std::string version, std::string_view splitChars = DefaultSplitChars) override; - void Assign(UINT64 version); - - UINT64 Major() const { return m_parts.size() > 0 ? m_parts[0].Integer : 0; } - UINT64 Minor() const { return m_parts.size() > 1 ? m_parts[1].Integer : 0; } - UINT64 Build() const { return m_parts.size() > 2 ? m_parts[2].Integer : 0; } - UINT64 Revision() const { return m_parts.size() > 3 ? m_parts[3].Integer : 0; } - }; - - // Version range represented by a min version and max version, both inclusive. - struct VersionRange - { - VersionRange() { m_isEmpty = true; }; - VersionRange(Version minVersion, Version maxVersion); - - bool IsEmpty() const { return m_isEmpty; } - - // Checks if version ranges overlap. Empty version range does not overlap with any version range. - bool Overlaps(const VersionRange& other) const; - - // Checks if the version range is effectively the same as a single version. - bool IsSameAsSingleVersion(const Version& version) const; - - // Checks if a version is within the version range - bool ContainsVersion(const Version& version) const; - - // < operator will thow if compared with an empty range or an overlapped range - bool operator<(const VersionRange& other) const; - - const Version& GetMinVersion() const; - const Version& GetMaxVersion() const; - - private: - Version m_minVersion; - Version m_maxVersion; - bool m_isEmpty = false; - }; - - // A range of versions indicated by a version and optionally a wildcard at the end. - struct GatedVersion - { - GatedVersion() {} - GatedVersion(Version&& version) : m_version(std::move(version)) {} - GatedVersion(const Version& version) : m_version(version) {} - GatedVersion(std::string_view versionString) : m_version(std::string{ versionString }) {} - GatedVersion(const std::string& versionString) : m_version(versionString) {} - - // Determines whether a given version falls within this Gated version. - // I.e., whether it matches up to the wildcard - bool IsValidVersion(Version version) const; - - bool operator==(const GatedVersion& other) const { return m_version == other.m_version; } - const std::string& ToString() const { return m_version.ToString(); } - - private: - // Hold the version string as a Version object that makes it easy to access each of - // the version's parts. The real magic is in IsValidVersion() - Version m_version; - }; - - // A channel string; existing solely to give a type. - // - // Compared lexicographically. - struct Channel - { - Channel() = default; - Channel(const std::string& channel) : m_channel(channel) {} - Channel(std::string&& channel) : m_channel(std::move(channel)) {} - - const std::string& ToString() const { return m_channel; } - - bool operator<(const Channel& other) const; - - private: - std::string m_channel; - }; - - // Contains a version and channel. - // These are compared by: - // if channel not equal, return compare channel - // else return !compare version - // - // The implication of this is that the default less sort will be: - // 2.0, "" - // 1.0, "" - // 3.0, "alpha" - // 2.0, "alpha" - struct VersionAndChannel - { - VersionAndChannel() = default; - VersionAndChannel(Version&& version, Channel&& channel); - - const Version& GetVersion() const { return m_version; } - const Channel& GetChannel() const { return m_channel; } - - std::string ToString() const; - - bool operator<(const VersionAndChannel& other) const; - - // A convenience function to make more semantic sense at call sites over the somewhat awkward less than ordering. - bool IsUpdatedBy(const VersionAndChannel& other) const; - - private: - Version m_version; - Channel m_channel; - }; - - // Checks if there are overlaps within the list of version ranges - bool HasOverlapInVersionRanges(const std::vector<VersionRange>& ranges); -} diff --git a/src/AppInstallerCommonCore/Versions.cpp b/src/AppInstallerCommonCore/Versions.cpp @@ -1,559 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -#include "pch.h" -#include "Public/AppInstallerVersions.h" -#include "Public/AppInstallerStrings.h" - -namespace AppInstaller::Utility -{ - using namespace std::string_view_literals; - - static constexpr std::string_view s_Version_Part_Latest = "Latest"sv; - static constexpr std::string_view s_Version_Part_Unknown = "Unknown"sv; - - static constexpr std::string_view s_Approximate_Less_Than = "< "sv; - static constexpr std::string_view s_Approximate_Greater_Than = "> "sv; - - Version::Version(std::string&& version, std::string_view splitChars) - { - Assign(std::move(version), splitChars); - } - - Version::Version(Version baseVersion, ApproximateComparator approximateComparator) : Version(std::move(baseVersion)) - { - if (approximateComparator == ApproximateComparator::None) - { - return; - } - - THROW_HR_IF(E_INVALIDARG, this->IsApproximate() || this->IsUnknown()); - - m_approximateComparator = approximateComparator; - if (approximateComparator == ApproximateComparator::LessThan) - { - m_version = std::string{ s_Approximate_Less_Than } + m_version; - } - else if (approximateComparator == ApproximateComparator::GreaterThan) - { - m_version = std::string{ s_Approximate_Greater_Than } + m_version; - } - } - - void Version::Assign(std::string version, std::string_view splitChars) - { - m_version = std::move(version); - - // Process approximate comparator if applicable - std::string baseVersion = m_version; - if (CaseInsensitiveStartsWith(m_version, s_Approximate_Less_Than)) - { - m_approximateComparator = ApproximateComparator::LessThan; - baseVersion = m_version.substr(s_Approximate_Less_Than.length(), m_version.length() - s_Approximate_Less_Than.length()); - } - else if (CaseInsensitiveStartsWith(m_version, s_Approximate_Greater_Than)) - { - m_approximateComparator = ApproximateComparator::GreaterThan; - baseVersion = m_version.substr(s_Approximate_Greater_Than.length(), m_version.length() - s_Approximate_Greater_Than.length()); - } - - // Then parse the base version - size_t pos = 0; - - while (pos < baseVersion.length()) - { - size_t newPos = baseVersion.find_first_of(splitChars, pos); - - size_t length = (newPos == std::string::npos ? baseVersion.length() : newPos) - pos; - m_parts.emplace_back(baseVersion.substr(pos, length)); - - pos += length + 1; - } - - // Trim version parts - Trim(); - - THROW_HR_IF(E_INVALIDARG, m_approximateComparator != ApproximateComparator::None && IsBaseVersionUnknown()); - } - - void Version::Trim() - { - while (!m_parts.empty()) - { - const Part& part = m_parts.back(); - if (part.Integer == 0 && part.Other.empty()) - { - m_parts.pop_back(); - } - else - { - return; - } - } - } - - bool Version::operator<(const Version& other) const - { - // Sort Latest higher than any other values - bool thisIsLatest = IsBaseVersionLatest(); - bool otherIsLatest = other.IsBaseVersionLatest(); - - if (thisIsLatest && otherIsLatest) - { - return ApproximateCompareLessThan(other); - } - else if (thisIsLatest || otherIsLatest) - { - // If only one is latest, this can only be less than if the other is and this is not. - return (otherIsLatest && !thisIsLatest); - } - - // Sort Unknown lower than any known values - bool thisIsUnknown = IsBaseVersionUnknown(); - bool otherIsUnknown = other.IsBaseVersionUnknown(); - - if (thisIsUnknown && otherIsUnknown) - { - // This code path should always return false as we disable approximate version for Unknown for now - return ApproximateCompareLessThan(other); - } - else if (thisIsUnknown || otherIsUnknown) - { - // If at least one is unknown, this can only be less than if it is and the other is not. - return (thisIsUnknown && !otherIsUnknown); - } - - for (size_t i = 0; i < m_parts.size(); ++i) - { - if (i >= other.m_parts.size()) - { - // All parts equal to this point - break; - } - - const Part& partA = m_parts[i]; - const Part& partB = other.m_parts[i]; - - if (partA < partB) - { - return true; - } - else if (partB < partA) - { - return false; - } - // else parts are equal, so continue to next part - } - - // All parts tested were equal - if (m_parts.size() == other.m_parts.size()) - { - return ApproximateCompareLessThan(other); - } - else - { - // Else this is only less if there are more parts in other. - return m_parts.size() < other.m_parts.size(); - } - - } - - bool Version::operator>(const Version& other) const - { - return other < *this; - } - - bool Version::operator<=(const Version& other) const - { - return !(*this > other); - } - - bool Version::operator>=(const Version& other) const - { - return !(*this < other); - } - - bool Version::operator==(const Version& other) const - { - if (m_approximateComparator != other.m_approximateComparator) - { - return false; - } - - if ((IsBaseVersionLatest() && other.IsBaseVersionLatest()) || - (IsBaseVersionUnknown() && other.IsBaseVersionUnknown())) - { - return true; - } - - if (m_parts.size() != other.m_parts.size()) - { - return false; - } - - for (size_t i = 0; i < m_parts.size(); ++i) - { - if (m_parts[i] != other.m_parts[i]) - { - return false; - } - } - - return true; - } - - bool Version::operator!=(const Version& other) const - { - return !(*this == other); - } - - bool Version::IsLatest() const - { - return (m_approximateComparator != ApproximateComparator::LessThan && IsBaseVersionLatest()); - } - - Version Version::CreateLatest() - { - Version result; - result.m_version = s_Version_Part_Latest; - result.m_parts.emplace_back(0, std::string{ s_Version_Part_Latest }); - return result; - } - - bool Version::IsUnknown() const - { - return IsBaseVersionUnknown(); - } - - Version Version::CreateUnknown() - { - Version result; - result.m_version = s_Version_Part_Unknown; - result.m_parts.emplace_back(0, std::string{ s_Version_Part_Unknown }); - return result; - } - - const Version::Part& Version::PartAt(size_t index) const - { - static Part s_zero{}; - - if (index < m_parts.size()) - { - return m_parts[index]; - } - else - { - return s_zero; - } - } - - Version Version::GetBaseVersion() const - { - Version baseVersion = *this; - baseVersion.m_approximateComparator = ApproximateComparator::None; - if (m_approximateComparator == ApproximateComparator::LessThan) - { - baseVersion.m_version = m_version.substr(s_Approximate_Less_Than.size()); - } - else if (m_approximateComparator == ApproximateComparator::GreaterThan) - { - baseVersion.m_version = m_version.substr(s_Approximate_Greater_Than.size()); - } - - return baseVersion; - } - - bool Version::IsBaseVersionLatest() const - { - return (m_parts.size() == 1 && m_parts[0].Integer == 0 && Utility::CaseInsensitiveEquals(m_parts[0].Other, s_Version_Part_Latest)); - } - - bool Version::IsBaseVersionUnknown() const - { - return (m_parts.size() == 1 && m_parts[0].Integer == 0 && Utility::CaseInsensitiveEquals(m_parts[0].Other, s_Version_Part_Unknown)); - } - - bool Version::ApproximateCompareLessThan(const Version& other) const - { - // Only true if this is less than, other is not, OR this is none, other is greater than - return (m_approximateComparator == ApproximateComparator::LessThan && other.m_approximateComparator != ApproximateComparator::LessThan) || - (m_approximateComparator == ApproximateComparator::None && other.m_approximateComparator == ApproximateComparator::GreaterThan); - } - - Version::Part::Part(const std::string& part) - { - const char* begin = part.c_str(); - char* end = nullptr; - errno = 0; - Integer = strtoull(begin, &end, 10); - - if (errno == ERANGE) - { - Integer = 0; - Other = part; - } - else if (static_cast<size_t>(end - begin) != part.length()) - { - Other = end; - } - } - - Version::Part::Part(uint64_t integer, std::string other) : - Integer(integer), Other(std::move(other)) {} - - bool Version::Part::operator<(const Part& other) const - { - if (Integer < other.Integer) - { - return true; - } - else if (Integer > other.Integer) - { - return false; - } - else if (Other.empty()) - { - // If this Other is empty, it is at least >= - return false; - } - else if (!Other.empty() && other.Other.empty()) - { - // If the other Other is empty and this is not, this is less. - return true; - } - else if (Other < other.Other) - { - return true; - } - - // else Other >= other.Other - return false; - } - - bool Version::Part::operator==(const Part& other) const - { - return Integer == other.Integer && Other == other.Other; - } - - bool Version::Part::operator!=(const Part& other) const - { - return !(*this == other); - } - - bool Channel::operator<(const Channel& other) const - { - return m_channel < other.m_channel; - } - - VersionAndChannel::VersionAndChannel(Version&& version, Channel&& channel) : - m_version(std::move(version)), m_channel(std::move(channel)) {} - - std::string VersionAndChannel::ToString() const - { - std::string result; - result = m_version.ToString(); - if (!m_channel.ToString().empty()) - { - result += '['; - result += m_channel.ToString(); - result += ']'; - } - return result; - } - - bool VersionAndChannel::operator<(const VersionAndChannel& other) const - { - if (m_channel < other.m_channel) - { - return true; - } - else if (other.m_channel < m_channel) - { - return false; - } - // We intentionally invert the order for version here. - else if (other.m_version < m_version) - { - return true; - } - - // else m_version >= other.m_version - return false; - } - - bool VersionAndChannel::IsUpdatedBy(const VersionAndChannel& other) const - { - // Channel crossing should not happen here. - if (!Utility::ICUCaseInsensitiveEquals(m_channel.ToString(), other.m_channel.ToString())) - { - return false; - } - - return m_version < other.m_version; - } - - UInt64Version::UInt64Version(UINT64 version) - { - Assign(version); - } - - void UInt64Version::Assign(UINT64 version) - { - const UINT64 mask16 = (1 << 16) - 1; - UINT64 revision = version & mask16; - UINT64 build = (version >> 0x10) & mask16; - UINT64 minor = (version >> 0x20) & mask16; - UINT64 major = (version >> 0x30) & mask16; - - // Construct a string representation of the provided version - std::stringstream ssVersion; - ssVersion << major - << Version::DefaultSplitChars << minor - << Version::DefaultSplitChars << build - << Version::DefaultSplitChars << revision; - m_version = ssVersion.str(); - - // Construct the 4 parts - m_parts = { major, minor, build, revision }; - - // Trim version parts - Trim(); - } - - UInt64Version::UInt64Version(std::string&& version, std::string_view splitChars) - { - Assign(std::move(version), splitChars); - } - - void UInt64Version::Assign(std::string version, std::string_view splitChars) - { - Version::Assign(std::move(version), splitChars); - - // After trimming trailing parts (0 or empty), - // at most 4 parts must be present - THROW_HR_IF(E_INVALIDARG, m_parts.size() > 4); - for (const auto& part : m_parts) - { - // Check for non-empty Other part - THROW_HR_IF(E_INVALIDARG, !part.Other.empty()); - - // Check for overflow Integer part - THROW_HR_IF(E_INVALIDARG, part.Integer >> 16 != 0); - } - } - - VersionRange::VersionRange(Version minVersion, Version maxVersion) - { - THROW_HR_IF(E_INVALIDARG, minVersion > maxVersion); - m_minVersion = std::move(minVersion); - m_maxVersion = std::move(maxVersion); - } - - bool VersionRange::Overlaps(const VersionRange& other) const - { - // No overlap if either is an empty range. - if (IsEmpty() || other.IsEmpty()) - { - return false; - } - - return m_minVersion <= other.m_maxVersion && m_maxVersion >= other.m_minVersion; - } - - bool VersionRange::IsSameAsSingleVersion(const Version& version) const - { - if (IsEmpty()) - { - return false; - } - - return m_minVersion == version && m_maxVersion == version; - } - - bool VersionRange::ContainsVersion(const Version& version) const - { - if (IsEmpty()) - { - return false; - } - - return version >= m_minVersion && version <= m_maxVersion; - } - - bool VersionRange::operator<(const VersionRange& other) const - { - THROW_HR_IF(E_INVALIDARG, IsEmpty() || other.IsEmpty() || Overlaps(other)); - - return m_minVersion < other.m_minVersion; - } - - const Version& VersionRange::GetMinVersion() const - { - THROW_HR_IF(E_NOT_VALID_STATE, IsEmpty()); - return m_minVersion; - } - - const Version& VersionRange::GetMaxVersion() const - { - THROW_HR_IF(E_NOT_VALID_STATE, IsEmpty()); - return m_maxVersion; - } - - bool GatedVersion::IsValidVersion(Version version) const - { - auto gateParts = m_version.GetParts(); - if (gateParts.empty()) - { - return false; - } - - if (gateParts.back() != Version::Part("*")) - { - // Without wildcards, revert to direct comparison - return m_version == version; - } - - auto versionParts = version.GetParts(); - for (size_t i = 0; i < gateParts.size() - 1; ++i) - { - if (versionParts.size() > i) - { - if (gateParts[i] == versionParts[i]) - { - continue; - } - else - { - // Mismatch with the gated version - return false; - } - } - else - { - // Assume trailing 0s on the version - if (gateParts[i] != Version::Part(0)) - { - return false; - } - } - } - - // All version parts matched - return true; - } - - bool HasOverlapInVersionRanges(const std::vector<VersionRange>& ranges) - { - for (size_t i = 0; i < ranges.size(); i++) - { - for (size_t j = i + 1; j < ranges.size(); j++) - { - if (ranges[i].Overlaps(ranges[j])) - { - return true; - } - } - } - - return false; - } -} diff --git a/src/AppInstallerSharedLib/AppInstallerLogging.cpp b/src/AppInstallerSharedLib/AppInstallerLogging.cpp @@ -33,14 +33,15 @@ namespace AppInstaller::Logging { switch(channel) { - case Channel::Fail: return "FAIL"; - case Channel::CLI: return "CLI"; - case Channel::SQL: return "SQL"; - case Channel::Repo: return "REPO"; - case Channel::YAML: return "YAML"; - case Channel::Core: return "CORE"; - case Channel::Test: return "TEST"; - default: return "NONE"; + case Channel::Fail: return "FAIL"; + case Channel::CLI: return "CLI"; + case Channel::SQL: return "SQL"; + case Channel::Repo: return "REPO"; + case Channel::YAML: return "YAML"; + case Channel::Core: return "CORE"; + case Channel::Test: return "TEST"; + case Channel::Config: return "CONF"; + default: return "NONE"; } } diff --git a/src/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj b/src/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj @@ -186,6 +186,11 @@ <EnablePREfast Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</EnablePREfast> <EnablePREfast Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">true</EnablePREfast> <EnablePREfast Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</EnablePREfast> + <RuntimeTypeInfo Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">false</RuntimeTypeInfo> + <RuntimeTypeInfo Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">false</RuntimeTypeInfo> + <RuntimeTypeInfo Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">false</RuntimeTypeInfo> + <RuntimeTypeInfo Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">false</RuntimeTypeInfo> + <RuntimeTypeInfo Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</RuntimeTypeInfo> </ClCompile> <Link> <GenerateWindowsMetadata>false</GenerateWindowsMetadata> @@ -201,6 +206,8 @@ <TreatWarningAsError Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</TreatWarningAsError> <SDLCheck Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</SDLCheck> <EnablePREfast Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</EnablePREfast> + <RuntimeTypeInfo Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</RuntimeTypeInfo> + <RuntimeTypeInfo Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</RuntimeTypeInfo> </ClCompile> <Link> <SubSystem Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Windows</SubSystem> @@ -228,6 +235,10 @@ <EnablePREfast Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">false</EnablePREfast> <EnablePREfast Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</EnablePREfast> <EnablePREfast Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</EnablePREfast> + <RuntimeTypeInfo Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">false</RuntimeTypeInfo> + <RuntimeTypeInfo Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">false</RuntimeTypeInfo> + <RuntimeTypeInfo Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</RuntimeTypeInfo> + <RuntimeTypeInfo Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</RuntimeTypeInfo> </ClCompile> <Link> <EnableCOMDATFolding>true</EnableCOMDATFolding> @@ -250,6 +261,7 @@ <LanguageStandard>stdcpp17</LanguageStandard> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <AdditionalOptions>%(AdditionalOptions) /fsanitize-coverage=inline-8bit-counters /fsanitize-coverage=edge /fsanitize-coverage=trace-cmp /fsanitize-coverage=trace-div</AdditionalOptions> + <RuntimeTypeInfo Condition="'$(Configuration)|$(Platform)'=='Fuzzing|x64'">false</RuntimeTypeInfo> </ClCompile> <Link> <EnableCOMDATFolding>true</EnableCOMDATFolding> @@ -281,6 +293,7 @@ <ClInclude Include="Public\AppInstallerSHA256.h" /> <ClInclude Include="Public\AppInstallerStrings.h" /> <ClInclude Include="Public\AppInstallerLogging.h" /> + <ClInclude Include="Public\AppInstallerVersions.h" /> <ClInclude Include="Public\winget\JsonSchemaValidation.h" /> <ClInclude Include="Public\winget\LocIndependent.h" /> <ClInclude Include="Public\winget\Resources.h" /> @@ -300,6 +313,7 @@ </ClCompile> <ClCompile Include="SHA256.cpp" /> <ClCompile Include="SharedThreadGlobals.cpp" /> + <ClCompile Include="Versions.cpp" /> <ClCompile Include="Yaml.cpp" /> <ClCompile Include="YamlWrapper.cpp" /> </ItemGroup> diff --git a/src/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj.filters b/src/AppInstallerSharedLib/AppInstallerSharedLib.vcxproj.filters @@ -56,6 +56,9 @@ <ClInclude Include="Public\AppInstallerLanguageUtilities.h"> <Filter>Public</Filter> </ClInclude> + <ClInclude Include="Public\AppInstallerVersions.h"> + <Filter>Public</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <ClCompile Include="pch.cpp"> @@ -91,6 +94,9 @@ <ClCompile Include="Resources.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="Versions.cpp"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <None Include="PropertySheet.props" /> diff --git a/src/AppInstallerSharedLib/Public/AppInstallerErrors.h b/src/AppInstallerSharedLib/Public/AppInstallerErrors.h @@ -152,6 +152,18 @@ #define WINGET_INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK ((HRESULT)0x0A150206) #define WINGET_INSTALLED_STATUS_FILE_ACCESS_ERROR ((HRESULT)0x8A150207) +// Configuration Errors +#define WINGET_CONFIG_ERROR_INVALID_CONFIGURATION_FILE ((HRESULT)0x8A15C001) +#define WINGET_CONFIG_ERROR_INVALID_YAML ((HRESULT)0x8A15C002) +#define WINGET_CONFIG_ERROR_INVALID_FIELD ((HRESULT)0x8A15C003) +#define WINGET_CONFIG_ERROR_UNKNOWN_CONFIGURATION_FILE_VERSION ((HRESULT)0x8A15C004) +#define WINGET_CONFIG_ERROR_SET_APPLY_FAILED ((HRESULT)0x8A15C005) +#define WINGET_CONFIG_ERROR_DUPLICATE_IDENTIFIER ((HRESULT)0x8A15C006) +#define WINGET_CONFIG_ERROR_MISSING_DEPENDENCY ((HRESULT)0x8A15C007) +#define WINGET_CONFIG_ERROR_DEPENDENCY_UNSATISFIED ((HRESULT)0x8A15C008) +#define WINGET_CONFIG_ERROR_ASSERTION_FAILED ((HRESULT)0x8A15C009) +#define WINGET_CONFIG_ERROR_MANUALLY_SKIPPED ((HRESULT)0x8A15C00A) + namespace AppInstaller { diff --git a/src/AppInstallerSharedLib/Public/AppInstallerLogging.h b/src/AppInstallerSharedLib/Public/AppInstallerLogging.h @@ -53,6 +53,7 @@ namespace AppInstaller::Logging YAML, Core, Test, + Config, All, }; diff --git a/src/AppInstallerSharedLib/Public/AppInstallerSHA256.h b/src/AppInstallerSharedLib/Public/AppInstallerSHA256.h @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once - +#include <filesystem> #include <memory> #include <string> #include <vector> diff --git a/src/AppInstallerSharedLib/Public/AppInstallerVersions.h b/src/AppInstallerSharedLib/Public/AppInstallerVersions.h @@ -0,0 +1,269 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include <string> +#include <string_view> +#include <vector> + +namespace AppInstaller::Utility +{ + using namespace std::string_view_literals; + + // Creates a comparable version object from a string. + // Versions are parsed by: + // 1. Parse approximate comparator sign if applicable + // 2. Splitting the string based on the given splitChars (or DefaultSplitChars) + // 3. Parsing a leading, positive integer from each split part + // 4. Saving any remaining, non-digits as a supplemental value + // + // Versions are compared by: + // for each part in each version + // if both sides have no more parts, return equal + // else if one side has no more parts, it is less + // else if integers not equal, return comparison of integers + // else if only one side has a non-empty string part, it is less + // else if string parts not equal, return comparison of strings + // if all parts are same, use approximate comparator if applicable + // + // Note: approximate to another approximate version is invalid. + // approximate to Unknown is invalid. + struct Version + { + // Used in approximate version to indicate the relation to the base version. + enum class ApproximateComparator + { + None, + LessThan, + GreaterThan, + }; + + // The default characters to split a version string on. + constexpr static std::string_view DefaultSplitChars = "."sv; + + Version() = default; + + Version(const std::string& version, std::string_view splitChars = DefaultSplitChars) : + Version(std::string(version), splitChars) {} + Version(std::string&& version, std::string_view splitChars = DefaultSplitChars); + + // Constructing an approximate version from a base version. + Version(Version baseVersion, ApproximateComparator approximateComparator); + + // Resets the version's value to the input. + virtual void Assign(std::string version, std::string_view splitChars = DefaultSplitChars); + + // Gets the full version string used to construct the Version. + const std::string& ToString() const { return m_version; } + + bool operator<(const Version& other) const; + bool operator>(const Version& other) const; + bool operator<=(const Version& other) const; + bool operator>=(const Version& other) const; + bool operator==(const Version& other) const; + bool operator!=(const Version& other) const; + + // Determines if this version is the sentinel value defining the 'Latest' version + bool IsLatest() const; + + // Returns a Version that will return true for IsLatest + static Version CreateLatest(); + + // Determines if this version is the sentinel value defining an 'Unknown' version + bool IsUnknown() const; + + // Returns a Version that will return true for IsUnknown + static Version CreateUnknown(); + + // Gets a bool indicating whether the full version string is empty. + // Does not indicate that Parts is empty; for instance when "0.0" is given, + // this will be false while GetParts().empty() would be true. + bool IsEmpty() const { return m_version.empty(); } + + // An individual version part in between split characters. + struct Part + { + Part() = default; + Part(uint64_t integer) : Integer(integer) {} + Part(const std::string& part); + Part(uint64_t integer, std::string other); + + bool operator<(const Part& other) const; + bool operator==(const Part& other) const; + bool operator!=(const Part& other) const; + + uint64_t Integer = 0; + std::string Other; + }; + + // Gets the part breakdown for a given version. + const std::vector<Part>& GetParts() const { return m_parts; } + + // Gets the part at the given index; or the implied zero part if past the end. + const Part& PartAt(size_t index) const; + + // Returns if the version is an approximate version. + bool IsApproximate() const { return m_approximateComparator != ApproximateComparator::None; } + + // Get the base version from approximate version, or return a copy if the version is not approximate. + Version GetBaseVersion() const; + + protected: + + bool IsBaseVersionLatest() const; + bool IsBaseVersionUnknown() const; + // Called by overloaded less than operator implementation when base version already compared and equal, less than determined by approximate comparator. + bool ApproximateCompareLessThan(const Version& other) const; + + std::string m_version; + std::vector<Part> m_parts; + ApproximateComparator m_approximateComparator = ApproximateComparator::None; + + // Remove trailing empty parts (0 or empty) + void Trim(); + }; + + // Four parts version number: 16-bits.16-bits.16-bits.16-bits + struct UInt64Version : public Version + { + UInt64Version() = default; + UInt64Version(UINT64 version); + UInt64Version(std::string&& version, std::string_view splitChars = DefaultSplitChars); + UInt64Version(const std::string& version, std::string_view splitChars = DefaultSplitChars) : + UInt64Version(std::string(version), splitChars) {} + + void Assign(std::string version, std::string_view splitChars = DefaultSplitChars) override; + void Assign(UINT64 version); + + UINT64 Major() const { return m_parts.size() > 0 ? m_parts[0].Integer : 0; } + UINT64 Minor() const { return m_parts.size() > 1 ? m_parts[1].Integer : 0; } + UINT64 Build() const { return m_parts.size() > 2 ? m_parts[2].Integer : 0; } + UINT64 Revision() const { return m_parts.size() > 3 ? m_parts[3].Integer : 0; } + }; + + // A semantic version as defined by semver.org + // This is currently fairly loose in its restrictions on a version, and is largely to separate out the prelease and build metadata portions. + struct SemanticVersion : public Version + { + SemanticVersion() = default; + SemanticVersion(std::string&& version); + SemanticVersion(const std::string& version) : + SemanticVersion(std::string(version)) {} + + void Assign(std::string version, std::string_view splitChars = DefaultSplitChars) override; + + // Indicates that the version is pre-release + bool IsPrerelease() const; + // The pre-release version + const Version& PrereleaseVersion() const; + + // Indicates that the version has build metadata + bool HasBuildMetadata() const; + // The build metadata version + const Version& BuildMetadata() const; + + private: + Version m_prerelease; + Version m_buildMetadata; + }; + + // Version range represented by a min version and max version, both inclusive. + struct VersionRange + { + VersionRange() { m_isEmpty = true; }; + VersionRange(Version minVersion, Version maxVersion); + + bool IsEmpty() const { return m_isEmpty; } + + // Checks if version ranges overlap. Empty version range does not overlap with any version range. + bool Overlaps(const VersionRange& other) const; + + // Checks if the version range is effectively the same as a single version. + bool IsSameAsSingleVersion(const Version& version) const; + + // Checks if a version is within the version range + bool ContainsVersion(const Version& version) const; + + // < operator will thow if compared with an empty range or an overlapped range + bool operator<(const VersionRange& other) const; + + const Version& GetMinVersion() const; + const Version& GetMaxVersion() const; + + private: + Version m_minVersion; + Version m_maxVersion; + bool m_isEmpty = false; + }; + + // A range of versions indicated by a version and optionally a wildcard at the end. + struct GatedVersion + { + GatedVersion() {} + GatedVersion(Version&& version) : m_version(std::move(version)) {} + GatedVersion(const Version& version) : m_version(version) {} + GatedVersion(std::string_view versionString) : m_version(std::string{ versionString }) {} + GatedVersion(const std::string& versionString) : m_version(versionString) {} + + // Determines whether a given version falls within this Gated version. + // I.e., whether it matches up to the wildcard + bool IsValidVersion(Version version) const; + + bool operator==(const GatedVersion& other) const { return m_version == other.m_version; } + const std::string& ToString() const { return m_version.ToString(); } + + private: + // Hold the version string as a Version object that makes it easy to access each of + // the version's parts. The real magic is in IsValidVersion() + Version m_version; + }; + + // A channel string; existing solely to give a type. + // + // Compared lexicographically. + struct Channel + { + Channel() = default; + Channel(const std::string& channel) : m_channel(channel) {} + Channel(std::string&& channel) : m_channel(std::move(channel)) {} + + const std::string& ToString() const { return m_channel; } + + bool operator<(const Channel& other) const; + + private: + std::string m_channel; + }; + + // Contains a version and channel. + // These are compared by: + // if channel not equal, return compare channel + // else return !compare version + // + // The implication of this is that the default less sort will be: + // 2.0, "" + // 1.0, "" + // 3.0, "alpha" + // 2.0, "alpha" + struct VersionAndChannel + { + VersionAndChannel() = default; + VersionAndChannel(Version&& version, Channel&& channel); + + const Version& GetVersion() const { return m_version; } + const Channel& GetChannel() const { return m_channel; } + + std::string ToString() const; + + bool operator<(const VersionAndChannel& other) const; + + // A convenience function to make more semantic sense at call sites over the somewhat awkward less than ordering. + bool IsUpdatedBy(const VersionAndChannel& other) const; + + private: + Version m_version; + Channel m_channel; + }; + + // Checks if there are overlaps within the list of version ranges + bool HasOverlapInVersionRanges(const std::vector<VersionRange>& ranges); +} diff --git a/src/AppInstallerSharedLib/Public/winget/SharedThreadGlobals.h b/src/AppInstallerSharedLib/Public/winget/SharedThreadGlobals.h @@ -30,7 +30,7 @@ namespace AppInstaller::ThreadLocalStorage static ThreadGlobals* GetForCurrentThread(); }; - // RAII object used to + // RAII object used to enable reverting back to the previous thread globals object. struct PreviousThreadGlobals { ~PreviousThreadGlobals(); diff --git a/src/AppInstallerSharedLib/Public/winget/Yaml.h b/src/AppInstallerSharedLib/Public/winget/Yaml.h @@ -99,6 +99,7 @@ namespace AppInstaller::YAML bool IsScalar() const { return m_type == Type::Scalar; } bool IsSequence() const { return m_type == Type::Sequence; } bool IsMap() const { return m_type == Type::Mapping; } + Type GetType() const { return m_type; } explicit operator bool() const { return IsDefined(); } @@ -141,6 +142,7 @@ namespace AppInstaller::YAML // The workers for the as function. std::string as_dispatch(std::string*) const; + std::wstring as_dispatch(std::wstring*) const; int64_t as_dispatch(int64_t*) const; int as_dispatch(int*) const; bool as_dispatch(bool*) const; diff --git a/src/AppInstallerSharedLib/Versions.cpp b/src/AppInstallerSharedLib/Versions.cpp @@ -0,0 +1,632 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "Public/AppInstallerVersions.h" +#include "Public/AppInstallerStrings.h" + +namespace AppInstaller::Utility +{ + using namespace std::string_view_literals; + + static constexpr std::string_view s_Version_Part_Latest = "Latest"sv; + static constexpr std::string_view s_Version_Part_Unknown = "Unknown"sv; + + static constexpr std::string_view s_Approximate_Less_Than = "< "sv; + static constexpr std::string_view s_Approximate_Greater_Than = "> "sv; + + Version::Version(std::string&& version, std::string_view splitChars) + { + Assign(std::move(version), splitChars); + } + + Version::Version(Version baseVersion, ApproximateComparator approximateComparator) : Version(std::move(baseVersion)) + { + if (approximateComparator == ApproximateComparator::None) + { + return; + } + + THROW_HR_IF(E_INVALIDARG, this->IsApproximate() || this->IsUnknown()); + + m_approximateComparator = approximateComparator; + if (approximateComparator == ApproximateComparator::LessThan) + { + m_version = std::string{ s_Approximate_Less_Than } + m_version; + } + else if (approximateComparator == ApproximateComparator::GreaterThan) + { + m_version = std::string{ s_Approximate_Greater_Than } + m_version; + } + } + + void Version::Assign(std::string version, std::string_view splitChars) + { + m_version = std::move(version); + + // Process approximate comparator if applicable + std::string baseVersion = m_version; + if (CaseInsensitiveStartsWith(m_version, s_Approximate_Less_Than)) + { + m_approximateComparator = ApproximateComparator::LessThan; + baseVersion = m_version.substr(s_Approximate_Less_Than.length(), m_version.length() - s_Approximate_Less_Than.length()); + } + else if (CaseInsensitiveStartsWith(m_version, s_Approximate_Greater_Than)) + { + m_approximateComparator = ApproximateComparator::GreaterThan; + baseVersion = m_version.substr(s_Approximate_Greater_Than.length(), m_version.length() - s_Approximate_Greater_Than.length()); + } + + // Then parse the base version + size_t pos = 0; + + while (pos < baseVersion.length()) + { + size_t newPos = baseVersion.find_first_of(splitChars, pos); + + size_t length = (newPos == std::string::npos ? baseVersion.length() : newPos) - pos; + m_parts.emplace_back(baseVersion.substr(pos, length)); + + pos += length + 1; + } + + // Trim version parts + Trim(); + + THROW_HR_IF(E_INVALIDARG, m_approximateComparator != ApproximateComparator::None && IsBaseVersionUnknown()); + } + + void Version::Trim() + { + while (!m_parts.empty()) + { + const Part& part = m_parts.back(); + if (part.Integer == 0 && part.Other.empty()) + { + m_parts.pop_back(); + } + else + { + return; + } + } + } + + bool Version::operator<(const Version& other) const + { + // Sort Latest higher than any other values + bool thisIsLatest = IsBaseVersionLatest(); + bool otherIsLatest = other.IsBaseVersionLatest(); + + if (thisIsLatest && otherIsLatest) + { + return ApproximateCompareLessThan(other); + } + else if (thisIsLatest || otherIsLatest) + { + // If only one is latest, this can only be less than if the other is and this is not. + return (otherIsLatest && !thisIsLatest); + } + + // Sort Unknown lower than any known values + bool thisIsUnknown = IsBaseVersionUnknown(); + bool otherIsUnknown = other.IsBaseVersionUnknown(); + + if (thisIsUnknown && otherIsUnknown) + { + // This code path should always return false as we disable approximate version for Unknown for now + return ApproximateCompareLessThan(other); + } + else if (thisIsUnknown || otherIsUnknown) + { + // If at least one is unknown, this can only be less than if it is and the other is not. + return (thisIsUnknown && !otherIsUnknown); + } + + for (size_t i = 0; i < m_parts.size(); ++i) + { + if (i >= other.m_parts.size()) + { + // All parts equal to this point + break; + } + + const Part& partA = m_parts[i]; + const Part& partB = other.m_parts[i]; + + if (partA < partB) + { + return true; + } + else if (partB < partA) + { + return false; + } + // else parts are equal, so continue to next part + } + + // All parts tested were equal + if (m_parts.size() == other.m_parts.size()) + { + return ApproximateCompareLessThan(other); + } + else + { + // Else this is only less if there are more parts in other. + return m_parts.size() < other.m_parts.size(); + } + + } + + bool Version::operator>(const Version& other) const + { + return other < *this; + } + + bool Version::operator<=(const Version& other) const + { + return !(*this > other); + } + + bool Version::operator>=(const Version& other) const + { + return !(*this < other); + } + + bool Version::operator==(const Version& other) const + { + if (m_approximateComparator != other.m_approximateComparator) + { + return false; + } + + if ((IsBaseVersionLatest() && other.IsBaseVersionLatest()) || + (IsBaseVersionUnknown() && other.IsBaseVersionUnknown())) + { + return true; + } + + if (m_parts.size() != other.m_parts.size()) + { + return false; + } + + for (size_t i = 0; i < m_parts.size(); ++i) + { + if (m_parts[i] != other.m_parts[i]) + { + return false; + } + } + + return true; + } + + bool Version::operator!=(const Version& other) const + { + return !(*this == other); + } + + bool Version::IsLatest() const + { + return (m_approximateComparator != ApproximateComparator::LessThan && IsBaseVersionLatest()); + } + + Version Version::CreateLatest() + { + Version result; + result.m_version = s_Version_Part_Latest; + result.m_parts.emplace_back(0, std::string{ s_Version_Part_Latest }); + return result; + } + + bool Version::IsUnknown() const + { + return IsBaseVersionUnknown(); + } + + Version Version::CreateUnknown() + { + Version result; + result.m_version = s_Version_Part_Unknown; + result.m_parts.emplace_back(0, std::string{ s_Version_Part_Unknown }); + return result; + } + + const Version::Part& Version::PartAt(size_t index) const + { + static Part s_zero{}; + + if (index < m_parts.size()) + { + return m_parts[index]; + } + else + { + return s_zero; + } + } + + Version Version::GetBaseVersion() const + { + Version baseVersion = *this; + baseVersion.m_approximateComparator = ApproximateComparator::None; + if (m_approximateComparator == ApproximateComparator::LessThan) + { + baseVersion.m_version = m_version.substr(s_Approximate_Less_Than.size()); + } + else if (m_approximateComparator == ApproximateComparator::GreaterThan) + { + baseVersion.m_version = m_version.substr(s_Approximate_Greater_Than.size()); + } + + return baseVersion; + } + + bool Version::IsBaseVersionLatest() const + { + return (m_parts.size() == 1 && m_parts[0].Integer == 0 && Utility::CaseInsensitiveEquals(m_parts[0].Other, s_Version_Part_Latest)); + } + + bool Version::IsBaseVersionUnknown() const + { + return (m_parts.size() == 1 && m_parts[0].Integer == 0 && Utility::CaseInsensitiveEquals(m_parts[0].Other, s_Version_Part_Unknown)); + } + + bool Version::ApproximateCompareLessThan(const Version& other) const + { + // Only true if this is less than, other is not, OR this is none, other is greater than + return (m_approximateComparator == ApproximateComparator::LessThan && other.m_approximateComparator != ApproximateComparator::LessThan) || + (m_approximateComparator == ApproximateComparator::None && other.m_approximateComparator == ApproximateComparator::GreaterThan); + } + + Version::Part::Part(const std::string& part) + { + const char* begin = part.c_str(); + char* end = nullptr; + errno = 0; + Integer = strtoull(begin, &end, 10); + + if (errno == ERANGE) + { + Integer = 0; + Other = part; + } + else if (static_cast<size_t>(end - begin) != part.length()) + { + Other = end; + } + } + + Version::Part::Part(uint64_t integer, std::string other) : + Integer(integer), Other(std::move(other)) {} + + bool Version::Part::operator<(const Part& other) const + { + if (Integer < other.Integer) + { + return true; + } + else if (Integer > other.Integer) + { + return false; + } + else if (Other.empty()) + { + // If this Other is empty, it is at least >= + return false; + } + else if (!Other.empty() && other.Other.empty()) + { + // If the other Other is empty and this is not, this is less. + return true; + } + else if (Other < other.Other) + { + return true; + } + + // else Other >= other.Other + return false; + } + + bool Version::Part::operator==(const Part& other) const + { + return Integer == other.Integer && Other == other.Other; + } + + bool Version::Part::operator!=(const Part& other) const + { + return !(*this == other); + } + + bool Channel::operator<(const Channel& other) const + { + return m_channel < other.m_channel; + } + + VersionAndChannel::VersionAndChannel(Version&& version, Channel&& channel) : + m_version(std::move(version)), m_channel(std::move(channel)) {} + + std::string VersionAndChannel::ToString() const + { + std::string result; + result = m_version.ToString(); + if (!m_channel.ToString().empty()) + { + result += '['; + result += m_channel.ToString(); + result += ']'; + } + return result; + } + + bool VersionAndChannel::operator<(const VersionAndChannel& other) const + { + if (m_channel < other.m_channel) + { + return true; + } + else if (other.m_channel < m_channel) + { + return false; + } + // We intentionally invert the order for version here. + else if (other.m_version < m_version) + { + return true; + } + + // else m_version >= other.m_version + return false; + } + + bool VersionAndChannel::IsUpdatedBy(const VersionAndChannel& other) const + { + // Channel crossing should not happen here. + if (!Utility::ICUCaseInsensitiveEquals(m_channel.ToString(), other.m_channel.ToString())) + { + return false; + } + + return m_version < other.m_version; + } + + UInt64Version::UInt64Version(UINT64 version) + { + Assign(version); + } + + void UInt64Version::Assign(UINT64 version) + { + const UINT64 mask16 = (1 << 16) - 1; + UINT64 revision = version & mask16; + UINT64 build = (version >> 0x10) & mask16; + UINT64 minor = (version >> 0x20) & mask16; + UINT64 major = (version >> 0x30) & mask16; + + // Construct a string representation of the provided version + std::stringstream ssVersion; + ssVersion << major + << Version::DefaultSplitChars << minor + << Version::DefaultSplitChars << build + << Version::DefaultSplitChars << revision; + m_version = ssVersion.str(); + + // Construct the 4 parts + m_parts = { major, minor, build, revision }; + + // Trim version parts + Trim(); + } + + UInt64Version::UInt64Version(std::string&& version, std::string_view splitChars) + { + Assign(std::move(version), splitChars); + } + + void UInt64Version::Assign(std::string version, std::string_view splitChars) + { + Version::Assign(std::move(version), splitChars); + + // After trimming trailing parts (0 or empty), + // at most 4 parts must be present + THROW_HR_IF(E_INVALIDARG, m_parts.size() > 4); + for (const auto& part : m_parts) + { + // Check for non-empty Other part + THROW_HR_IF(E_INVALIDARG, !part.Other.empty()); + + // Check for overflow Integer part + THROW_HR_IF(E_INVALIDARG, part.Integer >> 16 != 0); + } + } + + SemanticVersion::SemanticVersion(std::string&& version) + { + Assign(std::move(version), DefaultSplitChars); + } + + void SemanticVersion::Assign(std::string version, std::string_view splitChars) + { + // Semantic versions require using the default split character + THROW_HR_IF(E_INVALIDARG, splitChars != DefaultSplitChars); + + // First split off any trailing build metadata + std::string interimVersion = version; + size_t buildMetadataPos = interimVersion.find('+', 0); + + if (buildMetadataPos != std::string::npos) + { + m_buildMetadata.Assign(interimVersion.substr(buildMetadataPos + 1)); + interimVersion.resize(buildMetadataPos); + } + + // Now split off the prerelease data + size_t prereleasePos = interimVersion.find('-', 0); + + if (prereleasePos != std::string::npos) + { + m_prerelease.Assign(interimVersion.substr(prereleasePos + 1)); + interimVersion.resize(prereleasePos); + } + + // Parse main version + Version::Assign(std::move(interimVersion), splitChars); + THROW_HR_IF(E_INVALIDARG, IsApproximate()); + THROW_HR_IF(E_INVALIDARG, m_parts.size() > 3); + for (size_t i = 0; i < 3; ++i) + { + THROW_HR_IF(E_INVALIDARG, !PartAt(i).Other.empty()); + } + + // Put rest of version back onto Other of last part + size_t otherSplit = (prereleasePos != std::string::npos ? prereleasePos : buildMetadataPos); + if (otherSplit != std::string::npos) + { + while (m_parts.size() < 3) + { + m_parts.emplace_back(); + } + m_parts[2].Other = version.substr(otherSplit); + } + + // Overwrite the whole version string with our whole version string + m_version = std::move(version); + } + + bool SemanticVersion::IsPrerelease() const + { + return !m_prerelease.IsEmpty(); + } + + const Version& SemanticVersion::PrereleaseVersion() const + { + return m_prerelease; + } + + bool SemanticVersion::HasBuildMetadata() const + { + return !m_buildMetadata.IsEmpty(); + } + + const Version& SemanticVersion::BuildMetadata() const + { + return m_buildMetadata; + } + + VersionRange::VersionRange(Version minVersion, Version maxVersion) + { + THROW_HR_IF(E_INVALIDARG, minVersion > maxVersion); + m_minVersion = std::move(minVersion); + m_maxVersion = std::move(maxVersion); + } + + bool VersionRange::Overlaps(const VersionRange& other) const + { + // No overlap if either is an empty range. + if (IsEmpty() || other.IsEmpty()) + { + return false; + } + + return m_minVersion <= other.m_maxVersion && m_maxVersion >= other.m_minVersion; + } + + bool VersionRange::IsSameAsSingleVersion(const Version& version) const + { + if (IsEmpty()) + { + return false; + } + + return m_minVersion == version && m_maxVersion == version; + } + + bool VersionRange::ContainsVersion(const Version& version) const + { + if (IsEmpty()) + { + return false; + } + + return version >= m_minVersion && version <= m_maxVersion; + } + + bool VersionRange::operator<(const VersionRange& other) const + { + THROW_HR_IF(E_INVALIDARG, IsEmpty() || other.IsEmpty() || Overlaps(other)); + + return m_minVersion < other.m_minVersion; + } + + const Version& VersionRange::GetMinVersion() const + { + THROW_HR_IF(E_NOT_VALID_STATE, IsEmpty()); + return m_minVersion; + } + + const Version& VersionRange::GetMaxVersion() const + { + THROW_HR_IF(E_NOT_VALID_STATE, IsEmpty()); + return m_maxVersion; + } + + bool GatedVersion::IsValidVersion(Version version) const + { + auto gateParts = m_version.GetParts(); + if (gateParts.empty()) + { + return false; + } + + if (gateParts.back() != Version::Part("*")) + { + // Without wildcards, revert to direct comparison + return m_version == version; + } + + auto versionParts = version.GetParts(); + for (size_t i = 0; i < gateParts.size() - 1; ++i) + { + if (versionParts.size() > i) + { + if (gateParts[i] == versionParts[i]) + { + continue; + } + else + { + // Mismatch with the gated version + return false; + } + } + else + { + // Assume trailing 0s on the version + if (gateParts[i] != Version::Part(0)) + { + return false; + } + } + } + + // All version parts matched + return true; + } + + bool HasOverlapInVersionRanges(const std::vector<VersionRange>& ranges) + { + for (size_t i = 0; i < ranges.size(); i++) + { + for (size_t j = i + 1; j < ranges.size(); j++) + { + if (ranges[i].Overlaps(ranges[j])) + { + return true; + } + } + } + + return false; + } +} diff --git a/src/AppInstallerSharedLib/Yaml.cpp b/src/AppInstallerSharedLib/Yaml.cpp @@ -238,6 +238,11 @@ namespace AppInstaller::YAML return m_scalar; } + std::wstring Node::as_dispatch(std::wstring*) const + { + return Utility::ConvertToUTF16(m_scalar); + } + int64_t Node::as_dispatch(int64_t*) const { return std::stoll(m_scalar); diff --git a/src/JsonCppLib/JsonCppLib.vcxproj b/src/JsonCppLib/JsonCppLib.vcxproj @@ -84,13 +84,13 @@ <UseDebugLibraries>false</UseDebugLibraries> <PlatformToolset>v142</PlatformToolset> <PlatformToolset Condition="'$(VisualStudioVersion)' == '17.0'">v143</PlatformToolset> - <SpectreMitigation>Spectre</SpectreMitigation> + <SpectreMitigation>Spectre</SpectreMitigation> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Fuzzing|x64'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseDebugLibraries>false</UseDebugLibraries> - <PlatformToolset>v142</PlatformToolset> - <PlatformToolset Condition="'$(VisualStudioVersion)' == '17.0'">v143</PlatformToolset> + <PlatformToolset>v142</PlatformToolset> + <PlatformToolset Condition="'$(VisualStudioVersion)' == '17.0'">v143</PlatformToolset> <EnableASAN>true</EnableASAN> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration"> @@ -104,7 +104,7 @@ <UseDebugLibraries>false</UseDebugLibraries> <PlatformToolset>v142</PlatformToolset> <PlatformToolset Condition="'$(VisualStudioVersion)' == '17.0'">v143</PlatformToolset> - <SpectreMitigation>Spectre</SpectreMitigation> + <SpectreMitigation>Spectre</SpectreMitigation> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> @@ -177,6 +177,8 @@ <Optimization>Disabled</Optimization> <LanguageStandard>stdcpp17</LanguageStandard> <AdditionalIncludeDirectories>json</AdditionalIncludeDirectories> + <RuntimeTypeInfo>false</RuntimeTypeInfo> + <RuntimeTypeInfo>false</RuntimeTypeInfo> </ClCompile> <Link> <TargetMachine>MachineX86</TargetMachine> @@ -192,6 +194,7 @@ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <LanguageStandard>stdcpp17</LanguageStandard> <AdditionalIncludeDirectories>json</AdditionalIncludeDirectories> + <RuntimeTypeInfo>false</RuntimeTypeInfo> </ClCompile> <Link> <TargetMachine>MachineX86</TargetMachine> @@ -206,6 +209,7 @@ <LanguageStandard>stdcpp17</LanguageStandard> <AdditionalIncludeDirectories>json</AdditionalIncludeDirectories> <WarningLevel>Level3</WarningLevel> + <RuntimeTypeInfo>false</RuntimeTypeInfo> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Fuzzing|x64'"> @@ -215,6 +219,7 @@ <WarningLevel>Level3</WarningLevel> <AdditionalOptions>/fsanitize-coverage=inline-8bit-counters /fsanitize-coverage=edge /fsanitize-coverage=trace-cmp /fsanitize-coverage=trace-div %(AdditionalOptions)</AdditionalOptions> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <RuntimeTypeInfo>false</RuntimeTypeInfo> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'"> @@ -222,6 +227,7 @@ <LanguageStandard>stdcpp17</LanguageStandard> <AdditionalIncludeDirectories>json</AdditionalIncludeDirectories> <WarningLevel>Level3</WarningLevel> + <RuntimeTypeInfo>false</RuntimeTypeInfo> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'"> @@ -229,6 +235,7 @@ <LanguageStandard>stdcpp17</LanguageStandard> <AdditionalIncludeDirectories>json</AdditionalIncludeDirectories> <WarningLevel>Level3</WarningLevel> + <RuntimeTypeInfo>false</RuntimeTypeInfo> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'"> @@ -236,6 +243,8 @@ <LanguageStandard>stdcpp17</LanguageStandard> <AdditionalIncludeDirectories>json</AdditionalIncludeDirectories> <WarningLevel>Level3</WarningLevel> + <RuntimeTypeInfo>false</RuntimeTypeInfo> + <RuntimeTypeInfo>false</RuntimeTypeInfo> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> @@ -243,6 +252,8 @@ <LanguageStandard>stdcpp17</LanguageStandard> <AdditionalIncludeDirectories>json</AdditionalIncludeDirectories> <WarningLevel>Level3</WarningLevel> + <RuntimeTypeInfo>false</RuntimeTypeInfo> + <RuntimeTypeInfo>false</RuntimeTypeInfo> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> @@ -250,6 +261,7 @@ <LanguageStandard>stdcpp17</LanguageStandard> <AdditionalIncludeDirectories>json</AdditionalIncludeDirectories> <WarningLevel>Level3</WarningLevel> + <RuntimeTypeInfo>false</RuntimeTypeInfo> </ClCompile> </ItemDefinitionGroup> <ItemGroup> diff --git a/src/Microsoft.Management.Configuration.Projection/Microsoft.Management.Configuration.Projection.csproj b/src/Microsoft.Management.Configuration.Projection/Microsoft.Management.Configuration.Projection.csproj @@ -0,0 +1,36 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net6.0-windows10.0.22000.0</TargetFramework> + <Platforms>x64;x86;arm64</Platforms> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + <OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\$(MSBuildProjectName)\</OutputPath> + </PropertyGroup> + + <PropertyGroup> + <CsWinRTIncludes>Microsoft.Management.Configuration</CsWinRTIncludes> + <CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir> + <CsWinRTWindowsMetadata>10.0.22000.0</CsWinRTWindowsMetadata> + <!-- Ensure Support for Windows 10, Version 1809 --> + <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion> + </PropertyGroup> + + <PropertyGroup> + <!-- Workaround for MSB3271 error on processor architecture mismatch --> + <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.Windows.CsWinRT" Version="2.0.1" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\Microsoft.Management.Configuration\Microsoft.Management.Configuration.vcxproj"> + <OutputItemType>Content</OutputItemType> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + <ReferenceOutputAssembly>True</ReferenceOutputAssembly> + </ProjectReference> + </ItemGroup> + +</Project> diff --git a/src/Microsoft.Management.Configuration.UnitTests/Fixtures/UnitTestCollection.cs b/src/Microsoft.Management.Configuration.UnitTests/Fixtures/UnitTestCollection.cs @@ -0,0 +1,21 @@ +// ----------------------------------------------------------------------------- +// <copyright file="UnitTestCollection.cs" company="Microsoft Corporation"> +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// </copyright> +// ----------------------------------------------------------------------------- + +namespace Microsoft.Management.Configuration.UnitTests.Fixtures +{ + using Xunit; + + /// <summary> + /// Unit test collection. + /// </summary> + [CollectionDefinition("UnitTestCollection")] + public class UnitTestCollection : ICollectionFixture<UnitTestFixture> + { + // This class has no code, and is never created. Its purpose is simply + // to be the place to apply [CollectionDefinition] and all the + // ICollectionFixture<> interfaces. + } +} diff --git a/src/Microsoft.Management.Configuration.UnitTests/Fixtures/UnitTestFixture.cs b/src/Microsoft.Management.Configuration.UnitTests/Fixtures/UnitTestFixture.cs @@ -0,0 +1,30 @@ +// ----------------------------------------------------------------------------- +// <copyright file="UnitTestFixture.cs" company="Microsoft Corporation"> +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// </copyright> +// ----------------------------------------------------------------------------- + +namespace Microsoft.Management.Configuration.UnitTests.Fixtures +{ + using Xunit.Abstractions; + + /// <summary> + /// Unit test fixture. + /// </summary> + public class UnitTestFixture + { + /// <summary> + /// Initializes a new instance of the <see cref="UnitTestFixture"/> class. + /// </summary> + /// <param name="messageSink">The message sink for the fixture.</param> + public UnitTestFixture(IMessageSink messageSink) + { + this.MessageSink = messageSink; + } + + /// <summary> + /// Gets the message sink for the fixture. + /// </summary> + public IMessageSink MessageSink { get; private init; } + } +} diff --git a/src/Microsoft.Management.Configuration.UnitTests/Helpers/ConfigurationProcessorTestBase.cs b/src/Microsoft.Management.Configuration.UnitTests/Helpers/ConfigurationProcessorTestBase.cs @@ -0,0 +1,76 @@ +// ----------------------------------------------------------------------------- +// <copyright file="ConfigurationProcessorTestBase.cs" company="Microsoft Corporation"> +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// </copyright> +// ----------------------------------------------------------------------------- + +namespace Microsoft.Management.Configuration.UnitTests.Helpers +{ + using System; + using Microsoft.Management.Configuration.UnitTests.Fixtures; + using Windows.Storage.Streams; + using Xunit.Abstractions; + + /// <summary> + /// Test base that provides helpers for dealing with <see cref="ConfigurationProcessor"/>. + /// </summary> + public class ConfigurationProcessorTestBase + { + private readonly DiagnosticsEventSink diagnosticsEventSink; + + /// <summary> + /// Initializes a new instance of the <see cref="ConfigurationProcessorTestBase"/> class. + /// </summary> + /// <param name="fixture">Unit test fixture.</param> + /// <param name="log">Log helper.</param> + protected ConfigurationProcessorTestBase(UnitTestFixture fixture, ITestOutputHelper log) + { + this.Fixture = fixture; + this.Log = log; + this.diagnosticsEventSink = new DiagnosticsEventSink(fixture, log); + } + + /// <summary> + /// Gets the test fixture. + /// </summary> + protected UnitTestFixture Fixture { get; private init; } + + /// <summary> + /// Gets the output helper. + /// </summary> + protected ITestOutputHelper Log { get; private init; } + + /// <summary> + /// Create a new <see cref="ConfigurationProcessor"/> with the diagnostics event hooked up. + /// </summary> + /// <param name="factory">The factory to use.</param> + /// <returns>The new <see cref="ConfigurationProcessor"/> object.</returns> + protected ConfigurationProcessor CreateConfigurationProcessorWithDiagnostics(IConfigurationProcessorFactory? factory = null) + { + ConfigurationProcessor result = new ConfigurationProcessor(factory); + result.Diagnostics += this.diagnosticsEventSink.DiagnosticsHandler; + return result; + } + + /// <summary> + /// Creates an input stream from the given string. + /// </summary> + /// <param name="contents">The contents that the stream should contain.</param> + /// <returns>The created stream.</returns> + protected IInputStream CreateStream(string contents) + { + InMemoryRandomAccessStream result = new InMemoryRandomAccessStream(); + + using (DataWriter writer = new DataWriter(result)) + { + writer.UnicodeEncoding = UnicodeEncoding.Utf8; + writer.WriteString(contents); + writer.StoreAsync().AsTask().Wait(); + writer.DetachStream(); + } + + result.Seek(0); + return result; + } + } +} diff --git a/src/Microsoft.Management.Configuration.UnitTests/Helpers/DiagnosticsEventSink.cs b/src/Microsoft.Management.Configuration.UnitTests/Helpers/DiagnosticsEventSink.cs @@ -0,0 +1,49 @@ +// ----------------------------------------------------------------------------- +// <copyright file="DiagnosticsEventSink.cs" company="Microsoft Corporation"> +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// </copyright> +// ----------------------------------------------------------------------------- + +namespace Microsoft.Management.Configuration.UnitTests.Helpers +{ + using Microsoft.Management.Configuration.UnitTests.Fixtures; + using Xunit.Abstractions; + using Xunit.Sdk; + + /// <summary> + /// This class aids in getting diagnostics data from the <see cref="ConfigurationProcessor"/> out to the xUnit infrastructure. + /// </summary> + internal class DiagnosticsEventSink + { + private readonly UnitTestFixture fixture; + private readonly ITestOutputHelper log; + + /// <summary> + /// Initializes a new instance of the <see cref="DiagnosticsEventSink"/> class. + /// </summary> + /// <param name="fixture">Unit test fixture.</param> + /// <param name="log">Log helper.</param> + public DiagnosticsEventSink(UnitTestFixture fixture, ITestOutputHelper log) + { + this.fixture = fixture; + this.log = log; + } + + /// <summary> + /// Handles diagnostic information from a <see cref="ConfigurationProcessor"/>. + /// </summary> + /// <param name="sender">The object sending the information.</param> + /// <param name="e">The diagnostic information.</param> + public void DiagnosticsHandler(object? sender, DiagnosticInformation e) + { + if (e.Level == DiagnosticLevel.Verbose) + { + this.fixture.MessageSink.OnMessage(new DiagnosticMessage(e.Message)); + } + else + { + this.log.WriteLine(e.Message); + } + } + } +} diff --git a/src/Microsoft.Management.Configuration.UnitTests/Helpers/Errors.cs b/src/Microsoft.Management.Configuration.UnitTests/Helpers/Errors.cs @@ -0,0 +1,33 @@ +// ----------------------------------------------------------------------------- +// <copyright file="Errors.cs" company="Microsoft Corporation"> +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// </copyright> +// ----------------------------------------------------------------------------- + +namespace Microsoft.Management.Configuration.UnitTests.Helpers +{ + /// <summary> + /// Contains the error codes used by Microsoft.Management.Configuration + /// </summary> + internal static class Errors + { +#pragma warning disable SA1310 // Field names should not contain underscore +#pragma warning disable SA1600 // Elements should be documented +#pragma warning disable SA1025 // Code should not contain multiple whitespace in a row + + public static readonly int WINGET_CONFIG_ERROR_INVALID_CONFIGURATION_FILE = unchecked((int)0x8A15C001); + public static readonly int WINGET_CONFIG_ERROR_INVALID_YAML = unchecked((int)0x8A15C002); + public static readonly int WINGET_CONFIG_ERROR_INVALID_FIELD = unchecked((int)0x8A15C003); + public static readonly int WINGET_CONFIG_ERROR_UNKNOWN_CONFIGURATION_FILE_VERSION = unchecked((int)0x8A15C004); + public static readonly int WINGET_CONFIG_ERROR_SET_APPLY_FAILED = unchecked((int)0x8A15C005); + public static readonly int WINGET_CONFIG_ERROR_DUPLICATE_IDENTIFIER = unchecked((int)0x8A15C006); + public static readonly int WINGET_CONFIG_ERROR_MISSING_DEPENDENCY = unchecked((int)0x8A15C007); + public static readonly int WINGET_CONFIG_ERROR_DEPENDENCY_UNSATISFIED = unchecked((int)0x8A15C008); + public static readonly int WINGET_CONFIG_ERROR_ASSERTION_FAILED = unchecked((int)0x8A15C009); + public static readonly int WINGET_CONFIG_ERROR_MANUALLY_SKIPPED = unchecked((int)0x8A15C00A); + +#pragma warning restore SA1025 // Code should not contain multiple whitespace in a row +#pragma warning restore SA1600 // Elements should be documented +#pragma warning restore SA1310 // Field names should not contain underscore + } +} diff --git a/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationProcessorFactory.cs b/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationProcessorFactory.cs @@ -0,0 +1,113 @@ +// ----------------------------------------------------------------------------- +// <copyright file="TestConfigurationProcessorFactory.cs" company="Microsoft Corporation"> +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// </copyright> +// ----------------------------------------------------------------------------- + +namespace Microsoft.Management.Configuration.UnitTests.Helpers +{ + using System; + using System.Collections.Generic; + + /// <summary> + /// A test implementation of IConfigurationProcessorFactory. + /// </summary> + internal class TestConfigurationProcessorFactory : IConfigurationProcessorFactory + { + /// <summary> + /// Delegate type for CreateSetProcessor. + /// </summary> + /// <param name="factory">The TestConfigurationProcessorFactory that is calling this function.</param> + /// <param name="configurationSet">The set.</param> + /// <returns>A new TestConfigurationSetProcessor for the set.</returns> + internal delegate IConfigurationSetProcessor CreateSetProcessorDelegateType(TestConfigurationProcessorFactory factory, ConfigurationSet configurationSet); + + /// <summary> + /// Gets or sets the processor used when the incoming configuration set is null. + /// </summary> + internal TestConfigurationSetProcessor? NullProcessor { get; set; } + + /// <summary> + /// Gets or sets the processors to be used by this factory. + /// </summary> + internal Dictionary<ConfigurationSet, TestConfigurationSetProcessor> Processors { get; set; } = + new Dictionary<ConfigurationSet, TestConfigurationSetProcessor>(); + + /// <summary> + /// Gets or sets the exception used when the incoming configuration set is null. + /// </summary> + internal Exception? NullException { get; set; } + + /// <summary> + /// Gets or sets the exceptions to be used by this factory. + /// </summary> + internal Dictionary<ConfigurationSet, Exception> Exceptions { get; set; } = + new Dictionary<ConfigurationSet, Exception>(); + + /// <summary> + /// Gets or sets the delegate use to replace the default CreateSetProcessor functionality. + /// </summary> + internal CreateSetProcessorDelegateType? CreateSetProcessorDelegate { get; set; } + + /// <summary> + /// Creates a new TestConfigurationSetProcessor for the set. + /// </summary> + /// <param name="configurationSet">The set.</param> + /// <returns>A new TestConfigurationSetProcessor for the set.</returns> + public IConfigurationSetProcessor CreateSetProcessor(ConfigurationSet configurationSet) + { + if (this.CreateSetProcessorDelegate != null) + { + return this.CreateSetProcessorDelegate(this, configurationSet); + } + + return this.DefaultCreateSetProcessor(configurationSet); + } + + /// <summary> + /// The default test implementation that creates a new TestConfigurationSetProcessor for the set. + /// </summary> + /// <param name="configurationSet">The set.</param> + /// <returns>A new TestConfigurationSetProcessor for the set.</returns> + internal IConfigurationSetProcessor DefaultCreateSetProcessor(ConfigurationSet configurationSet) + { + if (configurationSet == null) + { + if (this.NullException != null) + { + throw this.NullException; + } + + if (this.NullProcessor == null) + { + this.NullProcessor = new TestConfigurationSetProcessor(null); + } + + return this.NullProcessor; + } + + if (this.Exceptions.ContainsKey(configurationSet)) + { + throw this.Exceptions[configurationSet]; + } + + if (!this.Processors.ContainsKey(configurationSet)) + { + this.Processors.Add(configurationSet, new TestConfigurationSetProcessor(configurationSet)); + } + + return this.Processors[configurationSet]; + } + + /// <summary> + /// A convenience function to create a new processor for the given set and store it in the dictionary for use in the test. + /// </summary> + /// <param name="configurationSet">The set.</param> + /// <returns>A new TestConfigurationSetProcessor for the set.</returns> + internal TestConfigurationSetProcessor CreateTestProcessor(ConfigurationSet configurationSet) + { + this.Processors[configurationSet] = new TestConfigurationSetProcessor(configurationSet); + return this.Processors[configurationSet]; + } + } +} diff --git a/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationSetProcessor.cs b/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationSetProcessor.cs @@ -0,0 +1,99 @@ +// ----------------------------------------------------------------------------- +// <copyright file="TestConfigurationSetProcessor.cs" company="Microsoft Corporation"> +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// </copyright> +// ----------------------------------------------------------------------------- + +namespace Microsoft.Management.Configuration.UnitTests.Helpers +{ + using System; + using System.Collections.Generic; + + /// <summary> + /// A test implementation of IConfigurationSetProcessor. + /// </summary> + internal class TestConfigurationSetProcessor : IConfigurationSetProcessor + { + private ConfigurationSet? set; + + /// <summary> + /// Initializes a new instance of the <see cref="TestConfigurationSetProcessor"/> class. + /// </summary> + /// <param name="set">The set that this processor is for.</param> + internal TestConfigurationSetProcessor(ConfigurationSet? set) + { + this.set = set; + } + + /// <summary> + /// Gets or sets the processors to be used by this factory. + /// </summary> + internal Dictionary<ConfigurationUnit, TestConfigurationUnitProcessor> Processors { get; set; } = + new Dictionary<ConfigurationUnit, TestConfigurationUnitProcessor>(); + + /// <summary> + /// Gets or sets the details to be used by this factory. + /// </summary> + internal Dictionary<ConfigurationUnit, TestConfigurationUnitProcessorDetails> Details { get; set; } = + new Dictionary<ConfigurationUnit, TestConfigurationUnitProcessorDetails>(); + + /// <summary> + /// Gets or sets the exceptions to be used by this factory. + /// </summary> + internal Dictionary<ConfigurationUnit, Exception> Exceptions { get; set; } = + new Dictionary<ConfigurationUnit, Exception>(); + + /// <summary> + /// Creates a new unit processor for the given unit. + /// </summary> + /// <param name="unit">The unit.</param> + /// <param name="directivesOverlay">Directives to override those in the unit.</param> + /// <returns>The configuration unit processor.</returns> + public IConfigurationUnitProcessor CreateUnitProcessor(ConfigurationUnit unit, IReadOnlyDictionary<string, object> directivesOverlay) + { + if (this.Exceptions.ContainsKey(unit)) + { + throw this.Exceptions[unit]; + } + + if (!this.Processors.ContainsKey(unit)) + { + this.Processors.Add(unit, new TestConfigurationUnitProcessor(unit, directivesOverlay)); + } + + return this.Processors[unit]; + } + + /// <summary> + /// Gets the unit processor details for the given unit. + /// </summary> + /// <param name="unit">The unit.</param> + /// <param name="detailLevel">The detail level requested.</param> + /// <returns>The details requested.</returns> + public IConfigurationUnitProcessorDetails GetUnitProcessorDetails(ConfigurationUnit unit, ConfigurationUnitDetailLevel detailLevel) + { + if (this.Exceptions.ContainsKey(unit)) + { + throw this.Exceptions[unit]; + } + + if (!this.Details.ContainsKey(unit)) + { + this.Details.Add(unit, new TestConfigurationUnitProcessorDetails(unit, detailLevel)); + } + + return this.Details[unit]; + } + + /// <summary> + /// Creates a new test processor for the given unit. + /// </summary> + /// <param name="unit">The unit.</param> + /// <returns>The configuration unit processor.</returns> + internal TestConfigurationUnitProcessor CreateTestProcessor(ConfigurationUnit unit) + { + this.Processors[unit] = new TestConfigurationUnitProcessor(unit); + return this.Processors[unit]; + } + } +} diff --git a/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationUnitProcessor.cs b/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationUnitProcessor.cs @@ -0,0 +1,146 @@ +// ----------------------------------------------------------------------------- +// <copyright file="TestConfigurationUnitProcessor.cs" company="Microsoft Corporation"> +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// </copyright> +// ----------------------------------------------------------------------------- + +namespace Microsoft.Management.Configuration.UnitTests.Helpers +{ + using System; + using System.Collections.Generic; + + /// <summary> + /// A test implementation of IConfigurationProcessorFactory. + /// </summary> + internal class TestConfigurationUnitProcessor : IConfigurationUnitProcessor + { + /// <summary> + /// Initializes a new instance of the <see cref="TestConfigurationUnitProcessor"/> class. + /// </summary> + /// <param name="unit">The unit.</param> + internal TestConfigurationUnitProcessor(ConfigurationUnit unit) + { + this.Unit = unit; + } + + /// <summary> + /// Initializes a new instance of the <see cref="TestConfigurationUnitProcessor"/> class. + /// </summary> + /// <param name="unit">The unit.</param> + /// <param name="directivesOverlay">The directives overlay.</param> + internal TestConfigurationUnitProcessor(ConfigurationUnit unit, IReadOnlyDictionary<string, object> directivesOverlay) + { + this.Unit = unit; + this.DirectivesOverlay = directivesOverlay; + } + + /// <summary> + /// The delegate for ApplySettings. + /// </summary> + /// <returns>The result.</returns> + internal delegate ApplySettingsResult ApplySettingsDelegateType(); + + /// <summary> + /// The delegate for GetSettings. + /// </summary> + /// <returns>The result.</returns> + internal delegate GetSettingsResult GetSettingsDelegateType(); + + /// <summary> + /// The delegate for TestSettings. + /// </summary> + /// <returns>The result.</returns> + internal delegate TestSettingsResult TestSettingsDelegateType(); + + /// <summary> + /// Gets or sets the directives overlay. + /// </summary> + public IReadOnlyDictionary<string, object>? DirectivesOverlay { get; set; } + + /// <summary> + /// Gets the configuration unit. + /// </summary> + public ConfigurationUnit Unit { get; private set; } + + /// <summary> + /// Gets or sets the delegate object for ApplySettings. + /// </summary> + internal ApplySettingsDelegateType? ApplySettingsDelegate { get; set; } + + /// <summary> + /// Gets the number of times ApplySettings is called. + /// </summary> + internal int ApplySettingsCalls { get; private set; } = 0; + + /// <summary> + /// Gets or sets the delegate object for GetSettings. + /// </summary> + internal GetSettingsDelegateType? GetSettingsDelegate { get; set; } + + /// <summary> + /// Gets the number of times GetSettings is called. + /// </summary> + internal int GetSettingsCalls { get; private set; } = 0; + + /// <summary> + /// Gets or sets the delegate object for TestSettings. + /// </summary> + internal TestSettingsDelegateType? TestSettingsDelegate { get; set; } + + /// <summary> + /// Gets the number of times TestSettings is called. + /// </summary> + internal int TestSettingsCalls { get; private set; } = 0; + + /// <summary> + /// Calls the ApplySettingsDelegate if one is provided; returns success if not. + /// </summary> + /// <returns>The result.</returns> + public ApplySettingsResult ApplySettings() + { + ++this.ApplySettingsCalls; + if (this.ApplySettingsDelegate != null) + { + return this.ApplySettingsDelegate(); + } + else + { + return new ApplySettingsResult(); + } + } + + /// <summary> + /// Calls the GetSettingsDelegate if one is provided; returns success if not (with no settings values). + /// </summary> + /// <returns>The result.</returns> + public GetSettingsResult GetSettings() + { + ++this.GetSettingsCalls; + if (this.GetSettingsDelegate != null) + { + return this.GetSettingsDelegate(); + } + else + { + return new GetSettingsResult(); + } + } + + /// <summary> + /// Calls the TestSettingsDelegate if one is provided; returns success if not (with a positive test result). + /// </summary> + /// <returns>The result.</returns> + public TestSettingsResult TestSettings() + { + ++this.TestSettingsCalls; + if (this.TestSettingsDelegate != null) + { + return this.TestSettingsDelegate(); + } + else + { + return new TestSettingsResult { TestResult = ConfigurationTestResult.Positive }; + } + } + } +} diff --git a/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationUnitProcessorDetails.cs b/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestConfigurationUnitProcessorDetails.cs @@ -0,0 +1,68 @@ +// ----------------------------------------------------------------------------- +// <copyright file="TestConfigurationUnitProcessorDetails.cs" company="Microsoft Corporation"> +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// </copyright> +// ----------------------------------------------------------------------------- + +namespace Microsoft.Management.Configuration.UnitTests.Helpers +{ + using System; + using System.Collections.Generic; + using Windows.Security.Cryptography.Certificates; + + /// <summary> + /// A test implementation of IConfigurationProcessorFactory. + /// </summary> + internal class TestConfigurationUnitProcessorDetails : IConfigurationUnitProcessorDetails + { + private ConfigurationUnit unit; + private ConfigurationUnitDetailLevel detailLevel; + + /// <summary> + /// Initializes a new instance of the <see cref="TestConfigurationUnitProcessorDetails"/> class. + /// </summary> + /// <param name="unit">The unit.</param> + /// <param name="detailLevel">The level of the details.</param> + internal TestConfigurationUnitProcessorDetails(ConfigurationUnit unit, ConfigurationUnitDetailLevel detailLevel) + { + this.unit = unit; + this.detailLevel = detailLevel; + } + +#pragma warning disable SA1600 // Elements should be documented + public string? Author { get; internal set; } + + public bool IsLocal { get; internal set; } + + public string? ModuleDescription { get; internal set; } + + public Uri? ModuleDocumentationUri { get; internal set; } + + public string? ModuleName { get; internal set; } + + public string? ModuleSource { get; internal set; } + + public string? ModuleType { get; internal set; } + + public DateTimeOffset PublishedDate { get; internal set; } + + public Uri? PublishedModuleUri { get; internal set; } + + public string? Publisher { get; internal set; } + + public IReadOnlyList<IConfigurationUnitSettingDetails>? Settings { get; internal set; } + + public CertificateChain? SigningCertificateChain { get; internal set; } + + public string? UnitDescription { get; internal set; } + + public Uri? UnitDocumentationUri { get; internal set; } + + public Uri? UnitIconUri { get; internal set; } + + public string? UnitName { get; internal set; } + + public string? Version { get; internal set; } +#pragma warning restore SA1600 // Elements should be documented + } +} diff --git a/src/Microsoft.Management.Configuration.UnitTests/Microsoft.Management.Configuration.UnitTests.csproj b/src/Microsoft.Management.Configuration.UnitTests/Microsoft.Management.Configuration.UnitTests.csproj @@ -0,0 +1,44 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net6.0-windows10.0.22000.0</TargetFramework> + <Nullable>enable</Nullable> + <SupportedOSPlatformVersion>10.0.17763.0</SupportedOSPlatformVersion> + <Platforms>x64;x86;arm64</Platforms> + <OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\$(MSBuildProjectName)\</OutputPath> + </PropertyGroup> + + <ItemGroup> + <AdditionalFiles Include="..\stylecop.json" Link="stylecop.json" /> + </ItemGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.Windows.CsWinRT" Version="2.0.1" /> + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" /> + <PackageReference Include="xunit" Version="2.4.2" /> + <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + <PrivateAssets>all</PrivateAssets> + </PackageReference> + <PackageReference Include="coverlet.collector" Version="3.1.2"> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + <PrivateAssets>all</PrivateAssets> + </PackageReference> + <PackageReference Include="StyleCop.Analyzers" Version="1.1.118"> + <PrivateAssets>all</PrivateAssets> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + </PackageReference> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\Microsoft.Management.Configuration.Projection\Microsoft.Management.Configuration.Projection.csproj"> + <OutputItemType>Content</OutputItemType> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + <ReferenceOutputAssembly>True</ReferenceOutputAssembly> + </ProjectReference> + <ProjectReference Include="..\Microsoft.Management.Configuration\Microsoft.Management.Configuration.vcxproj"> + <Private>True</Private> + </ProjectReference> + </ItemGroup> + +</Project> diff --git a/src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationProcessorApplyTests.cs b/src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationProcessorApplyTests.cs @@ -0,0 +1,439 @@ +// ----------------------------------------------------------------------------- +// <copyright file="ConfigurationProcessorApplyTests.cs" company="Microsoft Corporation"> +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// </copyright> +// ----------------------------------------------------------------------------- + +namespace Microsoft.Management.Configuration.UnitTests.Tests +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Runtime.InteropServices; + using System.Threading; + using Microsoft.Management.Configuration.UnitTests.Fixtures; + using Microsoft.Management.Configuration.UnitTests.Helpers; + using Microsoft.VisualBasic; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Xunit; + using Xunit.Abstractions; + + /// <summary> + /// Unit tests for running test on the processor. + /// </summary> + [Collection("UnitTestCollection")] + public class ConfigurationProcessorApplyTests : ConfigurationProcessorTestBase + { + /// <summary> + /// Initializes a new instance of the <see cref="ConfigurationProcessorApplyTests"/> class. + /// </summary> + /// <param name="fixture">Unit test fixture.</param> + /// <param name="log">Log helper.</param> + public ConfigurationProcessorApplyTests(UnitTestFixture fixture, ITestOutputHelper log) + : base(fixture, log) + { + } + + /// <summary> + /// An error creating the set processor results in an error for the function. + /// </summary> + [Fact] + public void ApplySet_SetProcessorError() + { + ConfigurationSet configurationSet = new ConfigurationSet(); + + TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); + factory.Exceptions.Add(configurationSet, new FileNotFoundException()); + + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); + + Assert.Throws<FileNotFoundException>(() => processor.ApplySet(configurationSet, ApplyConfigurationSetFlags.None)); + } + + /// <summary> + /// Multiple configuration units with the same identifier. + /// </summary> + [Fact] + public void ApplySet_DuplicateIdentifiers() + { + ConfigurationSet configurationSet = new ConfigurationSet(); + ConfigurationUnit configurationUnit1 = new ConfigurationUnit(); + ConfigurationUnit configurationUnit2 = new ConfigurationUnit(); + ConfigurationUnit configurationUnitDifferentIdentifier = new ConfigurationUnit(); + string sharedIdentifier = "SameIdentifier"; + configurationUnit1.Identifier = sharedIdentifier; + configurationUnit2.Identifier = sharedIdentifier; + configurationSet.ConfigurationUnits = new ConfigurationUnit[] { configurationUnit1, configurationUnit2, configurationUnitDifferentIdentifier }; + + TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); + + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); + + ApplyConfigurationSetResult result = processor.ApplySet(configurationSet, ApplyConfigurationSetFlags.None); + Assert.NotNull(result); + Assert.NotNull(result.ResultCode); + Assert.Equal(Errors.WINGET_CONFIG_ERROR_DUPLICATE_IDENTIFIER, result.ResultCode.HResult); + Assert.Equal(3, result.UnitResults.Count); + + foreach (var configurationUnit in new ConfigurationUnit[] { configurationUnit1, configurationUnit2 }) + { + ApplyConfigurationUnitResult unitResult = result.UnitResults.First(x => x.Unit == configurationUnit); + Assert.NotNull(unitResult); + Assert.False(unitResult.PreviouslyInDesiredState); + Assert.False(unitResult.RebootRequired); + Assert.NotNull(unitResult.ResultInformation); + Assert.NotNull(unitResult.ResultInformation.ResultCode); + Assert.Equal(Errors.WINGET_CONFIG_ERROR_DUPLICATE_IDENTIFIER, unitResult.ResultInformation.ResultCode.HResult); + } + + ApplyConfigurationUnitResult unitResultDifferentIdentifier = result.UnitResults.First(x => x.Unit == configurationUnitDifferentIdentifier); + Assert.NotNull(unitResultDifferentIdentifier); + Assert.False(unitResultDifferentIdentifier.PreviouslyInDesiredState); + Assert.False(unitResultDifferentIdentifier.RebootRequired); + Assert.NotNull(unitResultDifferentIdentifier.ResultInformation); + Assert.Null(unitResultDifferentIdentifier.ResultInformation.ResultCode); + } + + /// <summary> + /// A configuration unit has a dependency that is not in the set. + /// </summary> + [Fact] + public void ApplySet_MissingDependency() + { + ConfigurationSet configurationSet = new ConfigurationSet(); + ConfigurationUnit configurationUnit = new ConfigurationUnit(); + ConfigurationUnit configurationUnitMissingDependency = new ConfigurationUnit(); + configurationUnit.Identifier = "Identifier"; + configurationUnitMissingDependency.Dependencies = new string[] { "Dependency" }; + configurationSet.ConfigurationUnits = new ConfigurationUnit[] { configurationUnit, configurationUnitMissingDependency }; + + TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); + + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); + + ApplyConfigurationSetResult result = processor.ApplySet(configurationSet, ApplyConfigurationSetFlags.None); + Assert.NotNull(result); + Assert.NotNull(result.ResultCode); + Assert.Equal(Errors.WINGET_CONFIG_ERROR_MISSING_DEPENDENCY, result.ResultCode.HResult); + Assert.Equal(2, result.UnitResults.Count); + + ApplyConfigurationUnitResult unitResult = result.UnitResults.First(x => x.Unit == configurationUnit); + Assert.NotNull(unitResult); + Assert.False(unitResult.PreviouslyInDesiredState); + Assert.False(unitResult.RebootRequired); + Assert.NotNull(unitResult.ResultInformation); + Assert.Null(unitResult.ResultInformation.ResultCode); + + unitResult = result.UnitResults.First(x => x.Unit == configurationUnitMissingDependency); + Assert.NotNull(unitResult); + Assert.False(unitResult.PreviouslyInDesiredState); + Assert.False(unitResult.RebootRequired); + Assert.NotNull(unitResult.ResultInformation); + Assert.NotNull(unitResult.ResultInformation.ResultCode); + Assert.Equal(Errors.WINGET_CONFIG_ERROR_MISSING_DEPENDENCY, unitResult.ResultInformation.ResultCode.HResult); + } + + /// <summary> + /// The configuration set has a dependency cycle. + /// </summary> + [Fact] + public void ApplySet_DependencyCycle() + { + ConfigurationSet configurationSet = new ConfigurationSet(); + ConfigurationUnit configurationUnit1 = new ConfigurationUnit(); + ConfigurationUnit configurationUnit2 = new ConfigurationUnit(); + ConfigurationUnit configurationUnit3 = new ConfigurationUnit(); + configurationUnit1.Identifier = "Identifier1"; + configurationUnit2.Identifier = "Identifier2"; + configurationUnit3.Identifier = "Identifier3"; + configurationUnit1.Dependencies = new string[] { "Identifier3" }; + configurationUnit2.Dependencies = new string[] { "Identifier1" }; + configurationUnit3.Dependencies = new string[] { "Identifier2" }; + configurationSet.ConfigurationUnits = new ConfigurationUnit[] { configurationUnit1, configurationUnit2, configurationUnit3 }; + + TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); + + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); + + ApplyConfigurationSetResult result = processor.ApplySet(configurationSet, ApplyConfigurationSetFlags.None); + Assert.NotNull(result); + Assert.NotNull(result.ResultCode); + Assert.Equal(Errors.WINGET_CONFIG_ERROR_DEPENDENCY_UNSATISFIED, result.ResultCode.HResult); + Assert.Equal(3, result.UnitResults.Count); + + foreach (var unitResult in result.UnitResults) + { + Assert.NotNull(unitResult); + Assert.False(unitResult.PreviouslyInDesiredState); + Assert.False(unitResult.RebootRequired); + Assert.NotNull(unitResult.ResultInformation); + Assert.NotNull(unitResult.ResultInformation.ResultCode); + Assert.Equal(Errors.WINGET_CONFIG_ERROR_DEPENDENCY_UNSATISFIED, unitResult.ResultInformation.ResultCode.HResult); + } + } + + /// <summary> + /// Checks that the intent for configuration units is handled properly. + /// </summary> + [Fact] + public void ApplySet_IntentRespected() + { + ConfigurationSet configurationSet = new ConfigurationSet(); + ConfigurationUnit configurationUnitAssert = new ConfigurationUnit { Intent = ConfigurationUnitIntent.Assert }; + ConfigurationUnit configurationUnitInform = new ConfigurationUnit { Intent = ConfigurationUnitIntent.Inform }; + ConfigurationUnit configurationUnitApply = new ConfigurationUnit { Intent = ConfigurationUnitIntent.Apply }; + configurationSet.ConfigurationUnits = new ConfigurationUnit[] { configurationUnitInform, configurationUnitApply, configurationUnitAssert }; + + TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); + TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); + TestConfigurationUnitProcessor unitProcessorAssert = setProcessor.CreateTestProcessor(configurationUnitAssert); + TestConfigurationUnitProcessor unitProcessorInform = setProcessor.CreateTestProcessor(configurationUnitInform); + TestConfigurationUnitProcessor unitProcessorApply = setProcessor.CreateTestProcessor(configurationUnitApply); + unitProcessorApply.TestSettingsDelegate = () => new TestSettingsResult { TestResult = ConfigurationTestResult.Negative }; + + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); + + ApplyConfigurationSetResult result = processor.ApplySet(configurationSet, ApplyConfigurationSetFlags.None); + Assert.NotNull(result); + Assert.Null(result.ResultCode); + Assert.Equal(3, result.UnitResults.Count); + + foreach (var unitResult in result.UnitResults) + { + Assert.NotNull(unitResult); + Assert.False(unitResult.PreviouslyInDesiredState); + Assert.False(unitResult.RebootRequired); + Assert.NotNull(unitResult.ResultInformation); + Assert.Null(unitResult.ResultInformation.ResultCode); + } + + Assert.Equal(1, unitProcessorAssert.TestSettingsCalls); + Assert.Equal(0, unitProcessorAssert.GetSettingsCalls); + Assert.Equal(0, unitProcessorAssert.ApplySettingsCalls); + + Assert.Equal(0, unitProcessorInform.TestSettingsCalls); + Assert.Equal(1, unitProcessorInform.GetSettingsCalls); + Assert.Equal(0, unitProcessorInform.ApplySettingsCalls); + + Assert.Equal(1, unitProcessorApply.TestSettingsCalls); + Assert.Equal(0, unitProcessorApply.GetSettingsCalls); + Assert.Equal(1, unitProcessorApply.ApplySettingsCalls); + } + + /// <summary> + /// An assertion fails to run. + /// </summary> + [Fact] + public void ApplySet_AssertionFailure() + { + ConfigurationSet configurationSet = new ConfigurationSet(); + ConfigurationUnit configurationUnitAssert = new ConfigurationUnit { Intent = ConfigurationUnitIntent.Assert }; + ConfigurationUnit configurationUnitApply = new ConfigurationUnit { Intent = ConfigurationUnitIntent.Apply }; + configurationSet.ConfigurationUnits = new ConfigurationUnit[] { configurationUnitApply, configurationUnitAssert }; + + TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); + TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); + TestConfigurationUnitProcessor unitProcessorAssert = setProcessor.CreateTestProcessor(configurationUnitAssert); + unitProcessorAssert.TestSettingsDelegate = () => throw new NullReferenceException(); + TestConfigurationUnitProcessor unitProcessorApply = setProcessor.CreateTestProcessor(configurationUnitApply); + unitProcessorApply.TestSettingsDelegate = () => new TestSettingsResult { TestResult = ConfigurationTestResult.Negative }; + + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); + + ApplyConfigurationSetResult result = processor.ApplySet(configurationSet, ApplyConfigurationSetFlags.None); + Assert.NotNull(result); + Assert.NotNull(result.ResultCode); + Assert.Equal(Errors.WINGET_CONFIG_ERROR_ASSERTION_FAILED, result.ResultCode.HResult); + Assert.Equal(2, result.UnitResults.Count); + + ApplyConfigurationUnitResult unitResult = result.UnitResults.First(x => x.Unit == configurationUnitAssert); + Assert.NotNull(unitResult); + Assert.False(unitResult.PreviouslyInDesiredState); + Assert.False(unitResult.RebootRequired); + Assert.NotNull(unitResult.ResultInformation); + Assert.NotNull(unitResult.ResultInformation.ResultCode); + Assert.IsType<NullReferenceException>(unitResult.ResultInformation.ResultCode); + + unitResult = result.UnitResults.First(x => x.Unit == configurationUnitApply); + Assert.NotNull(unitResult); + Assert.False(unitResult.PreviouslyInDesiredState); + Assert.False(unitResult.RebootRequired); + Assert.NotNull(unitResult.ResultInformation); + Assert.NotNull(unitResult.ResultInformation.ResultCode); + Assert.Equal(Errors.WINGET_CONFIG_ERROR_ASSERTION_FAILED, unitResult.ResultInformation.ResultCode.HResult); + } + + /// <summary> + /// An assertion is found to be false. + /// </summary> + [Fact] + public void ApplySet_AssertionNegative() + { + ConfigurationSet configurationSet = new ConfigurationSet(); + ConfigurationUnit configurationUnitAssert = new ConfigurationUnit { Intent = ConfigurationUnitIntent.Assert }; + ConfigurationUnit configurationUnitApply = new ConfigurationUnit { Intent = ConfigurationUnitIntent.Apply }; + configurationSet.ConfigurationUnits = new ConfigurationUnit[] { configurationUnitApply, configurationUnitAssert }; + + TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); + TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); + TestConfigurationUnitProcessor unitProcessorAssert = setProcessor.CreateTestProcessor(configurationUnitAssert); + unitProcessorAssert.TestSettingsDelegate = () => new TestSettingsResult { TestResult = ConfigurationTestResult.Negative }; + TestConfigurationUnitProcessor unitProcessorApply = setProcessor.CreateTestProcessor(configurationUnitApply); + unitProcessorApply.TestSettingsDelegate = () => new TestSettingsResult { TestResult = ConfigurationTestResult.Negative }; + + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); + + ApplyConfigurationSetResult result = processor.ApplySet(configurationSet, ApplyConfigurationSetFlags.None); + Assert.NotNull(result); + Assert.NotNull(result.ResultCode); + Assert.Equal(Errors.WINGET_CONFIG_ERROR_ASSERTION_FAILED, result.ResultCode.HResult); + Assert.Equal(2, result.UnitResults.Count); + + foreach (var unitResult in result.UnitResults) + { + Assert.NotNull(unitResult); + Assert.False(unitResult.PreviouslyInDesiredState); + Assert.False(unitResult.RebootRequired); + Assert.NotNull(unitResult.ResultInformation); + Assert.NotNull(unitResult.ResultInformation.ResultCode); + Assert.Equal(Errors.WINGET_CONFIG_ERROR_ASSERTION_FAILED, unitResult.ResultInformation.ResultCode.HResult); + } + } + + /// <summary> + /// A unit in the correct state is not applied again. + /// </summary> + [Fact] + public void ApplySet_UnitAlreadyInCorrectState() + { + ConfigurationSet configurationSet = new ConfigurationSet(); + ConfigurationUnit configurationUnit = new ConfigurationUnit { Intent = ConfigurationUnitIntent.Apply }; + configurationSet.ConfigurationUnits = new ConfigurationUnit[] { configurationUnit }; + + TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); + TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); + TestConfigurationUnitProcessor unitProcessor = setProcessor.CreateTestProcessor(configurationUnit); + unitProcessor.TestSettingsDelegate = () => new TestSettingsResult { TestResult = ConfigurationTestResult.Positive }; + + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); + + ApplyConfigurationSetResult result = processor.ApplySet(configurationSet, ApplyConfigurationSetFlags.None); + Assert.NotNull(result); + Assert.Null(result.ResultCode); + Assert.Equal(1, result.UnitResults.Count); + + ApplyConfigurationUnitResult unitResult = result.UnitResults.First(); + Assert.NotNull(unitResult); + Assert.True(unitResult.PreviouslyInDesiredState); + Assert.False(unitResult.RebootRequired); + Assert.NotNull(unitResult.ResultInformation); + Assert.Null(unitResult.ResultInformation.ResultCode); + } + + /// <summary> + /// Checks the progress reporting. + /// </summary> + [Fact] + public void ApplySet_Progress() + { + ConfigurationSet configurationSet = new ConfigurationSet(); + ConfigurationUnit assert1 = new ConfigurationUnit() { Intent = ConfigurationUnitIntent.Assert, Identifier = "Assert1" }; + ConfigurationUnit assert2 = new ConfigurationUnit() { Intent = ConfigurationUnitIntent.Assert, Identifier = "Assert2", Dependencies = new string[] { assert1.Identifier } }; + ConfigurationUnit inform1 = new ConfigurationUnit() { Intent = ConfigurationUnitIntent.Inform, Identifier = "Inform1" }; + ConfigurationUnit apply1 = new ConfigurationUnit() { Intent = ConfigurationUnitIntent.Apply, Identifier = "Apply1" }; + ConfigurationUnit apply2 = new ConfigurationUnit() { Intent = ConfigurationUnitIntent.Apply, Identifier = "Apply2" }; + ConfigurationUnit apply3 = new ConfigurationUnit() { Intent = ConfigurationUnitIntent.Apply, Identifier = "Apply3", Dependencies = new string[] { apply1.Identifier, apply2.Identifier } }; + ConfigurationUnit apply4 = new ConfigurationUnit() { Intent = ConfigurationUnitIntent.Apply, Identifier = "Apply4", ShouldApply = false }; + ConfigurationUnit apply5 = new ConfigurationUnit() { Intent = ConfigurationUnitIntent.Apply, Identifier = "Apply5", Dependencies = new string[] { apply4.Identifier } }; + configurationSet.ConfigurationUnits = new ConfigurationUnit[] { assert2, assert1, inform1, apply1, apply3, apply4, apply2, apply5 }; + + ManualResetEvent startProcessing = new ManualResetEvent(false); + + TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); + factory.CreateSetProcessorDelegate = (f, c) => + { + startProcessing.WaitOne(); + return f.DefaultCreateSetProcessor(c); + }; + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); + List<ConfigurationSetChangeData> progressEvents = new List<ConfigurationSetChangeData>(); + + var operation = processor.ApplySetAsync(configurationSet, ApplyConfigurationSetFlags.None); + operation.Progress = (asyncInfo, progress) => progressEvents.Add(progress); + startProcessing.Set(); + operation.AsTask().Wait(); + ApplyConfigurationSetResult result = operation.GetResults(); + + Assert.NotNull(result); + Assert.NotNull(result.ResultCode); + Assert.Equal(Errors.WINGET_CONFIG_ERROR_DEPENDENCY_UNSATISFIED, result.ResultCode.HResult); + Assert.NotNull(result.UnitResults); + Assert.Equal(configurationSet.ConfigurationUnits.Count, result.UnitResults.Count); + + // Verify that progress events match the expected + ExpectedConfigurationChangeData[] expectedProgress = new ExpectedConfigurationChangeData[] + { + new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.SetStateChanged, SetState = ConfigurationSetState.InProgress }, + new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.InProgress, Unit = assert1 }, + new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.Completed, Unit = assert1 }, + new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.InProgress, Unit = assert2 }, + new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.Completed, Unit = assert2 }, + new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.InProgress, Unit = inform1 }, + new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.Completed, Unit = inform1 }, + new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.InProgress, Unit = apply1 }, + new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.Completed, Unit = apply1 }, + new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.Skipped, Unit = apply4, HResult = Errors.WINGET_CONFIG_ERROR_MANUALLY_SKIPPED }, + new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.InProgress, Unit = apply2 }, + new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.Completed, Unit = apply2 }, + new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.InProgress, Unit = apply3 }, + new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.Completed, Unit = apply3 }, + new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.UnitStateChanged, UnitState = ConfigurationUnitState.Skipped, Unit = apply5, HResult = Errors.WINGET_CONFIG_ERROR_DEPENDENCY_UNSATISFIED }, + new ExpectedConfigurationChangeData() { Change = ConfigurationSetChangeEventType.SetStateChanged, SetState = ConfigurationSetState.Completed }, + }; + + Assert.Equal(expectedProgress.Count(), progressEvents.Count); + + for (int i = 0; i < progressEvents.Count; ++i) + { + this.Log.WriteLine($"Comparing event {i}"); + Assert.Equal(expectedProgress[i].Change, progressEvents[i].Change); + switch (expectedProgress[i].Change) + { + case ConfigurationSetChangeEventType.SetStateChanged: + Assert.Equal(expectedProgress[i].SetState, progressEvents[i].SetState); + break; + case ConfigurationSetChangeEventType.UnitStateChanged: + Assert.Equal(expectedProgress[i].UnitState, progressEvents[i].UnitState); + Assert.Same(expectedProgress[i].Unit, progressEvents[i].Unit); + + Assert.NotNull(progressEvents[i].ResultInformation); + if (expectedProgress[i].HResult == 0) + { + Assert.Null(progressEvents[i].ResultInformation.ResultCode); + } + else + { + Assert.NotNull(progressEvents[i].ResultInformation.ResultCode); + Assert.Equal(expectedProgress[i].HResult, progressEvents[i].ResultInformation.ResultCode.HResult); + } + + break; + default: + Assert.Fail("Unexpected ConfigurationSetChangeEventType value"); + break; + } + } + } + + private struct ExpectedConfigurationChangeData + { + public ConfigurationSetChangeEventType Change; + public ConfigurationSetState SetState; + public ConfigurationUnitState UnitState; + public int HResult; + public ConfigurationUnit Unit; + } + } +} diff --git a/src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationProcessorTestTests.cs b/src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationProcessorTestTests.cs @@ -0,0 +1,285 @@ +// ----------------------------------------------------------------------------- +// <copyright file="ConfigurationProcessorTestTests.cs" company="Microsoft Corporation"> +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// </copyright> +// ----------------------------------------------------------------------------- + +namespace Microsoft.Management.Configuration.UnitTests.Tests +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Runtime.InteropServices; + using Microsoft.Management.Configuration.UnitTests.Fixtures; + using Microsoft.Management.Configuration.UnitTests.Helpers; + using Microsoft.VisualBasic; + using Microsoft.VisualStudio.TestPlatform.ObjectModel; + using Xunit; + using Xunit.Abstractions; + + /// <summary> + /// Unit tests for running test on the processor. + /// </summary> + [Collection("UnitTestCollection")] + public class ConfigurationProcessorTestTests : ConfigurationProcessorTestBase + { + /// <summary> + /// Initializes a new instance of the <see cref="ConfigurationProcessorTestTests"/> class. + /// </summary> + /// <param name="fixture">Unit test fixture.</param> + /// <param name="log">Log helper.</param> + public ConfigurationProcessorTestTests(UnitTestFixture fixture, ITestOutputHelper log) + : base(fixture, log) + { + } + + /// <summary> + /// An error creating the set processor results in an error for the function. + /// </summary> + [Fact] + public void TestSet_SetProcessorError() + { + ConfigurationSet configurationSet = new ConfigurationSet(); + + TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); + factory.Exceptions.Add(configurationSet, new FileNotFoundException()); + + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); + + Assert.Throws<FileNotFoundException>(() => processor.TestSet(configurationSet)); + } + + /// <summary> + /// An error creating a unit processor results in an error for that unit. + /// </summary> + [Fact] + public void TestSet_UnitProcessorCreationError() + { + ConfigurationSet configurationSet = new ConfigurationSet(); + ConfigurationUnit configurationUnitThrows = new ConfigurationUnit(); + ConfigurationUnit configurationUnitWorks = new ConfigurationUnit(); + configurationSet.ConfigurationUnits = new ConfigurationUnit[] { configurationUnitThrows, configurationUnitWorks }; + + TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); + TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); + setProcessor.Exceptions.Add(configurationUnitThrows, new NullReferenceException()); + + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); + + TestConfigurationSetResult result = processor.TestSet(configurationSet); + + Assert.NotNull(result); + Assert.Equal(ConfigurationTestResult.Failed, result.TestResult); + Assert.NotNull(result.UnitResults); + Assert.Equal(2, result.UnitResults.Count); + + TestConfigurationUnitResult throwsResult = result.UnitResults.First(x => x.Unit == configurationUnitThrows); + Assert.NotNull(throwsResult); + Assert.Equal(ConfigurationTestResult.Failed, throwsResult.TestResult); + Assert.NotNull(throwsResult.ResultInformation); + Assert.NotNull(throwsResult.ResultInformation.ResultCode); + Assert.IsType<NullReferenceException>(throwsResult.ResultInformation.ResultCode); + + TestConfigurationUnitResult worksResult = result.UnitResults.First(x => x.Unit == configurationUnitWorks); + Assert.NotNull(worksResult); + Assert.Equal(ConfigurationTestResult.Positive, worksResult.TestResult); + Assert.NotNull(worksResult.ResultInformation); + Assert.Null(worksResult.ResultInformation.ResultCode); + } + + /// <summary> + /// An error running a unit processor results in an error for that unit. + /// </summary> + [Fact] + public void TestSet_UnitProcessorExecutionError() + { + ConfigurationSet configurationSet = new ConfigurationSet(); + ConfigurationUnit configurationUnitThrows = new ConfigurationUnit(); + ConfigurationUnit configurationUnitWorks = new ConfigurationUnit(); + configurationSet.ConfigurationUnits = new ConfigurationUnit[] { configurationUnitWorks, configurationUnitThrows }; + + TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); + TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); + TestConfigurationUnitProcessor unitProcessor = setProcessor.CreateTestProcessor(configurationUnitThrows); + unitProcessor.TestSettingsDelegate = () => throw new NullReferenceException(); + + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); + + TestConfigurationSetResult result = processor.TestSet(configurationSet); + + Assert.NotNull(result); + Assert.Equal(ConfigurationTestResult.Failed, result.TestResult); + Assert.NotNull(result.UnitResults); + Assert.Equal(2, result.UnitResults.Count); + + TestConfigurationUnitResult throwsResult = result.UnitResults.First(x => x.Unit == configurationUnitThrows); + Assert.NotNull(throwsResult); + Assert.Equal(ConfigurationTestResult.Failed, throwsResult.TestResult); + Assert.NotNull(throwsResult.ResultInformation); + Assert.NotNull(throwsResult.ResultInformation.ResultCode); + Assert.IsType<NullReferenceException>(throwsResult.ResultInformation.ResultCode); + + TestConfigurationUnitResult worksResult = result.UnitResults.First(x => x.Unit == configurationUnitWorks); + Assert.NotNull(worksResult); + Assert.Equal(ConfigurationTestResult.Positive, worksResult.TestResult); + Assert.NotNull(worksResult.ResultInformation); + Assert.Null(worksResult.ResultInformation.ResultCode); + } + + /// <summary> + /// An error running a unit processor results in an error for that unit. + /// </summary> + [Fact] + public void TestSet_UnitProcessorResultError() + { + ConfigurationSet configurationSet = new ConfigurationSet(); + ConfigurationUnit configurationUnitThrows = new ConfigurationUnit(); + ConfigurationUnit configurationUnitWorks = new ConfigurationUnit(); + configurationSet.ConfigurationUnits = new ConfigurationUnit[] { configurationUnitWorks, configurationUnitThrows }; + + TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); + TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); + TestConfigurationUnitProcessor unitProcessor = setProcessor.CreateTestProcessor(configurationUnitThrows); + TestSettingsResult testResult = new TestSettingsResult(); + testResult.TestResult = ConfigurationTestResult.Failed; + testResult.ResultInformation.ResultCode = new NullReferenceException(); + testResult.ResultInformation.Description = "Failed again"; + unitProcessor.TestSettingsDelegate = () => testResult; + + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); + + TestConfigurationSetResult result = processor.TestSet(configurationSet); + + Assert.NotNull(result); + Assert.Equal(ConfigurationTestResult.Failed, result.TestResult); + Assert.NotNull(result.UnitResults); + Assert.Equal(2, result.UnitResults.Count); + + TestConfigurationUnitResult throwsResult = result.UnitResults.First(x => x.Unit == configurationUnitThrows); + Assert.NotNull(throwsResult); + Assert.Equal(ConfigurationTestResult.Failed, throwsResult.TestResult); + Assert.NotNull(throwsResult.ResultInformation); + Assert.NotNull(throwsResult.ResultInformation.ResultCode); + Assert.IsType<NullReferenceException>(throwsResult.ResultInformation.ResultCode); + Assert.Equal(testResult.ResultInformation.Description, throwsResult.ResultInformation.Description); + + TestConfigurationUnitResult worksResult = result.UnitResults.First(x => x.Unit == configurationUnitWorks); + Assert.NotNull(worksResult); + Assert.Equal(ConfigurationTestResult.Positive, worksResult.TestResult); + Assert.NotNull(worksResult.ResultInformation); + Assert.Null(worksResult.ResultInformation.ResultCode); + } + + /// <summary> + /// Ensures that the expected TestResult comes back for all types. + /// </summary> + [Fact] + public void TestSet_ResultTypes() + { + this.RunTestSetTestForResultTypes(new ConfigurationTestResult[] { ConfigurationTestResult.Positive, ConfigurationTestResult.Negative, ConfigurationTestResult.Failed, ConfigurationTestResult.NotRun }, ConfigurationTestResult.Failed); + } + + /// <summary> + /// Ensures that a single negative makes the overall result negative. + /// </summary> + [Fact] + public void TestSet_Negative() + { + this.RunTestSetTestForResultTypes(new ConfigurationTestResult[] { ConfigurationTestResult.Positive, ConfigurationTestResult.Negative, ConfigurationTestResult.Positive, ConfigurationTestResult.NotRun }, ConfigurationTestResult.Negative); + } + + /// <summary> + /// Ensures that a single not run test does not impact the positive result. + /// </summary> + [Fact] + public void TestSet_Positive() + { + this.RunTestSetTestForResultTypes(new ConfigurationTestResult[] { ConfigurationTestResult.Positive, ConfigurationTestResult.Positive, ConfigurationTestResult.Positive, ConfigurationTestResult.NotRun }, ConfigurationTestResult.Positive); + } + + /// <summary> + /// Creates a test scenario where the units produce the given test results. + /// </summary> + /// <param name="resultTypes">The result types for each unit.</param> + /// <param name="overallResult">The expected overall test result.</param> + private void RunTestSetTestForResultTypes(ConfigurationTestResult[] resultTypes, ConfigurationTestResult overallResult) + { + ConfigurationSet configurationSet = new ConfigurationSet(); + ConfigurationUnit[] configurationUnits = new ConfigurationUnit[resultTypes.Length]; + + TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); + TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); + + TestSettingsResult positiveResult = new TestSettingsResult(); + positiveResult.TestResult = ConfigurationTestResult.Positive; + + TestSettingsResult negativeResult = new TestSettingsResult(); + negativeResult.TestResult = ConfigurationTestResult.Negative; + + TestSettingsResult failedResult = new TestSettingsResult(); + failedResult.TestResult = ConfigurationTestResult.Failed; + failedResult.ResultInformation.ResultCode = new NullReferenceException(); + failedResult.ResultInformation.Description = "Failed again"; + + for (int i = 0; i < resultTypes.Length; ++i) + { + configurationUnits[i] = new ConfigurationUnit(); + configurationUnits[i].UnitName = $"Unit {i}"; + TestConfigurationUnitProcessor unitProcessor = setProcessor.CreateTestProcessor(configurationUnits[i]); + + switch (resultTypes[i]) + { + case ConfigurationTestResult.Positive: + unitProcessor.TestSettingsDelegate = () => positiveResult; + break; + case ConfigurationTestResult.Negative: + unitProcessor.TestSettingsDelegate = () => negativeResult; + break; + case ConfigurationTestResult.NotRun: + configurationUnits[i].Intent = ConfigurationUnitIntent.Inform; + break; + case ConfigurationTestResult.Failed: + unitProcessor.TestSettingsDelegate = () => failedResult; + break; + } + } + + configurationSet.ConfigurationUnits = configurationUnits; + + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); + + TestConfigurationSetResult result = processor.TestSet(configurationSet); + + Assert.NotNull(result); + Assert.Equal(overallResult, result.TestResult); + Assert.NotNull(result.UnitResults); + Assert.Equal(resultTypes.Length, result.UnitResults.Count); + + for (int i = 0; i < resultTypes.Length; ++i) + { + TestConfigurationUnitResult unitResult = result.UnitResults.First(x => x.Unit == configurationUnits[i]); + + Assert.NotNull(unitResult); + Assert.Equal(resultTypes[i], unitResult.TestResult); + Assert.NotNull(unitResult.ResultInformation); + + switch (resultTypes[i]) + { + case ConfigurationTestResult.Positive: + case ConfigurationTestResult.Negative: + case ConfigurationTestResult.NotRun: + Assert.Null(unitResult.ResultInformation.ResultCode); + Assert.Empty(unitResult.ResultInformation.Description); + break; + case ConfigurationTestResult.Failed: + Assert.NotNull(unitResult.ResultInformation.ResultCode); + Assert.IsType<NullReferenceException>(unitResult.ResultInformation.ResultCode); + Assert.Equal(failedResult.ResultInformation.Description, unitResult.ResultInformation.Description); + break; + } + } + } + } +} diff --git a/src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationSetAuthoringTests.cs b/src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationSetAuthoringTests.cs @@ -0,0 +1,111 @@ +// ----------------------------------------------------------------------------- +// <copyright file="ConfigurationSetAuthoringTests.cs" company="Microsoft Corporation"> +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// </copyright> +// ----------------------------------------------------------------------------- + +namespace Microsoft.Management.Configuration.UnitTests.Tests +{ + using System; + using Microsoft.Management.Configuration.UnitTests.Fixtures; + using Microsoft.VisualBasic; + using Xunit; + using Xunit.Abstractions; + + /// <summary> + /// Unit tests for configuration set authoring (creating objects). + /// </summary> + [Collection("UnitTestCollection")] + public class ConfigurationSetAuthoringTests + { + private readonly UnitTestFixture fixture; + private readonly ITestOutputHelper log; + + /// <summary> + /// Initializes a new instance of the <see cref="ConfigurationSetAuthoringTests"/> class. + /// </summary> + /// <param name="fixture">Unit test fixture.</param> + /// <param name="log">Log helper.</param> + public ConfigurationSetAuthoringTests(UnitTestFixture fixture, ITestOutputHelper log) + { + this.fixture = fixture; + this.log = log; + } + + /// <summary> + /// Creates a configuration set and sets all available properties. + /// </summary> + [Fact] + public void ConfigurationSetAndProperties() + { + string testName = "Test Name"; + string testOrigin = "Test Origin"; + string testPath = "TestPath.ext"; + + ConfigurationSet testSet = new ConfigurationSet(); + + testSet.Name = testName; + Assert.Equal(testName, testSet.Name); + testSet.Origin = testOrigin; + Assert.Equal(testOrigin, testSet.Origin); + testSet.Path = testPath; + Assert.Equal(testPath, testSet.Path); + + Assert.NotEqual(Guid.Empty, testSet.InstanceIdentifier); + Assert.Equal(ConfigurationSetState.Unknown, testSet.State); + + Assert.Empty(testSet.ConfigurationUnits); + testSet.ConfigurationUnits = new ConfigurationUnit[] { new ConfigurationUnit() }; + Assert.Equal(1, testSet.ConfigurationUnits.Count); + } + + /// <summary> + /// Creates a configuration unit and sets all available properties. + /// </summary> + [Fact] + public void ConfigurationUnitAndProperties() + { + string testName = "Test Name"; + string testIdentifier = "Test Identifier"; + ConfigurationUnitIntent testIntent = ConfigurationUnitIntent.Assert; + + ConfigurationUnit testUnit = new ConfigurationUnit(); + + testUnit.UnitName = testName; + Assert.Equal(testName, testUnit.UnitName); + testUnit.Identifier = testIdentifier; + Assert.Equal(testIdentifier, testUnit.Identifier); + + Assert.NotEqual(Guid.Empty, testUnit.InstanceIdentifier); + + Assert.Equal(ConfigurationUnitIntent.Apply, testUnit.Intent); + testUnit.Intent = testIntent; + Assert.Equal(testIntent, testUnit.Intent); + + Assert.Empty(testUnit.Dependencies); + testUnit.Dependencies = new string[] { "dependency1", "dependency2" }; + Assert.Equal(2, testUnit.Dependencies.Count); + + Assert.Empty(testUnit.Directives); + Assert.Empty(testUnit.Settings); + Assert.Null(testUnit.Details); + + Assert.Equal(ConfigurationUnitState.Unknown, testUnit.State); + + Assert.Null(testUnit.ResultInformation); + + Assert.True(testUnit.ShouldApply); + testUnit.ShouldApply = false; + Assert.False(testUnit.ShouldApply); + } + + /// <summary> + /// This test is to ensure that real tests are added when Serialize is implemented. + /// </summary> + [Fact] + public void ConfigurationSetSerializeNotImplemented() + { + Assert.Throws<NotImplementedException>(() => new ConfigurationSet().Serialize(null)); + } + } +} diff --git a/src/Microsoft.Management.Configuration.UnitTests/Tests/OpenConfigurationSetTests.cs b/src/Microsoft.Management.Configuration.UnitTests/Tests/OpenConfigurationSetTests.cs @@ -0,0 +1,283 @@ +// ----------------------------------------------------------------------------- +// <copyright file="OpenConfigurationSetTests.cs" company="Microsoft Corporation"> +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// </copyright> +// ----------------------------------------------------------------------------- + +namespace Microsoft.Management.Configuration.UnitTests.Tests +{ + using System; + using System.Linq; + using System.Runtime.InteropServices; + using Microsoft.Management.Configuration.UnitTests.Fixtures; + using Microsoft.Management.Configuration.UnitTests.Helpers; + using Microsoft.VisualBasic; + using Xunit; + using Xunit.Abstractions; + + /// <summary> + /// Unit tests for parsing configuration sets from streams. + /// </summary> + [Collection("UnitTestCollection")] + public class OpenConfigurationSetTests : ConfigurationProcessorTestBase + { + /// <summary> + /// Initializes a new instance of the <see cref="OpenConfigurationSetTests"/> class. + /// </summary> + /// <param name="fixture">Unit test fixture.</param> + /// <param name="log">Log helper.</param> + public OpenConfigurationSetTests(UnitTestFixture fixture, ITestOutputHelper log) + : base(fixture, log) + { + } + + /// <summary> + /// Passes a null stream as input. + /// </summary> + [Fact] + public void NullStream() + { + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); + + OpenConfigurationSetResult result = processor.OpenConfigurationSet(null); + Assert.Null(result.Set); + Assert.IsType<NullReferenceException>(result.ResultCode); + Assert.Equal(string.Empty, result.Field); + } + + /// <summary> + /// Passes an empty stream as input. + /// </summary> + [Fact] + public void EmptyStream() + { + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); + + OpenConfigurationSetResult result = processor.OpenConfigurationSet(this.CreateStream(string.Empty)); + Assert.Null(result.Set); + Assert.NotNull(result.ResultCode); + Assert.Equal(Errors.WINGET_CONFIG_ERROR_INVALID_YAML, result.ResultCode.HResult); + Assert.Equal(string.Empty, result.Field); + } + + /// <summary> + /// Passes a stream with a single null byte in it. + /// </summary> + [Fact] + public void NullByteStream() + { + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); + + OpenConfigurationSetResult result = processor.OpenConfigurationSet(this.CreateStream("\0")); + Assert.Null(result.Set); + Assert.NotNull(result.ResultCode); + Assert.Equal(Errors.WINGET_CONFIG_ERROR_INVALID_YAML, result.ResultCode.HResult); + Assert.Equal(string.Empty, result.Field); + } + + /// <summary> + /// Passes YAML, but it isn't anything like a configuration file. + /// </summary> + [Fact] + public void NotConfigYAML() + { + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); + + OpenConfigurationSetResult result = processor.OpenConfigurationSet(this.CreateStream("yaml: yep")); + Assert.Null(result.Set); + Assert.NotNull(result.ResultCode); + Assert.Equal(Errors.WINGET_CONFIG_ERROR_INVALID_FIELD, result.ResultCode.HResult); + Assert.NotEqual(string.Empty, result.Field); + } + + /// <summary> + /// Passes YAML without a schema version. + /// </summary> + [Fact] + public void NoConfigVersion() + { + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); + + OpenConfigurationSetResult result = processor.OpenConfigurationSet(this.CreateStream(@" +properties: + thing: 1 +")); + Assert.Null(result.Set); + Assert.NotNull(result.ResultCode); + Assert.Equal(Errors.WINGET_CONFIG_ERROR_INVALID_FIELD, result.ResultCode.HResult); + Assert.NotEqual(string.Empty, result.Field); + } + + /// <summary> + /// Passes YAML that appears to be from the distant future. + /// </summary> + [Fact] + public void UnknownConfigVersion() + { + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); + + OpenConfigurationSetResult result = processor.OpenConfigurationSet(this.CreateStream(@" +properties: + configurationVersion: 99999999 +")); + Assert.Null(result.Set); + Assert.NotNull(result.ResultCode); + Assert.Equal(Errors.WINGET_CONFIG_ERROR_UNKNOWN_CONFIGURATION_FILE_VERSION, result.ResultCode.HResult); + Assert.Equal(string.Empty, result.Field); + } + + /// <summary> + /// Has one of each type of intent to ensure that it is set properly. + /// </summary> + [Fact] + public void EnsureIntent() + { + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); + + OpenConfigurationSetResult result = processor.OpenConfigurationSet(this.CreateStream(@" +properties: + configurationVersion: 0.1 + assertions: + - resource: Assert + parameters: + - resource: Inform + resources: + - resource: Apply +")); + + Assert.NotNull(result.Set); + Assert.Null(result.ResultCode); + Assert.Equal(string.Empty, result.Field); + + var units = result.Set.ConfigurationUnits; + Assert.Equal(3, units.Count); + bool sawAssert = false; + bool sawInform = false; + bool sawApply = false; + + foreach (var unit in units) + { + Assert.Equal(unit.UnitName, unit.Intent.ToString()); + switch (unit.Intent) + { + case ConfigurationUnitIntent.Assert: sawAssert = true; break; + case ConfigurationUnitIntent.Inform: sawInform = true; break; + case ConfigurationUnitIntent.Apply: sawApply = true; break; + default: Assert.Fail("Unknown intent"); break; + } + } + + Assert.True(sawAssert); + Assert.True(sawInform); + Assert.True(sawApply); + } + + /// <summary> + /// Passes YAML with resources being something other than a sequence. + /// </summary> + [Fact] + public void NonSequenceUnits() + { + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); + + OpenConfigurationSetResult result = processor.OpenConfigurationSet(this.CreateStream(@" +properties: + configurationVersion: 0.1 + resources: 1 +")); + Assert.Null(result.Set); + Assert.NotNull(result.ResultCode); + Assert.Equal(Errors.WINGET_CONFIG_ERROR_INVALID_FIELD, result.ResultCode.HResult); + Assert.NotEqual(string.Empty, result.Field); + } + + /// <summary> + /// Passes YAML with a resource being something other than a map. + /// </summary> + [Fact] + public void NonMapSequenceUnits() + { + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); + + OpenConfigurationSetResult result = processor.OpenConfigurationSet(this.CreateStream(@" +properties: + configurationVersion: 0.1 + resources: + - string +")); + Assert.Null(result.Set); + Assert.NotNull(result.ResultCode); + Assert.Equal(Errors.WINGET_CONFIG_ERROR_INVALID_FIELD, result.ResultCode.HResult); + Assert.NotEqual(string.Empty, result.Field); + } + + /// <summary> + /// Passes YAML with all values present. + /// </summary> + [Fact] + public void CheckAllUnitProperties() + { + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(); + + OpenConfigurationSetResult result = processor.OpenConfigurationSet(this.CreateStream(@" +properties: + configurationVersion: 0.1 + resources: + - resource: Resource + id: Identifier + dependsOn: + - Dependency1 + - Dependency2 + directives: + Directive1: A + Directive2: B + settings: + Setting1: 1 + Setting2: 2 +")); + Assert.NotNull(result.Set); + Assert.Null(result.ResultCode); + Assert.Equal(string.Empty, result.Field); + + Assert.NotEqual(Guid.Empty, result.Set.InstanceIdentifier); + + var units = result.Set.ConfigurationUnits; + Assert.NotNull(units); + Assert.Equal(1, units.Count); + + ConfigurationUnit unit = units[0]; + Assert.NotNull(unit); + Assert.Equal("Resource", unit.UnitName); + Assert.NotEqual(Guid.Empty, unit.InstanceIdentifier); + Assert.Equal("Identifier", unit.Identifier); + Assert.Equal(ConfigurationUnitIntent.Apply, unit.Intent); + + var dependencies = unit.Dependencies; + Assert.NotNull(dependencies); + Assert.Equal(2, dependencies.Count); + Assert.Contains("Dependency1", dependencies); + Assert.Contains("Dependency2", dependencies); + + var directives = unit.Directives; + Assert.NotNull(directives); + Assert.Equal(2, directives.Count); + Assert.Contains("Directive1", directives); + Assert.Equal("A", directives["Directive1"]); + Assert.Contains("Directive2", directives); + Assert.Equal("B", directives["Directive2"]); + + var settings = unit.Settings; + Assert.NotNull(settings); + Assert.Equal(2, settings.Count); + Assert.Contains("Setting1", settings); + Assert.Equal("1", settings["Setting1"]); + Assert.Contains("Setting2", settings); + Assert.Equal("2", settings["Setting2"]); + + Assert.Null(unit.Details); + Assert.Equal(ConfigurationUnitState.Unknown, unit.State); + Assert.Null(unit.ResultInformation); + Assert.True(unit.ShouldApply); + } + } +} diff --git a/src/Microsoft.Management.Configuration.UnitTests/Tests/ProcessorGetTests.cs b/src/Microsoft.Management.Configuration.UnitTests/Tests/ProcessorGetTests.cs @@ -0,0 +1,189 @@ +// ----------------------------------------------------------------------------- +// <copyright file="ProcessorGetTests.cs" company="Microsoft Corporation"> +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// </copyright> +// ----------------------------------------------------------------------------- + +namespace Microsoft.Management.Configuration.UnitTests.Tests +{ + using System.IO; + using System.Runtime.InteropServices; + using Microsoft.Management.Configuration.UnitTests.Fixtures; + using Microsoft.Management.Configuration.UnitTests.Helpers; + using Microsoft.VisualBasic; + using Xunit; + using Xunit.Abstractions; + + /// <summary> + /// Unit tests for getting details on processors. + /// </summary> + [Collection("UnitTestCollection")] + public class ProcessorGetTests : ConfigurationProcessorTestBase + { + /// <summary> + /// Initializes a new instance of the <see cref="ProcessorGetTests"/> class. + /// </summary> + /// <param name="fixture">Unit test fixture.</param> + /// <param name="log">Log helper.</param> + public ProcessorGetTests(UnitTestFixture fixture, ITestOutputHelper log) + : base(fixture, log) + { + } + + /// <summary> + /// Getting unit details throws an error. + /// </summary> + [Fact] + public void GetUnitDetailsError() + { + ConfigurationUnit configurationUnitThrows = new ConfigurationUnit(); + + TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); + factory.NullProcessor = new TestConfigurationSetProcessor(null); + factory.NullProcessor.Exceptions.Add(configurationUnitThrows, new FileNotFoundException()); + + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); + + Assert.Throws<FileNotFoundException>(() => processor.GetUnitDetails(configurationUnitThrows, ConfigurationUnitDetailLevel.Local)); + } + + /// <summary> + /// Getting unit details retrieves a value. + /// </summary> + [Fact] + public void GetUnitDetailsSuccess() + { + ConfigurationUnit configurationUnit = new ConfigurationUnit(); + TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); + + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); + + Assert.Null(configurationUnit.Details); + processor.GetUnitDetails(configurationUnit, ConfigurationUnitDetailLevel.Local); + Assert.NotNull(configurationUnit.Details); + } + + /// <summary> + /// Getting set details throws an error. + /// </summary> + [Fact] + public void GetSetDetailsError() + { + ConfigurationSet configurationSet = new ConfigurationSet(); + ConfigurationUnit configurationUnitWorks = new ConfigurationUnit(); + ConfigurationUnit configurationUnitThrows = new ConfigurationUnit(); + configurationSet.ConfigurationUnits = new ConfigurationUnit[] { configurationUnitWorks, configurationUnitThrows }; + + TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); + TestConfigurationSetProcessor setProcessor = factory.CreateTestProcessor(configurationSet); + setProcessor.Exceptions.Add(configurationUnitThrows, new InvalidDataException()); + + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); + + Assert.Throws<InvalidDataException>(() => processor.GetSetDetails(configurationSet, ConfigurationUnitDetailLevel.Local)); + Assert.NotNull(configurationUnitWorks.Details); + } + + /// <summary> + /// Getting set details retrieves all values. + /// </summary> + [Fact] + public void GetSetDetailsSuccess() + { + ConfigurationSet configurationSet = new ConfigurationSet(); + ConfigurationUnit configurationUnit1 = new ConfigurationUnit(); + ConfigurationUnit configurationUnit2 = new ConfigurationUnit(); + configurationSet.ConfigurationUnits = new ConfigurationUnit[] { configurationUnit1, configurationUnit2 }; + + TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); + + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); + + processor.GetSetDetails(configurationSet, ConfigurationUnitDetailLevel.Local); + Assert.NotNull(configurationUnit1.Details); + Assert.NotNull(configurationUnit2.Details); + } + + /// <summary> + /// The unit settings processor returns an error HRESULT. + /// </summary> + [Fact] + public void GetSettings_ProcessorSettingsError() + { + ConfigurationUnit configurationUnit = new ConfigurationUnit(); + + TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); + factory.NullProcessor = new TestConfigurationSetProcessor(null); + TestConfigurationUnitProcessor unitProcessor = factory.NullProcessor.CreateTestProcessor(configurationUnit); + unitProcessor.GetSettingsDelegate = () => throw new FileNotFoundException(); + + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); + + GetConfigurationUnitSettingsResult result = processor.GetUnitSettings(configurationUnit); + + Assert.NotNull(result); + Assert.NotNull(result.Settings); + Assert.NotNull(result.ResultInformation); + Assert.NotNull(result.ResultInformation.ResultCode); + Assert.IsType<FileNotFoundException>(result.ResultInformation.ResultCode); + } + + /// <summary> + /// The unit settings processor returns an error in it's result. + /// </summary> + [Fact] + public void GetSettings_ProcessorSettingsFailedResult() + { + ConfigurationUnit configurationUnit = new ConfigurationUnit(); + + TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); + factory.NullProcessor = new TestConfigurationSetProcessor(null); + TestConfigurationUnitProcessor unitProcessor = factory.NullProcessor.CreateTestProcessor(configurationUnit); + GetSettingsResult getSettingsResult = new GetSettingsResult(); + getSettingsResult.ResultInformation.ResultCode = new InvalidDataException(); + getSettingsResult.ResultInformation.Description = "We fail because we must"; + unitProcessor.GetSettingsDelegate = () => getSettingsResult; + + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); + + GetConfigurationUnitSettingsResult result = processor.GetUnitSettings(configurationUnit); + + Assert.NotNull(result); + Assert.Null(result.Settings); + Assert.NotNull(result.ResultInformation); + Assert.Equal(getSettingsResult.ResultInformation.ResultCode.HResult, result.ResultInformation.ResultCode.HResult); + Assert.Equal(getSettingsResult.ResultInformation.Description, result.ResultInformation.Description); + } + + /// <summary> + /// The unit settings processor returns good settings. + /// </summary> + [Fact] + public void GetSettings_ProcessorSettingsSuccess() + { + ConfigurationUnit configurationUnit = new ConfigurationUnit(); + + TestConfigurationProcessorFactory factory = new TestConfigurationProcessorFactory(); + factory.NullProcessor = new TestConfigurationSetProcessor(null); + TestConfigurationUnitProcessor unitProcessor = factory.NullProcessor.CreateTestProcessor(configurationUnit); + GetSettingsResult getSettingsResult = new GetSettingsResult(); + getSettingsResult.Settings = new Windows.Foundation.Collections.ValueSet(); + getSettingsResult.Settings.Add("key", "value"); + unitProcessor.GetSettingsDelegate = () => getSettingsResult; + + ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics(factory); + + GetConfigurationUnitSettingsResult result = processor.GetUnitSettings(configurationUnit); + + Assert.NotNull(result); + Assert.NotNull(result.ResultInformation); + Assert.Null(result.ResultInformation.ResultCode); + Assert.Empty(result.ResultInformation.Description); + Assert.NotNull(result.Settings); + Assert.NotEmpty(result.Settings); + Assert.Contains("key", result.Settings); + Assert.IsType<string>(result.Settings["key"]); + Assert.Equal("value", (string)result.Settings["key"]); + } + } +} diff --git a/src/Microsoft.Management.Configuration/ApplyConfigurationSetResult.cpp b/src/Microsoft.Management.Configuration/ApplyConfigurationSetResult.cpp @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "ApplyConfigurationSetResult.h" +#include "ApplyConfigurationSetResult.g.cpp" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + ApplyConfigurationSetResult::ApplyConfigurationSetResult() : + m_unitResults(single_threaded_vector<ApplyConfigurationUnitResult>()) + {} + + Windows::Foundation::Collections::IVectorView<ApplyConfigurationUnitResult> ApplyConfigurationSetResult::UnitResults() + { + return m_unitResults.GetView(); + } + + const Windows::Foundation::Collections::IVector<ApplyConfigurationUnitResult>& ApplyConfigurationSetResult::UnitResultsVector() + { + return m_unitResults; + } + + hresult ApplyConfigurationSetResult::ResultCode() + { + return m_resultCode; + } + + void ApplyConfigurationSetResult::ResultCode(hresult value) + { + m_resultCode = value; + } +} diff --git a/src/Microsoft.Management.Configuration/ApplyConfigurationSetResult.h b/src/Microsoft.Management.Configuration/ApplyConfigurationSetResult.h @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "ApplyConfigurationSetResult.g.h" +#include <winrt/Windows.Foundation.Collections.h> + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + struct ApplyConfigurationSetResult : ApplyConfigurationSetResultT<ApplyConfigurationSetResult> + { + using ApplyConfigurationUnitResult = Configuration::ApplyConfigurationUnitResult; + + ApplyConfigurationSetResult(); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + const Windows::Foundation::Collections::IVector<ApplyConfigurationUnitResult>& UnitResultsVector(); + void ResultCode(hresult value); +#endif + + Windows::Foundation::Collections::IVectorView<ApplyConfigurationUnitResult> UnitResults(); + hresult ResultCode(); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + private: + Windows::Foundation::Collections::IVector<ApplyConfigurationUnitResult> m_unitResults; + hresult m_resultCode; +#endif + }; +} diff --git a/src/Microsoft.Management.Configuration/ApplyConfigurationUnitResult.cpp b/src/Microsoft.Management.Configuration/ApplyConfigurationUnitResult.cpp @@ -0,0 +1,59 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "ApplyConfigurationUnitResult.h" +#include "ApplyConfigurationUnitResult.g.cpp" +#include "ConfigurationUnitResultInformation.h" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + ConfigurationUnit ApplyConfigurationUnitResult::Unit() + { + return m_unit; + } + + void ApplyConfigurationUnitResult::Unit(ConfigurationUnit value) + { + m_unit = std::move(value); + } + + ConfigurationUnitState ApplyConfigurationUnitResult::State() const + { + return m_state.load(); + } + + void ApplyConfigurationUnitResult::State(ConfigurationUnitState value) + { + m_state.store(value); + } + + bool ApplyConfigurationUnitResult::PreviouslyInDesiredState() const + { + return m_previouslyInDesiredState; + } + + void ApplyConfigurationUnitResult::PreviouslyInDesiredState(bool value) + { + m_previouslyInDesiredState = value; + } + + bool ApplyConfigurationUnitResult::RebootRequired() const + { + return m_rebootRequired; + } + + void ApplyConfigurationUnitResult::RebootRequired(bool value) + { + m_rebootRequired = value; + } + + Configuration::ConfigurationUnitResultInformation ApplyConfigurationUnitResult::ResultInformation() + { + return m_resultInformation; + } + + void ApplyConfigurationUnitResult::ResultInformation(ConfigurationUnitResultInformation value) + { + m_resultInformation = std::move(value); + } +} diff --git a/src/Microsoft.Management.Configuration/ApplyConfigurationUnitResult.h b/src/Microsoft.Management.Configuration/ApplyConfigurationUnitResult.h @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "ApplyConfigurationUnitResult.g.h" +#include <atomic> + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + struct ApplyConfigurationUnitResult : ApplyConfigurationUnitResultT<ApplyConfigurationUnitResult> + { + using ConfigurationUnit = Configuration::ConfigurationUnit; + using ConfigurationUnitResultInformation = Configuration::ConfigurationUnitResultInformation; + + ApplyConfigurationUnitResult() = default; + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + void Unit(ConfigurationUnit value); + void State(ConfigurationUnitState value); + void PreviouslyInDesiredState(bool value); + void RebootRequired(bool value); + void ResultInformation(ConfigurationUnitResultInformation value); +#endif + + ConfigurationUnit Unit(); + ConfigurationUnitState State() const; + bool PreviouslyInDesiredState() const; + bool RebootRequired() const; + ConfigurationUnitResultInformation ResultInformation(); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + private: + ConfigurationUnit m_unit = nullptr; + std::atomic<ConfigurationUnitState> m_state = ConfigurationUnitState::Pending; + bool m_previouslyInDesiredState = false; + bool m_rebootRequired = false; + ConfigurationUnitResultInformation m_resultInformation = nullptr; +#endif + }; +} diff --git a/src/Microsoft.Management.Configuration/ApplySettingsResult.cpp b/src/Microsoft.Management.Configuration/ApplySettingsResult.cpp @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "ApplySettingsResult.h" +#include "ApplySettingsResult.g.cpp" +#include "ConfigurationUnitResultInformation.h" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + ApplySettingsResult::ApplySettingsResult() : + m_resultInformation(*make_self<wil::details::module_count_wrapper<implementation::ConfigurationUnitResultInformation>>()) + { + } + + bool ApplySettingsResult::RebootRequired() const + { + return m_rebootRequired; + } + + void ApplySettingsResult::RebootRequired(bool value) + { + m_rebootRequired = value; + } + + Configuration::ConfigurationUnitResultInformation ApplySettingsResult::ResultInformation() + { + return m_resultInformation; + } +} diff --git a/src/Microsoft.Management.Configuration/ApplySettingsResult.h b/src/Microsoft.Management.Configuration/ApplySettingsResult.h @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "ApplySettingsResult.g.h" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + struct ApplySettingsResult : ApplySettingsResultT<ApplySettingsResult> + { + ApplySettingsResult(); + + bool RebootRequired() const; + void RebootRequired(bool value); + + Configuration::ConfigurationUnitResultInformation ResultInformation(); + + private: + bool m_rebootRequired = false; + Configuration::ConfigurationUnitResultInformation m_resultInformation; + }; +} + +namespace winrt::Microsoft::Management::Configuration::factory_implementation +{ + struct ApplySettingsResult : ApplySettingsResultT<ApplySettingsResult, implementation::ApplySettingsResult> + { + }; +} diff --git a/src/Microsoft.Management.Configuration/ConfigThreadGlobals.cpp b/src/Microsoft.Management.Configuration/ConfigThreadGlobals.cpp @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "ConfigThreadGlobals.h" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + AppInstaller::Logging::DiagnosticLogger& ConfigThreadGlobals::GetDiagnosticLogger() + { + return m_logger; + } + + AppInstaller::Logging::TelemetryTraceLogger& ConfigThreadGlobals::GetTelemetryLogger() + { + THROW_HR(E_NOTIMPL); + } +} diff --git a/src/Microsoft.Management.Configuration/ConfigThreadGlobals.h b/src/Microsoft.Management.Configuration/ConfigThreadGlobals.h @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include <winget/SharedThreadGlobals.h> + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + // Interface for access to values that are stored on a per-thread object. + struct ConfigThreadGlobals : public AppInstaller::ThreadLocalStorage::ThreadGlobals + { + ConfigThreadGlobals() = default; + virtual ~ConfigThreadGlobals() = default; + + AppInstaller::Logging::DiagnosticLogger& GetDiagnosticLogger() override; + + AppInstaller::Logging::TelemetryTraceLogger& GetTelemetryLogger() override; + + protected: + AppInstaller::Logging::DiagnosticLogger m_logger; + }; +} diff --git a/src/Microsoft.Management.Configuration/ConfigurationChangeData.cpp b/src/Microsoft.Management.Configuration/ConfigurationChangeData.cpp @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "ConfigurationChangeData.h" +#include "ConfigurationChangeData.g.cpp" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + void ConfigurationChangeData::Initialize(ConfigurationChangeEventType change, guid instanceIdentifier, ConfigurationSetState state) + { + m_change = change; + m_instanceIdentifier = instanceIdentifier; + m_state = state; + } + + ConfigurationChangeEventType ConfigurationChangeData::Change() + { + return m_change; + } + + guid ConfigurationChangeData::InstanceIdentifier() + { + return m_instanceIdentifier; + } + + ConfigurationSetState ConfigurationChangeData::State() + { + return m_state; + } +} diff --git a/src/Microsoft.Management.Configuration/ConfigurationChangeData.h b/src/Microsoft.Management.Configuration/ConfigurationChangeData.h @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "ConfigurationChangeData.g.h" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + struct ConfigurationChangeData : ConfigurationChangeDataT<ConfigurationChangeData> + { + ConfigurationChangeData() = default; + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + void Initialize(ConfigurationChangeEventType change, guid instanceIdentifier, ConfigurationSetState state); +#endif + + ConfigurationChangeEventType Change(); + guid InstanceIdentifier(); + ConfigurationSetState State(); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + private: + ConfigurationChangeEventType m_change{}; + guid m_instanceIdentifier; + ConfigurationSetState m_state{}; +#endif + }; +} diff --git a/src/Microsoft.Management.Configuration/ConfigurationConflict.cpp b/src/Microsoft.Management.Configuration/ConfigurationConflict.cpp @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "ConfigurationConflict.h" +#include "ConfigurationConflict.g.cpp" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + void ConfigurationConflict::Initialize(ConfigurationConflictType conflict) + { + m_conflict = conflict; + } + + ConfigurationConflictType ConfigurationConflict::Conflict() + { + return m_conflict; + } + + ConfigurationSet ConfigurationConflict::FirstSet() + { + return m_firstSet; + } + + ConfigurationSet ConfigurationConflict::SecondSet() + { + return m_secondSet; + } + + ConfigurationUnit ConfigurationConflict::FirstUnit() + { + return m_firstUnit; + } + + ConfigurationUnit ConfigurationConflict::SecondUnit() + { + return m_secondUnit; + } + + Windows::Foundation::Collections::IVectorView<ConfigurationConflictSetting> ConfigurationConflict::Settings() + { + if (m_settings) + { + return m_settings.GetView(); + } + else + { + return nullptr; + } + } +} diff --git a/src/Microsoft.Management.Configuration/ConfigurationConflict.h b/src/Microsoft.Management.Configuration/ConfigurationConflict.h @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "ConfigurationConflict.g.h" +#include <winrt/Windows.Foundation.Collections.h> + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + struct ConfigurationConflict : ConfigurationConflictT<ConfigurationConflict> + { + ConfigurationConflict() = default; + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + void Initialize(ConfigurationConflictType conflict); +#endif + + ConfigurationConflictType Conflict(); + ConfigurationSet FirstSet(); + ConfigurationSet SecondSet(); + ConfigurationUnit FirstUnit(); + ConfigurationUnit SecondUnit(); + Windows::Foundation::Collections::IVectorView<ConfigurationConflictSetting> Settings(); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + private: + ConfigurationConflictType m_conflict{}; + ConfigurationSet m_firstSet = nullptr; + ConfigurationSet m_secondSet = nullptr; + ConfigurationUnit m_firstUnit = nullptr; + ConfigurationUnit m_secondUnit = nullptr; + Windows::Foundation::Collections::IVector<ConfigurationConflictSetting> m_settings = nullptr; +#endif + }; +} diff --git a/src/Microsoft.Management.Configuration/ConfigurationConflictSetting.cpp b/src/Microsoft.Management.Configuration/ConfigurationConflictSetting.cpp @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "ConfigurationConflictSetting.h" +#include "ConfigurationConflictSetting.g.cpp" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + void ConfigurationConflictSetting::Initialize(std::wstring_view name, const IInspectable& firstValue, const IInspectable& secondValue) + { + m_name = name; + m_firstValue = firstValue; + m_secondValue = secondValue; + } + + hstring ConfigurationConflictSetting::Name() + { + return m_name; + } + + ConfigurationConflictSetting::IInspectable ConfigurationConflictSetting::FirstValue() + { + return m_firstValue; + } + + ConfigurationConflictSetting::IInspectable ConfigurationConflictSetting::SecondValue() + { + return m_secondValue; + } +} diff --git a/src/Microsoft.Management.Configuration/ConfigurationConflictSetting.h b/src/Microsoft.Management.Configuration/ConfigurationConflictSetting.h @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "ConfigurationConflictSetting.g.h" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + struct ConfigurationConflictSetting : ConfigurationConflictSettingT<ConfigurationConflictSetting> + { + ConfigurationConflictSetting() = default; + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + void Initialize(std::wstring_view name, const IInspectable& firstValue, const IInspectable& secondValue); +#endif + + hstring Name(); + IInspectable FirstValue(); + IInspectable SecondValue(); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + private: + hstring m_name; + IInspectable m_firstValue = nullptr; + IInspectable m_secondValue = nullptr; +#endif + }; +} diff --git a/src/Microsoft.Management.Configuration/ConfigurationProcessor.cpp b/src/Microsoft.Management.Configuration/ConfigurationProcessor.cpp @@ -0,0 +1,397 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "ConfigurationProcessor.h" +#include "ConfigurationProcessor.g.cpp" +#include "ConfigurationSet.h" +#include "OpenConfigurationSetResult.h" +#include "ConfigurationSetParser.h" +#include "DiagnosticInformation.h" +#include "ApplyConfigurationSetResult.h" +#include "ConfigurationSetApplyProcessor.h" +#include "TestConfigurationSetResult.h" +#include "TestConfigurationUnitResult.h" +#include "ConfigurationUnitResultInformation.h" +#include "GetConfigurationUnitSettingsResult.h" +#include "ExceptionResultHelpers.h" +#include "ConfigurationSetChangeData.h" + +#include <AppInstallerErrors.h> +#include <AppInstallerStrings.h> + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + namespace + { + // ILogger that sends data back to the Diagnostics event of the ConfigurationProcessor. + struct ConfigurationProcessorDiagnosticsLogger : public AppInstaller::Logging::ILogger + { + ConfigurationProcessorDiagnosticsLogger(ConfigurationProcessor& processor) : m_processor(processor) {} + + std::string GetName() const override + { + return "ConfigurationProcessorDiagnosticsLogger"; + } + + void Write(AppInstaller::Logging::Channel channel, AppInstaller::Logging::Level level, std::string_view message) noexcept override try + { + std::ostringstream strstr; + strstr << '[' << AppInstaller::Logging::GetChannelName(channel) << "] " << message; + m_processor.Diagnostics(ConvertLevel(level), strstr.str()); + } + catch (...) {} + + void WriteDirect(std::string_view message) noexcept override try + { + m_processor.Diagnostics(DiagnosticLevel::Informational, message); + } + catch (...) {} + + private: + DiagnosticLevel ConvertLevel(AppInstaller::Logging::Level level) + { + switch (level) + { + case AppInstaller::Logging::Level::Verbose: return DiagnosticLevel::Verbose; + case AppInstaller::Logging::Level::Info: return DiagnosticLevel::Informational; + case AppInstaller::Logging::Level::Warning: return DiagnosticLevel::Warning; + case AppInstaller::Logging::Level::Error: return DiagnosticLevel::Error; + case AppInstaller::Logging::Level::Crit: return DiagnosticLevel::Critical; + default: return DiagnosticLevel::Warning; + } + } + + ConfigurationProcessor& m_processor; + }; + + // Helper to ensure a one-time callback attach + struct AttachWilFailureCallback + { + AttachWilFailureCallback() + { + wil::SetResultLoggingCallback(wilResultLoggingCallback); + } + + ~AttachWilFailureCallback() = default; + + static void __stdcall wilResultLoggingCallback(const wil::FailureInfo& info) noexcept + { + AICLI_LOG(Fail, Error, << [&]() { + wchar_t message[2048]; + GetFailureLogString(message, ARRAYSIZE(message), info); + return AppInstaller::Utility::ConvertToUTF8(message); + }()); + } + }; + + // Specifies the set of intents that should execute during a Test request + bool ShouldTestDuringTest(ConfigurationUnitIntent intent) + { + return (intent == ConfigurationUnitIntent::Assert || intent == ConfigurationUnitIntent::Apply); + } + } + + ConfigurationProcessor::ConfigurationProcessor(const IConfigurationProcessorFactory& factory) : m_factory(factory) + { + AppInstaller::Logging::DiagnosticLogger& logger = m_threadGlobals.GetDiagnosticLogger(); + logger.EnableChannel(AppInstaller::Logging::Channel::All); + logger.SetLevel(AppInstaller::Logging::Level::Verbose); + logger.AddLogger(std::make_unique<ConfigurationProcessorDiagnosticsLogger>(*this)); + } + + event_token ConfigurationProcessor::Diagnostics(const Windows::Foundation::EventHandler<DiagnosticInformation>& handler) + { + static AttachWilFailureCallback s_callbackAttach; + return m_diagnostics.add(handler); + } + + void ConfigurationProcessor::Diagnostics(const event_token& token) noexcept + { + m_diagnostics.remove(token); + } + + event_token ConfigurationProcessor::ConfigurationChange(const Windows::Foundation::TypedEventHandler<ConfigurationSet, ConfigurationChangeData>& handler) + { + return m_configurationChange.add(handler); + } + + void ConfigurationProcessor::ConfigurationChange(const event_token& token) noexcept + { + m_configurationChange.remove(token); + } + + Windows::Foundation::Collections::IVector<Configuration::ConfigurationSet> ConfigurationProcessor::GetConfigurationHistory() + { + THROW_HR(E_NOTIMPL); + } + + Windows::Foundation::IAsyncOperation<Windows::Foundation::Collections::IVector<Configuration::ConfigurationSet>> ConfigurationProcessor::GetConfigurationHistoryAsync() + { + co_return GetConfigurationHistory(); + } + + Configuration::OpenConfigurationSetResult ConfigurationProcessor::OpenConfigurationSet(const Windows::Storage::Streams::IInputStream& stream) + { + return OpenConfigurationSetAsync(stream).get(); + } + + Windows::Foundation::IAsyncOperation<Configuration::OpenConfigurationSetResult> ConfigurationProcessor::OpenConfigurationSetAsync(const Windows::Storage::Streams::IInputStream& stream) + { + Windows::Storage::Streams::IInputStream localStream = stream; + co_await winrt::resume_background(); + + auto threadGlobals = m_threadGlobals.SetForCurrentThread(); + auto result = make_self<wil::details::module_count_wrapper<OpenConfigurationSetResult>>(); + + if (!stream) + { + result->Initialize(E_POINTER, {}); + co_return *result; + } + + try + { + std::unique_ptr<ConfigurationSetParser> parser = ConfigurationSetParser::Create(localStream); + if (FAILED(parser->Result())) + { + result->Initialize(parser->Result(), parser->Field()); + co_return *result; + } + + auto configurationSet = make_self<wil::details::module_count_wrapper<implementation::ConfigurationSet>>(); + configurationSet->Initialize(parser->GetConfigurationUnits()); + if (FAILED(parser->Result())) + { + result->Initialize(parser->Result(), parser->Field()); + co_return *result; + } + + result->Initialize(*configurationSet); + } + catch (const wil::ResultException& resultException) + { + result->Initialize(resultException.GetErrorCode()); + } + catch (...) + { + result->Initialize(WINGET_CONFIG_ERROR_INVALID_CONFIGURATION_FILE); + LOG_CAUGHT_EXCEPTION(); + } + + co_return *result; + } + + Windows::Foundation::Collections::IVector<ConfigurationConflict> ConfigurationProcessor::CheckForConflicts( + const Windows::Foundation::Collections::IVectorView<ConfigurationSet>& configurationSets, + bool includeConfigurationHistory) + { + UNREFERENCED_PARAMETER(configurationSets); + UNREFERENCED_PARAMETER(includeConfigurationHistory); + THROW_HR(E_NOTIMPL); + } + + Windows::Foundation::IAsyncOperation<Windows::Foundation::Collections::IVector<ConfigurationConflict>> ConfigurationProcessor::CheckForConflictsAsync( + const Windows::Foundation::Collections::IVectorView<ConfigurationSet>& configurationSets, + bool includeConfigurationHistory) + { + co_return CheckForConflicts(configurationSets, includeConfigurationHistory); + } + + void ConfigurationProcessor::GetSetDetails(const ConfigurationSet& configurationSet, ConfigurationUnitDetailLevel detailLevel) + { + return GetSetDetailsAsync(configurationSet, detailLevel).get(); + } + + Windows::Foundation::IAsyncAction ConfigurationProcessor::GetSetDetailsAsync(const ConfigurationSet& configurationSet, ConfigurationUnitDetailLevel detailLevel) + { + THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); + + ConfigurationSet localSet = configurationSet; + co_await winrt::resume_background(); + + auto threadGlobals = m_threadGlobals.SetForCurrentThread(); + + IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(localSet); + + for (const auto& unit : localSet.ConfigurationUnits()) + { + IConfigurationUnitProcessorDetails details = setProcessor.GetUnitProcessorDetails(unit, detailLevel); + get_self<implementation::ConfigurationUnit>(unit)->Details(std::move(details)); + } + } + + void ConfigurationProcessor::GetUnitDetails(const ConfigurationUnit& unit, ConfigurationUnitDetailLevel detailLevel) + { + return GetUnitDetailsAsync(unit, detailLevel).get(); + } + + Windows::Foundation::IAsyncAction ConfigurationProcessor::GetUnitDetailsAsync(const ConfigurationUnit& unit, ConfigurationUnitDetailLevel detailLevel) + { + THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); + + ConfigurationUnit localUnit = unit; + co_await winrt::resume_background(); + + auto threadGlobals = m_threadGlobals.SetForCurrentThread(); + + IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(nullptr); + IConfigurationUnitProcessorDetails details = setProcessor.GetUnitProcessorDetails(localUnit, detailLevel); + get_self<implementation::ConfigurationUnit>(localUnit)->Details(std::move(details)); + } + + Configuration::ApplyConfigurationSetResult ConfigurationProcessor::ApplySet(const ConfigurationSet& configurationSet, ApplyConfigurationSetFlags flags) + { + return ApplySetAsync(configurationSet, flags).get(); + } + + Windows::Foundation::IAsyncOperationWithProgress<Configuration::ApplyConfigurationSetResult, Configuration::ConfigurationSetChangeData> ConfigurationProcessor::ApplySetAsync(const ConfigurationSet& configurationSet, ApplyConfigurationSetFlags flags) + { + THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); + + ConfigurationSet localSet = configurationSet; + co_await winrt::resume_background(); + auto progress = co_await winrt::get_progress_token(); + + // TODO: Not needed until we have history implemented + UNREFERENCED_PARAMETER(flags); + + auto threadGlobals = m_threadGlobals.SetForCurrentThread(); + + auto result = make_self<wil::details::module_count_wrapper<implementation::ApplyConfigurationSetResult>>(); + ConfigurationSetApplyProcessor applyProcessor{ localSet, m_factory.CreateSetProcessor(localSet), result, progress }; + progress.set_result(*result); + + applyProcessor.Process(); + + co_return *result; + } + + Configuration::TestConfigurationSetResult ConfigurationProcessor::TestSet(const ConfigurationSet& configurationSet) + { + return TestSetAsync(configurationSet).get(); + } + + Windows::Foundation::IAsyncOperationWithProgress<Configuration::TestConfigurationSetResult, Configuration::TestConfigurationUnitResult> ConfigurationProcessor::TestSetAsync(const ConfigurationSet& configurationSet) + { + THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); + + ConfigurationSet localSet = configurationSet; + co_await winrt::resume_background(); + auto progress = co_await winrt::get_progress_token(); + + auto threadGlobals = m_threadGlobals.SetForCurrentThread(); + + IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(localSet); + auto result = make_self<wil::details::module_count_wrapper<implementation::TestConfigurationSetResult>>(); + result->TestResult(ConfigurationTestResult::NotRun); + progress.set_result(*result); + + for (const auto& unit : localSet.ConfigurationUnits()) + { + AICLI_LOG(Config, Info, << "Testing configuration unit: " << AppInstaller::Utility::ConvertToUTF8(unit.UnitName())); + + auto testResult = make_self<wil::details::module_count_wrapper<implementation::TestConfigurationUnitResult>>(); + auto unitResult = make_self<wil::details::module_count_wrapper<implementation::ConfigurationUnitResultInformation>>(); + testResult->Initialize(unit, *unitResult); + + if (ShouldTestDuringTest(unit.Intent())) + { + IConfigurationUnitProcessor unitProcessor; + + try + { + // TODO: Directives overlay to prevent running elevated for test + unitProcessor = setProcessor.CreateUnitProcessor(unit, {}); + } + catch (...) + { + ExtractUnitResultInformation(std::current_exception(), unitResult); + } + + if (unitProcessor) + { + try + { + TestSettingsResult settingsResult = unitProcessor.TestSettings(); + testResult->TestResult(settingsResult.TestResult()); + testResult->ResultInformation(settingsResult.ResultInformation()); + } + catch (...) + { + ExtractUnitResultInformation(std::current_exception(), unitResult); + } + } + } + else + { + testResult->TestResult(ConfigurationTestResult::NotRun); + } + + if (FAILED(unitResult->ResultCode())) + { + testResult->TestResult(ConfigurationTestResult::Failed); + } + + result->AppendUnitResult(*testResult); + + progress(*testResult); + } + + co_return *result; + } + + Configuration::GetConfigurationUnitSettingsResult ConfigurationProcessor::GetUnitSettings(const ConfigurationUnit& unit) + { + return GetUnitSettingsAsync(unit).get(); + } + + Windows::Foundation::IAsyncOperation<Configuration::GetConfigurationUnitSettingsResult> ConfigurationProcessor::GetUnitSettingsAsync(const ConfigurationUnit& unit) + { + THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); + + ConfigurationUnit localUnit = unit; + co_await winrt::resume_background(); + + auto threadGlobals = m_threadGlobals.SetForCurrentThread(); + + IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(nullptr); + auto result = make_self<wil::details::module_count_wrapper<implementation::GetConfigurationUnitSettingsResult>>(); + auto unitResult = make_self<wil::details::module_count_wrapper<implementation::ConfigurationUnitResultInformation>>(); + result->ResultInformation(*unitResult); + + IConfigurationUnitProcessor unitProcessor; + + try + { + // TODO: Directives overlay to prevent running elevated for get + unitProcessor = setProcessor.CreateUnitProcessor(localUnit, {}); + } + catch (...) + { + ExtractUnitResultInformation(std::current_exception(), unitResult); + } + + if (unitProcessor) + { + try + { + GetSettingsResult settingsResult = unitProcessor.GetSettings(); + result->Settings(settingsResult.Settings()); + result->ResultInformation(settingsResult.ResultInformation()); + } + catch (...) + { + ExtractUnitResultInformation(std::current_exception(), unitResult); + } + } + + co_return *result; + } + + void ConfigurationProcessor::Diagnostics(DiagnosticLevel level, std::string_view message) + { + auto diagnostics = make_self<wil::details::module_count_wrapper<implementation::DiagnosticInformation>>(); + diagnostics->Initialize(level, AppInstaller::Utility::ConvertToUTF16(message)); + m_diagnostics(*this, *diagnostics); + } +} diff --git a/src/Microsoft.Management.Configuration/ConfigurationProcessor.h b/src/Microsoft.Management.Configuration/ConfigurationProcessor.h @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "ConfigurationProcessor.g.h" +#include <winrt/Windows.Foundation.h> +#include <winrt/Windows.Foundation.Collections.h> +#include <winrt/Windows.Storage.Streams.h> +#include "ConfigThreadGlobals.h" + +#include <string_view> +#include <functional> + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + struct ConfigurationProcessor : ConfigurationProcessorT<ConfigurationProcessor> + { + using ConfigurationSet = Configuration::ConfigurationSet; + using ConfigurationSetChangeData = Configuration::ConfigurationSetChangeData; + using ConfigurationUnit = Configuration::ConfigurationUnit; + using DiagnosticInformation = Configuration::DiagnosticInformation; + using ApplyConfigurationSetResult = Configuration::ApplyConfigurationSetResult; + using TestConfigurationSetResult = Configuration::TestConfigurationSetResult; + using TestConfigurationUnitResult = Configuration::TestConfigurationUnitResult; + using GetConfigurationUnitSettingsResult = Configuration::GetConfigurationUnitSettingsResult; + + ConfigurationProcessor(const IConfigurationProcessorFactory& factory); + + event_token Diagnostics(const Windows::Foundation::EventHandler<DiagnosticInformation>& handler); + void Diagnostics(const event_token& token) noexcept; + + event_token ConfigurationChange(const Windows::Foundation::TypedEventHandler<ConfigurationSet, ConfigurationChangeData>& handler); + void ConfigurationChange(const event_token& token) noexcept; + + Windows::Foundation::Collections::IVector<ConfigurationSet> GetConfigurationHistory(); + Windows::Foundation::IAsyncOperation<Windows::Foundation::Collections::IVector<ConfigurationSet>> GetConfigurationHistoryAsync(); + + Configuration::OpenConfigurationSetResult OpenConfigurationSet(const Windows::Storage::Streams::IInputStream& stream); + Windows::Foundation::IAsyncOperation<Configuration::OpenConfigurationSetResult> OpenConfigurationSetAsync(const Windows::Storage::Streams::IInputStream& stream); + + Windows::Foundation::Collections::IVector<ConfigurationConflict> CheckForConflicts( + const Windows::Foundation::Collections::IVectorView<ConfigurationSet>& configurationSets, + bool includeConfigurationHistory); + Windows::Foundation::IAsyncOperation<Windows::Foundation::Collections::IVector<ConfigurationConflict>> CheckForConflictsAsync( + const Windows::Foundation::Collections::IVectorView<ConfigurationSet>& configurationSets, + bool includeConfigurationHistory); + + void GetSetDetails(const ConfigurationSet& configurationSet, ConfigurationUnitDetailLevel detailLevel); + Windows::Foundation::IAsyncAction GetSetDetailsAsync(const ConfigurationSet& configurationSet, ConfigurationUnitDetailLevel detailLevel); + + void GetUnitDetails(const ConfigurationUnit& unit, ConfigurationUnitDetailLevel detailLevel); + Windows::Foundation::IAsyncAction GetUnitDetailsAsync(const ConfigurationUnit& unit, ConfigurationUnitDetailLevel detailLevel); + + ApplyConfigurationSetResult ApplySet(const ConfigurationSet& configurationSet, ApplyConfigurationSetFlags flags); + Windows::Foundation::IAsyncOperationWithProgress<ApplyConfigurationSetResult, ConfigurationSetChangeData> ApplySetAsync(const ConfigurationSet& configurationSet, ApplyConfigurationSetFlags flags); + + TestConfigurationSetResult TestSet(const ConfigurationSet& configurationSet); + Windows::Foundation::IAsyncOperationWithProgress<TestConfigurationSetResult, TestConfigurationUnitResult> TestSetAsync(const ConfigurationSet& configurationSet); + + GetConfigurationUnitSettingsResult GetUnitSettings(const ConfigurationUnit& unit); + Windows::Foundation::IAsyncOperation<GetConfigurationUnitSettingsResult> GetUnitSettingsAsync(const ConfigurationUnit& unit); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + // Sends diagnostics objects to the event. + void Diagnostics(DiagnosticLevel level, std::string_view message); + + private: + IConfigurationProcessorFactory m_factory = nullptr; + event<Windows::Foundation::EventHandler<DiagnosticInformation>> m_diagnostics; + event<Windows::Foundation::TypedEventHandler<ConfigurationSet, ConfigurationChangeData>> m_configurationChange; + ConfigThreadGlobals m_threadGlobals; +#endif + }; +} + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) +namespace winrt::Microsoft::Management::Configuration::factory_implementation +{ + struct ConfigurationProcessor : ConfigurationProcessorT<ConfigurationProcessor, implementation::ConfigurationProcessor> + { + }; +} +#endif diff --git a/src/Microsoft.Management.Configuration/ConfigurationSet.cpp b/src/Microsoft.Management.Configuration/ConfigurationSet.cpp @@ -0,0 +1,118 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "ConfigurationSet.h" +#include "ConfigurationSet.g.cpp" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + ConfigurationSet::ConfigurationSet() + { + GUID instanceIdentifier; + THROW_IF_FAILED(CoCreateGuid(&instanceIdentifier)); + m_instanceIdentifier = instanceIdentifier; + } + + ConfigurationSet::ConfigurationSet(const guid& instanceIdentifier) : + m_instanceIdentifier(instanceIdentifier), m_mutableFlag(false) + { + } + + void ConfigurationSet::Initialize(std::vector<Configuration::ConfigurationUnit>&& units) + { + m_configurationUnits = winrt::single_threaded_vector<Configuration::ConfigurationUnit>(std::move(units)); + } + + hstring ConfigurationSet::Name() + { + return m_name; + } + + void ConfigurationSet::Name(const hstring& value) + { + m_mutableFlag.RequireMutable(); + m_name = value; + } + + hstring ConfigurationSet::Origin() + { + return m_origin; + } + + void ConfigurationSet::Origin(const hstring& value) + { + m_mutableFlag.RequireMutable(); + m_origin = value; + } + + hstring ConfigurationSet::Path() + { + return m_path; + } + + void ConfigurationSet::Path(const hstring& value) + { + m_mutableFlag.RequireMutable(); + m_path = value; + } + + guid ConfigurationSet::InstanceIdentifier() + { + return m_instanceIdentifier; + } + + ConfigurationSetState ConfigurationSet::State() + { + return ConfigurationSetState::Unknown; + } + + clock::time_point ConfigurationSet::FirstApply() + { + return m_firstApply; + } + + clock::time_point ConfigurationSet::ApplyBegun() + { + return clock::time_point{}; + } + + clock::time_point ConfigurationSet::ApplyEnded() + { + return clock::time_point{}; + } + + Windows::Foundation::Collections::IVectorView<Configuration::ConfigurationUnit> ConfigurationSet::ConfigurationUnits() + { + return m_configurationUnits.GetView(); + } + + void ConfigurationSet::ConfigurationUnits(const Windows::Foundation::Collections::IVectorView<ConfigurationUnit>& value) + { + m_mutableFlag.RequireMutable(); + + std::vector<ConfigurationUnit> temp{ value.Size() }; + value.GetMany(0, temp); + m_configurationUnits = winrt::single_threaded_vector<ConfigurationUnit>(std::move(temp)); + } + + event_token ConfigurationSet::ConfigurationSetChange(const Windows::Foundation::TypedEventHandler<WinRT_Self, ConfigurationSetChangeData>& handler) + { + return m_configurationSetChange.add(handler); + } + + void ConfigurationSet::ConfigurationSetChange(const event_token& token) noexcept + { + m_configurationSetChange.remove(token); + } + + void ConfigurationSet::Serialize(const Windows::Storage::Streams::IOutputStream& stream) + { + UNREFERENCED_PARAMETER(stream); + THROW_HR(E_NOTIMPL); + } + + void ConfigurationSet::Remove() + { + THROW_HR(E_NOTIMPL); + } +} diff --git a/src/Microsoft.Management.Configuration/ConfigurationSet.h b/src/Microsoft.Management.Configuration/ConfigurationSet.h @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "ConfigurationSet.g.h" +#include "MutableFlag.h" +#include <winrt/Windows.Foundation.h> +#include <winrt/Windows.Foundation.Collections.h> +#include <vector> + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + struct ConfigurationSet : ConfigurationSetT<ConfigurationSet> + { + using WinRT_Self = ::winrt::Microsoft::Management::Configuration::ConfigurationSet; + using ConfigurationUnit = ::winrt::Microsoft::Management::Configuration::ConfigurationUnit; + + ConfigurationSet(); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + ConfigurationSet(const guid& instanceIdentifier); + void Initialize(std::vector<Configuration::ConfigurationUnit>&& units); +#endif + + hstring Name(); + void Name(const hstring& value); + + hstring Origin(); + void Origin(const hstring& value); + + hstring Path(); + void Path(const hstring& value); + + guid InstanceIdentifier(); + ConfigurationSetState State(); + clock::time_point FirstApply(); + clock::time_point ApplyBegun(); + clock::time_point ApplyEnded(); + + Windows::Foundation::Collections::IVectorView<ConfigurationUnit> ConfigurationUnits(); + void ConfigurationUnits(const Windows::Foundation::Collections::IVectorView<ConfigurationUnit>& value); + + event_token ConfigurationSetChange(const Windows::Foundation::TypedEventHandler<WinRT_Self, ConfigurationSetChangeData>& handler); + void ConfigurationSetChange(const event_token& token) noexcept; + + void Serialize(const Windows::Storage::Streams::IOutputStream& stream); + + void Remove(); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + private: + hstring m_name; + hstring m_origin; + hstring m_path; + guid m_instanceIdentifier; + clock::time_point m_firstApply{}; + Windows::Foundation::Collections::IVector<ConfigurationUnit> m_configurationUnits{ winrt::single_threaded_vector<ConfigurationUnit>() }; + winrt::event<Windows::Foundation::TypedEventHandler<WinRT_Self, ConfigurationSetChangeData>> m_configurationSetChange; + + MutableFlag m_mutableFlag; +#endif + }; +} + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) +namespace winrt::Microsoft::Management::Configuration::factory_implementation +{ + struct ConfigurationSet : ConfigurationSetT<ConfigurationSet, implementation::ConfigurationSet> + { + }; +} +#endif diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetApplyProcessor.cpp b/src/Microsoft.Management.Configuration/ConfigurationSetApplyProcessor.cpp @@ -0,0 +1,447 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "ConfigurationSetApplyProcessor.h" +#include "ConfigurationSetChangeData.h" +#include "ExceptionResultHelpers.h" + +#include <AppInstallerErrors.h> +#include <AppInstallerLogging.h> +#include <AppInstallerStrings.h> + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + namespace + { + std::string GetNormalizedIdentifier(hstring identifier) + { + using namespace AppInstaller::Utility; + return FoldCase(NormalizedString{ identifier }); + } + } + + ConfigurationSetApplyProcessor::ConfigurationSetApplyProcessor(const Configuration::ConfigurationSet& configurationSet, IConfigurationSetProcessor&& setProcessor, result_type result, const std::function<void(ConfigurationSetChangeData)>& progress) : + m_setProcessor(std::move(setProcessor)), m_result(std::move(result)), m_progress(progress) + { + // Create a copy of the set of configuration units + auto unitsView = configurationSet.ConfigurationUnits(); + std::vector<ConfigurationUnit> unitsToProcess{ unitsView.Size() }; + unitsView.GetMany(0, unitsToProcess); + + // Create the unit info vector from these units + for (const auto& unit : unitsToProcess) + { + m_unitInfo.emplace_back(unit); + m_result->UnitResultsVector().Append(*m_unitInfo.back().Result); + } + } + + void ConfigurationSetApplyProcessor::Process() + { + if (!PreProcess()) + { + return; + } + + // TODO: When cross process is implemented, send Pending until we actually start + SendProgress(ConfigurationSetState::InProgress); + + ProcessInternal(HasProcessedSuccessfully, &ConfigurationSetApplyProcessor::ProcessUnit, true); + + SendProgress(ConfigurationSetState::Completed); + } + + ConfigurationSetApplyProcessor::UnitInfo::UnitInfo(const Configuration::ConfigurationUnit& unit) : + Unit(unit), Result(make_self<wil::details::module_count_wrapper<implementation::ApplyConfigurationUnitResult>>()), + ResultInformation(make_self<wil::details::module_count_wrapper<implementation::ConfigurationUnitResultInformation>>()) + { + Result->Unit(unit); + Result->ResultInformation(*ResultInformation); + } + + bool ConfigurationSetApplyProcessor::PreProcess() + { + bool result = true; + + for (size_t i = 0; i < m_unitInfo.size(); ++i) + { + if (!AddUnitToMap(m_unitInfo[i], i)) + { + result = false; + } + } + + if (!result) + { + // This is the only error that adding to the map can produce + m_result->ResultCode(WINGET_CONFIG_ERROR_DUPLICATE_IDENTIFIER); + return false; + } + + for (UnitInfo& unitInfo : m_unitInfo) + { + for (hstring dependencyHstring : unitInfo.Unit.Dependencies()) + { + // Throw out empty dependency strings + if (dependencyHstring.empty()) + { + continue; + } + + std::string dependency = GetNormalizedIdentifier(dependencyHstring); + auto itr = m_idToUnitInfoIndex.find(dependency); + if (itr == m_idToUnitInfoIndex.end()) + { + AICLI_LOG(Config, Error, << "Found missing dependency: " << dependency); + unitInfo.ResultInformation->ResultCode(WINGET_CONFIG_ERROR_MISSING_DEPENDENCY); + result = false; + } + else + { + unitInfo.DependencyIndices.emplace_back(itr->second); + } + } + } + + if (!result) + { + // This is the only error that adding to the map can produce + m_result->ResultCode(WINGET_CONFIG_ERROR_MISSING_DEPENDENCY); + return false; + } + + return ProcessInternal(HasPreprocessed, &ConfigurationSetApplyProcessor::MarkPreprocessed); + } + + bool ConfigurationSetApplyProcessor::AddUnitToMap(UnitInfo& unitInfo, size_t unitInfoIndex) + { + hstring originalIdentifier = unitInfo.Unit.Identifier(); + if (originalIdentifier.empty()) + { + return true; + } + + std::string identifier = GetNormalizedIdentifier(originalIdentifier); + + auto itr = m_idToUnitInfoIndex.find(identifier); + if (itr != m_idToUnitInfoIndex.end()) + { + AICLI_LOG(Config, Error, << "Found duplicate identifier: " << identifier); + // Found a duplicate identifier, mark both as such + unitInfo.ResultInformation->ResultCode(WINGET_CONFIG_ERROR_DUPLICATE_IDENTIFIER); + m_unitInfo[itr->second].ResultInformation->ResultCode(WINGET_CONFIG_ERROR_DUPLICATE_IDENTIFIER); + return false; + } + else + { + m_idToUnitInfoIndex.emplace(std::move(identifier), unitInfoIndex); + return true; + } + } + + bool ConfigurationSetApplyProcessor::ProcessInternal(CheckDependencyPtr checkDependencyFunction, ProcessUnitPtr processUnitFunction, bool sendProgress) + { + // Create the set of units that need to be processed + std::vector<size_t> unitsToProcess; + for (size_t i = 0, size = m_unitInfo.size(); i < size; ++i) + { + unitsToProcess.emplace_back(i); + } + + // Always process all ConfigurationUnitIntent::Assert first + if (!ProcessIntentInternal( + unitsToProcess, + checkDependencyFunction, + processUnitFunction, + ConfigurationUnitIntent::Assert, + WINGET_CONFIG_ERROR_ASSERTION_FAILED, + WINGET_CONFIG_ERROR_ASSERTION_FAILED, + sendProgress)) + { + return false; + } + + // Then all ConfigurationUnitIntent::Inform + if (!ProcessIntentInternal( + unitsToProcess, + checkDependencyFunction, + processUnitFunction, + ConfigurationUnitIntent::Inform, + WINGET_CONFIG_ERROR_DEPENDENCY_UNSATISFIED, + WINGET_CONFIG_ERROR_DEPENDENCY_UNSATISFIED, + sendProgress)) + { + return false; + } + + // Then all ConfigurationUnitIntent::Apply + return ProcessIntentInternal( + unitsToProcess, + checkDependencyFunction, + processUnitFunction, + ConfigurationUnitIntent::Apply, + E_FAIL, // This should not happen as there are no other intents left + WINGET_CONFIG_ERROR_SET_APPLY_FAILED, + sendProgress); + } + + bool ConfigurationSetApplyProcessor::ProcessIntentInternal( + std::vector<size_t>& unitsToProcess, + CheckDependencyPtr checkDependencyFunction, + ProcessUnitPtr processUnitFunction, + ConfigurationUnitIntent intent, + hresult errorForOtherIntents, + hresult errorForFailures, + bool sendProgress) + { + // Always process the first item in the list that is available to be processed + bool hasProcessed = true; + bool hasFailure = false; + while (hasProcessed) + { + hasProcessed = false; + for (auto itr = unitsToProcess.begin(), end = unitsToProcess.end(); itr != end; ++itr) + { + UnitInfo& unitInfo = m_unitInfo[*itr]; + if (HasIntentAndSatisfiedDependencies(unitInfo, intent, checkDependencyFunction)) + { + if (!(this->*processUnitFunction)(unitInfo)) + { + hasFailure = true; + } + unitsToProcess.erase(itr); + hasProcessed = true; + break; + } + } + } + + // Mark all remaining items with intent as failed due to dependency + bool hasRemainingDependencies = false; + for (size_t index : unitsToProcess) + { + UnitInfo& unitInfo = m_unitInfo[index]; + if (unitInfo.Unit.Intent() == intent) + { + hasRemainingDependencies = true; + unitInfo.ResultInformation->ResultCode(WINGET_CONFIG_ERROR_DEPENDENCY_UNSATISFIED); + if (sendProgress) + { + SendProgress(ConfigurationUnitState::Skipped, unitInfo); + } + } + } + + // Any failures are fatal, mark all other units as failed due to that + if (hasFailure || hasRemainingDependencies) + { + for (size_t index : unitsToProcess) + { + UnitInfo& unitInfo = m_unitInfo[index]; + if (unitInfo.Unit.Intent() != intent) + { + unitInfo.ResultInformation->ResultCode(errorForOtherIntents); + if (sendProgress) + { + SendProgress(ConfigurationUnitState::Skipped, unitInfo); + } + } + } + + if (hasFailure) + { + m_result->ResultCode(errorForFailures); + } + else // hasRemainingDependencies + { + m_result->ResultCode(WINGET_CONFIG_ERROR_DEPENDENCY_UNSATISFIED); + } + return false; + } + + return true; + } + + bool ConfigurationSetApplyProcessor::HasIntentAndSatisfiedDependencies( + const UnitInfo& unitInfo, + ConfigurationUnitIntent intent, + CheckDependencyPtr checkDependencyFunction) const + { + bool result = false; + + if (unitInfo.Unit.Intent() == intent) + { + result = true; + for (size_t dependencyIndex : unitInfo.DependencyIndices) + { + if (!checkDependencyFunction(m_unitInfo[dependencyIndex])) + { + result = false; + break; + } + } + } + + return result; + } + + bool ConfigurationSetApplyProcessor::HasPreprocessed(const UnitInfo& unitInfo) + { + return unitInfo.PreProcessed; + } + + bool ConfigurationSetApplyProcessor::MarkPreprocessed(UnitInfo& unitInfo) + { + unitInfo.PreProcessed = true; + return true; + } + + bool ConfigurationSetApplyProcessor::HasProcessedSuccessfully(const UnitInfo& unitInfo) + { + return unitInfo.Processed && SUCCEEDED(unitInfo.ResultInformation->ResultCode()); + } + + bool ConfigurationSetApplyProcessor::ProcessUnit(UnitInfo& unitInfo) + { + IConfigurationUnitProcessor unitProcessor; + + // Once we get this far, consider the unit processed even if we fail to create the actual processor. + unitInfo.Processed = true; + + if (!unitInfo.Unit.ShouldApply()) + { + // If the unit is requested to be skipped, we mark it with a failure to prevent any dependency from running. + // But we return true from this function to indicate a successful "processing". + unitInfo.ResultInformation->ResultCode(WINGET_CONFIG_ERROR_MANUALLY_SKIPPED); + SendProgress(ConfigurationUnitState::Skipped, unitInfo); + return true; + } + + // Send a progress event that we are starting, and prepare one for completion when we exit the function + SendProgress(ConfigurationUnitState::InProgress, unitInfo); + auto sendCompletedProgress = wil::scope_exit([this, &unitInfo]() { SendProgress(ConfigurationUnitState::Completed, unitInfo); }); + + try + { + unitProcessor = m_setProcessor.CreateUnitProcessor(unitInfo.Unit, {}); + } + catch (...) + { + ExtractUnitResultInformation(std::current_exception(), unitInfo.ResultInformation); + return false; + } + + try + { + switch (unitInfo.Unit.Intent()) + { + case ConfigurationUnitIntent::Assert: + { + TestSettingsResult settingsResult = unitProcessor.TestSettings(); + + if (settingsResult.TestResult() == ConfigurationTestResult::Positive) + { + return true; + } + else if (settingsResult.TestResult() == ConfigurationTestResult::Negative) + { + unitInfo.ResultInformation->ResultCode(WINGET_CONFIG_ERROR_ASSERTION_FAILED); + return false; + } + else if (settingsResult.TestResult() == ConfigurationTestResult::Failed) + { + unitInfo.ResultInformation->Initialize(settingsResult.ResultInformation()); + return false; + } + else + { + unitInfo.ResultInformation->ResultCode(E_UNEXPECTED); + return false; + } + } + case ConfigurationUnitIntent::Inform: + { + // Force the processor to retrieve the settings + GetSettingsResult settingsResult = unitProcessor.GetSettings(); + if (SUCCEEDED(settingsResult.ResultInformation().ResultCode())) + { + return true; + } + else + { + unitInfo.ResultInformation->Initialize(settingsResult.ResultInformation()); + return false; + } + } + case ConfigurationUnitIntent::Apply: + { + TestSettingsResult testSettingsResult = unitProcessor.TestSettings(); + + if (testSettingsResult.TestResult() == ConfigurationTestResult::Positive) + { + unitInfo.Result->PreviouslyInDesiredState(true); + return true; + } + else if (testSettingsResult.TestResult() == ConfigurationTestResult::Negative) + { + ApplySettingsResult applySettingsResult = unitProcessor.ApplySettings(); + if (SUCCEEDED(applySettingsResult.ResultInformation().ResultCode())) + { + unitInfo.Result->RebootRequired(applySettingsResult.RebootRequired()); + return true; + } + else + { + unitInfo.ResultInformation->Initialize(applySettingsResult.ResultInformation()); + return false; + } + } + else if (testSettingsResult.TestResult() == ConfigurationTestResult::Failed) + { + unitInfo.ResultInformation->Initialize(testSettingsResult.ResultInformation()); + return false; + } + else + { + unitInfo.ResultInformation->ResultCode(E_UNEXPECTED); + return false; + } + } + default: + unitInfo.ResultInformation->ResultCode(E_UNEXPECTED); + return false; + } + } + catch (...) + { + ExtractUnitResultInformation(std::current_exception(), unitInfo.ResultInformation); + return false; + } + } + + void ConfigurationSetApplyProcessor::SendProgress(ConfigurationSetState state) + { + if (m_progress) + { + try + { + m_progress(implementation::ConfigurationSetChangeData::Create(state)); + } + CATCH_LOG(); + } + } + + void ConfigurationSetApplyProcessor::SendProgress(ConfigurationUnitState state, const UnitInfo& unitInfo) + { + unitInfo.Result->State(state); + + if (m_progress) + { + try + { + m_progress(implementation::ConfigurationSetChangeData::Create(state, *unitInfo.ResultInformation, unitInfo.Unit)); + } + CATCH_LOG(); + } + } +} diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetApplyProcessor.h b/src/Microsoft.Management.Configuration/ConfigurationSetApplyProcessor.h @@ -0,0 +1,100 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "ConfigurationSet.h" +#include "ConfigurationUnit.h" +#include "ApplyConfigurationSetResult.h" +#include "ApplyConfigurationUnitResult.h" +#include "ConfigurationUnitResultInformation.h" + +#include <map> +#include <string> +#include <vector> + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + // A helper to better organize the configuration set Apply. + struct ConfigurationSetApplyProcessor + { + using ConfigurationSet = Configuration::ConfigurationSet; + using ConfigurationUnit = Configuration::ConfigurationUnit; + using ConfigurationUnitResultInformation = Configuration::ConfigurationUnitResultInformation; + using ConfigurationSetChangeData = Configuration::ConfigurationSetChangeData; + + using result_type = decltype(make_self<wil::details::module_count_wrapper<implementation::ApplyConfigurationSetResult>>()); + + ConfigurationSetApplyProcessor(const ConfigurationSet& configurationSet, IConfigurationSetProcessor&& setProcessor, result_type result, const std::function<void(ConfigurationSetChangeData)>& progress); + + // Processes the apply for the configuration set. + void Process(); + + private: + // Contains all of the relevant data for a configuration unit. + struct UnitInfo + { + UnitInfo(const ConfigurationUnit& unit); + + ConfigurationUnit Unit; + std::vector<size_t> DependencyIndices; + decltype(make_self<wil::details::module_count_wrapper<implementation::ApplyConfigurationUnitResult>>()) Result; + decltype(make_self<wil::details::module_count_wrapper<implementation::ConfigurationUnitResultInformation>>()) ResultInformation; + bool PreProcessed = false; + bool Processed = false; + }; + + // Builds out some data used during processing and validates the set along the way. + bool PreProcess(); + + // Adds the given unit to the identifier to unit info index map. + bool AddUnitToMap(UnitInfo& unitInfo, size_t unitInfoIndex); + + // Checks the dependency; returns true to indicate that the dependency is satisfied, false if not. + using CheckDependencyPtr = bool (*)(const UnitInfo&); + + // Processes the unit; returns true if successful, false if not. + using ProcessUnitPtr = bool (ConfigurationSetApplyProcessor::*)(UnitInfo&); + + // Runs the processing using the given functions. + bool ProcessInternal(CheckDependencyPtr checkDependencyFunction, ProcessUnitPtr processUnitFunction, bool sendProgress = false); + + // Processes one of the non-writing intent types, which are fatal if not all successful + bool ProcessIntentInternal( + std::vector<size_t>& unitsToProcess, + CheckDependencyPtr checkDependencyFunction, + ProcessUnitPtr processUnitFunction, + ConfigurationUnitIntent intent, + hresult errorForOtherIntents, + hresult errorForFailures, + bool sendProgress); + + // Determines if the given unit has the given intent and all of its dependencies are satisfied + bool HasIntentAndSatisfiedDependencies( + const UnitInfo& unitInfo, + ConfigurationUnitIntent intent, + CheckDependencyPtr checkDependencyFunction) const; + + // Checks a dependency for preprocessing. + static bool HasPreprocessed(const UnitInfo& unitInfo); + + // Marks a unit as preprocessed. + bool MarkPreprocessed(UnitInfo& unitInfo); + + // Checks a dependency for having processed successfully. + static bool HasProcessedSuccessfully(const UnitInfo& unitInfo); + + // Processes a configuration unit per its intent. + bool ProcessUnit(UnitInfo& unitInfo); + + // Sends progress + // TODO: Eventually these functions/call sites will be used for history + void SendProgress(ConfigurationSetState state); + void SendProgress(ConfigurationUnitState state, const UnitInfo& unitInfo); + + IConfigurationSetProcessor m_setProcessor; + result_type m_result; + std::function<void(ConfigurationSetChangeData)> m_progress; + std::vector<UnitInfo> m_unitInfo; + std::map<std::string, size_t> m_idToUnitInfoIndex; + hresult m_resultCode; + }; +} diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetChangeData.cpp b/src/Microsoft.Management.Configuration/ConfigurationSetChangeData.cpp @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "ConfigurationSetChangeData.h" +#include "ConfigurationSetChangeData.g.cpp" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + Configuration::ConfigurationSetChangeData ConfigurationSetChangeData::Create(ConfigurationSetState state) + { + auto result = make_self<wil::details::module_count_wrapper<implementation::ConfigurationSetChangeData>>(); + result->Initialize(state); + return *result; + } + + Configuration::ConfigurationSetChangeData ConfigurationSetChangeData::Create(ConfigurationUnitState state, ConfigurationUnitResultInformation resultInformation, ConfigurationUnit unit) + { + auto result = make_self<wil::details::module_count_wrapper<implementation::ConfigurationSetChangeData>>(); + result->Initialize(state, resultInformation, unit); + return *result; + } + + void ConfigurationSetChangeData::Initialize(ConfigurationSetState state) + { + m_change = ConfigurationSetChangeEventType::SetStateChanged; + m_setState = state; + } + + void ConfigurationSetChangeData::Initialize(ConfigurationUnitState state, ConfigurationUnitResultInformation resultInformation, ConfigurationUnit unit) + { + m_change = ConfigurationSetChangeEventType::UnitStateChanged; + m_setState = ConfigurationSetState::InProgress; + m_unitState = state; + m_resultInformation = resultInformation; + m_unit = unit; + } + + ConfigurationSetChangeEventType ConfigurationSetChangeData::Change() + { + return m_change; + } + + ConfigurationSetState ConfigurationSetChangeData::SetState() + { + return m_setState; + } + + ConfigurationUnitState ConfigurationSetChangeData::UnitState() + { + return m_unitState; + } + + ConfigurationUnitResultInformation ConfigurationSetChangeData::ResultInformation() + { + return m_resultInformation; + } + + ConfigurationUnit ConfigurationSetChangeData::Unit() + { + return m_unit; + } +} diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetChangeData.h b/src/Microsoft.Management.Configuration/ConfigurationSetChangeData.h @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "ConfigurationSetChangeData.g.h" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + struct ConfigurationSetChangeData : ConfigurationSetChangeDataT<ConfigurationSetChangeData> + { + using ConfigurationUnit = Configuration::ConfigurationUnit; + using ConfigurationUnitResultInformation = Configuration::ConfigurationUnitResultInformation; + + ConfigurationSetChangeData() = default; + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + static Configuration::ConfigurationSetChangeData Create(ConfigurationSetState state); + static Configuration::ConfigurationSetChangeData Create(ConfigurationUnitState state, ConfigurationUnitResultInformation resultInformation, ConfigurationUnit unit); + + void Initialize(ConfigurationSetState state); + void Initialize(ConfigurationUnitState state, ConfigurationUnitResultInformation resultInformation, ConfigurationUnit unit); +#endif + + ConfigurationSetChangeEventType Change(); + ConfigurationSetState SetState(); + ConfigurationUnitState UnitState(); + ConfigurationUnitResultInformation ResultInformation(); + ConfigurationUnit Unit(); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + private: + ConfigurationSetChangeEventType m_change = ConfigurationSetChangeEventType::Unknown; + ConfigurationSetState m_setState = ConfigurationSetState::Unknown; + ConfigurationUnitState m_unitState = ConfigurationUnitState::Unknown; + ConfigurationUnitResultInformation m_resultInformation = nullptr; + ConfigurationUnit m_unit = nullptr; +#endif + }; +} diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetParser.cpp b/src/Microsoft.Management.Configuration/ConfigurationSetParser.cpp @@ -0,0 +1,98 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include <ConfigurationSetParser.h> + +#include <AppInstallerErrors.h> +#include <AppInstallerLogging.h> +#include <AppInstallerStrings.h> +#include <AppInstallerVersions.h> +#include <winget/Yaml.h> + +#include "ConfigurationSetParserError.h" +#include "ConfigurationSetParser_0_1.h" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + namespace + { + std::string StreamToString(const Windows::Storage::Streams::IInputStream& stream) + { + uint32_t bufferSize = 1 << 20; + Windows::Storage::Streams::Buffer buffer(bufferSize); + Windows::Storage::Streams::InputStreamOptions readOptions = Windows::Storage::Streams::InputStreamOptions::Partial | Windows::Storage::Streams::InputStreamOptions::ReadAhead; + std::string result; + + for (;;) + { + Windows::Storage::Streams::IBuffer readBuffer = stream.ReadAsync(buffer, bufferSize, readOptions).get(); + + size_t readSize = static_cast<size_t>(readBuffer.Length()); + if (readSize) + { + static_assert(sizeof(char) == sizeof(*readBuffer.data())); + result.append(reinterpret_cast<char*>(readBuffer.data()), readSize); + } + else + { + break; + } + } + + return result; + } + } + + std::unique_ptr<ConfigurationSetParser> ConfigurationSetParser::Create(const Windows::Storage::Streams::IInputStream& stream) + { + return Create(StreamToString(stream)); + } + + std::unique_ptr<ConfigurationSetParser> ConfigurationSetParser::Create(std::string_view input) + { + AppInstaller::YAML::Node document; + + try + { + document = AppInstaller::YAML::Load(input); + } + CATCH_LOG(); + + if (!document.IsMap()) + { + AICLI_LOG(Config, Info, << "Invalid YAML"); + return std::make_unique<ConfigurationSetParserError>(WINGET_CONFIG_ERROR_INVALID_YAML); + } + + AppInstaller::YAML::Node& propertiesNode = document[NodeName_Properties]; + if (!propertiesNode.IsMap()) + { + AICLI_LOG(Config, Info, << "Invalid properties"); + return std::make_unique<ConfigurationSetParserError>(WINGET_CONFIG_ERROR_INVALID_FIELD, NodeName_Properties); + } + + AppInstaller::YAML::Node& versionNode = propertiesNode[NodeName_ConfigurationVersion]; + if (!versionNode.IsScalar()) + { + AICLI_LOG(Config, Info, << "Invalid configuration version"); + return std::make_unique<ConfigurationSetParserError>(WINGET_CONFIG_ERROR_INVALID_FIELD, NodeName_ConfigurationVersion); + } + + AppInstaller::Utility::SemanticVersion schemaVersion(versionNode.as<std::string>()); + + if (schemaVersion.PartAt(0).Integer == 0 && schemaVersion.PartAt(1).Integer == 1) + { + return std::make_unique<ConfigurationSetParser_0_1>(std::move(document)); + } + + AICLI_LOG(Config, Info, << "Unknown configuration version: " << schemaVersion.ToString()); + return std::make_unique<ConfigurationSetParserError>(WINGET_CONFIG_ERROR_UNKNOWN_CONFIGURATION_FILE_VERSION); + } + + void ConfigurationSetParser::SetError(hresult result, std::string_view field) + { + AICLI_LOG(Config, Error, << "ConfigurationSetParser error: " << AppInstaller::Logging::SetHRFormat << result << " [" << field << "]"); + m_result = result; + m_field = AppInstaller::Utility::ConvertToUTF16(field); + } +} diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetParser.h b/src/Microsoft.Management.Configuration/ConfigurationSetParser.h @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include <ConfigurationUnit.h> +#include <winrt/Windows.Storage.Streams.h> +#include <memory> +#include <string_view> +#include <vector> + +using namespace std::string_view_literals; + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + // Interface for parsing a configuration set stream. + struct ConfigurationSetParser + { + static constexpr std::string_view NodeName_Properties = "properties"sv; + static constexpr std::string_view NodeName_ConfigurationVersion = "configurationVersion"sv; + + // Create a parser from the given stream. + static std::unique_ptr<ConfigurationSetParser> Create(const Windows::Storage::Streams::IInputStream& stream); + + // Create a parser from the given bytes (the encoding is detected). + static std::unique_ptr<ConfigurationSetParser> Create(std::string_view input); + + virtual ~ConfigurationSetParser() noexcept = default; + + ConfigurationSetParser(const ConfigurationSetParser&) = delete; + ConfigurationSetParser& operator=(const ConfigurationSetParser&) = delete; + ConfigurationSetParser(ConfigurationSetParser&&) = default; + ConfigurationSetParser& operator=(ConfigurationSetParser&&) = default; + + // Retrieve the configuration units from the parser. + virtual std::vector<Configuration::ConfigurationUnit> GetConfigurationUnits() = 0; + + // The latest result code from the parser. + hresult Result() const { return m_result; } + + // The field related to the result code. + hstring Field() const { return m_field; } + + protected: + ConfigurationSetParser() = default; + + // Set the error state + void SetError(hresult result, std::string_view field = {}); + + hresult m_result; + hstring m_field; + }; +} diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetParserError.h b/src/Microsoft.Management.Configuration/ConfigurationSetParserError.h @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "ConfigurationSetParser.h" +#include <string_view> + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + // Parser object that only indicates an error occurred. + struct ConfigurationSetParserError : public ConfigurationSetParser + { + ConfigurationSetParserError(hresult result, std::string_view field = {}) + { + SetError(result, field); + } + + std::vector<Configuration::ConfigurationUnit> GetConfigurationUnits() override { return {}; } + }; +} diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_1.cpp b/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_1.cpp @@ -0,0 +1,207 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "ConfigurationSetParser_0_1.h" + +#include <AppInstallerErrors.h> +#include <AppInstallerStrings.h> + +#include <sstream> + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + using namespace AppInstaller::YAML; + +#define CHECK_ERROR(_op_) (_op_); if (FAILED(m_result)) { return; } +#define FIELD_ERROR(_field_) SetError(WINGET_CONFIG_ERROR_INVALID_FIELD, (_field_)); return +#define FIELD_ERROR_IF(_condition_,_field_) if (_condition_) { FIELD_ERROR(_field_); } + + namespace + { + Windows::Foundation::IInspectable GetIInspectableFromNode(const Node& node); + + // Returns the appropriate IPropertyValue for the given node, which is assumed to be a scalar. + Windows::Foundation::IInspectable GetPropertyValueFromScalar(const Node& node) + { + // TODO: Use the tag to determine the type of property to create. + return Windows::Foundation::PropertyValue::CreateString(node.as<std::wstring>()); + } + + // Returns the appropriate IPropertyValue for the given node, which is assumed to be a scalar. + Windows::Foundation::IInspectable GetPropertyValueFromSequence(const Node& sequenceNode) + { + Windows::Foundation::Collections::ValueSet result; + size_t index = 0; + + for (const Node& sequenceItem : sequenceNode.Sequence()) + { + std::wostringstream strstr; + strstr << index++; + result.Insert(strstr.str(), GetIInspectableFromNode(sequenceItem)); + } + + result.Insert(L"treatAsArray", Windows::Foundation::PropertyValue::CreateBoolean(true)); + return result; + } + + // Fills the ValueSet from the given node, which is assumed to be a map. + void FillValueSetFromMap(const Node& mapNode, const Windows::Foundation::Collections::ValueSet& valueSet) + { + for (const auto& mapItem : mapNode.Mapping()) + { + // Insert returns true if it replaces an existing key, and that indicates an invalid map. + THROW_HR_IF(WINGET_CONFIG_ERROR_INVALID_CONFIGURATION_FILE, valueSet.Insert(mapItem.first.as<std::wstring>(), GetIInspectableFromNode(mapItem.second))); + } + } + + // Returns the appropriate IInspectable for the given node. + Windows::Foundation::IInspectable GetIInspectableFromNode(const Node& node) + { + ::winrt::Windows::Foundation::IInspectable result; + + switch (node.GetType()) + { + case Node::Type::Invalid: + case Node::Type::None: + // Leave value as null + break; + case Node::Type::Scalar: + result = GetPropertyValueFromScalar(node); + break; + case Node::Type::Sequence: + result = GetPropertyValueFromSequence(node); + break; + case Node::Type::Mapping: + { + Windows::Foundation::Collections::ValueSet subset; + FillValueSetFromMap(node, subset); + result = std::move(subset); + } + break; + default: + THROW_HR(E_UNEXPECTED); + } + + return result; + } + } + + std::vector<Configuration::ConfigurationUnit> ConfigurationSetParser_0_1::GetConfigurationUnits() + { + std::vector<Configuration::ConfigurationUnit> result; + const Node& properties = m_document[NodeName_Properties]; + ParseConfigurationUnitsFromSubsection(properties, "assertions", ConfigurationUnitIntent::Assert, result); + ParseConfigurationUnitsFromSubsection(properties, "parameters", ConfigurationUnitIntent::Inform, result); + ParseConfigurationUnitsFromSubsection(properties, "resources", ConfigurationUnitIntent::Apply, result); + // TODO: Additional semantic validation? + return result; + } + + void ConfigurationSetParser_0_1::ParseConfigurationUnitsFromSubsection(const Node& document, std::string_view subsection, ConfigurationUnitIntent intent, std::vector<Configuration::ConfigurationUnit>& result) + { + if (FAILED(m_result)) + { + return; + } + + Node subsectionNode = document[subsection]; + + if (!subsectionNode.IsDefined()) + { + return; + } + + FIELD_ERROR_IF(!subsectionNode.IsSequence(), subsection); + + std::ostringstream strstr; + strstr << subsection; + size_t index = 0; + + for (const Node& item : subsectionNode.Sequence()) + { + if (!item.IsMap()) + { + strstr << '[' << index << ']'; + FIELD_ERROR(strstr.str()); + } + index++; + + auto configurationUnit = make_self<wil::details::module_count_wrapper<ConfigurationUnit>>(); + + CHECK_ERROR(GetStringValueForUnit(item, "resource", true, configurationUnit.get(), &ConfigurationUnit::UnitName)); + CHECK_ERROR(GetStringValueForUnit(item, "id", false, configurationUnit.get(), &ConfigurationUnit::Identifier)); + configurationUnit->Intent(intent); + CHECK_ERROR(GetStringArrayForUnit(item, "dependsOn", configurationUnit.get(), &ConfigurationUnit::Dependencies)); + CHECK_ERROR(GetValueSet(item, "directives", false, configurationUnit->Directives())); + CHECK_ERROR(GetValueSet(item, "settings", false, configurationUnit->Settings())); + + result.emplace_back(*configurationUnit); + } + } + + void ConfigurationSetParser_0_1::GetStringValueForUnit(const Node& item, std::string_view valueName, bool required, ConfigurationUnit* unit, void(ConfigurationUnit::* propertyFunction)(const hstring& value)) + { + const Node& valueNode = item[valueName]; + + if (valueNode) + { + FIELD_ERROR_IF(!valueNode.IsScalar(), valueName); + } + else + { + FIELD_ERROR_IF(required, valueName); + return; + } + + (unit->*propertyFunction)(hstring{ valueNode.as<std::wstring>() }); + } + + void ConfigurationSetParser_0_1::GetStringArrayForUnit(const Node& item, std::string_view arrayName, ConfigurationUnit* unit, void(ConfigurationUnit::* propertyFunction)(std::vector<hstring>&& value)) + { + const Node& arrayNode = item[arrayName]; + + if (!arrayNode) + { + return; + } + + FIELD_ERROR_IF(!arrayNode.IsSequence(), arrayName); + + std::vector<hstring> arrayValue; + + std::ostringstream strstr; + strstr << arrayName; + size_t index = 0; + + for (const Node& arrayItem : arrayNode.Sequence()) + { + if (!arrayItem.IsScalar()) + { + strstr << '[' << index << ']'; + FIELD_ERROR(strstr.str()); + } + index++; + + arrayValue.emplace_back(arrayItem.as<std::wstring>()); + } + + (unit->*propertyFunction)(std::move(arrayValue)); + } + + void ConfigurationSetParser_0_1::GetValueSet(const Node& item, std::string_view mapName, bool required, const Windows::Foundation::Collections::ValueSet& valueSet) + { + const Node& mapNode = item[mapName]; + + if (mapNode) + { + FIELD_ERROR_IF(!mapNode.IsMap(), mapName); + } + else + { + FIELD_ERROR_IF(required, mapName); + return; + } + + FillValueSetFromMap(mapNode, valueSet); + } +} diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_1.h b/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_1.h @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "ConfigurationSetParser.h" + +#include <winget/Yaml.h> + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + // Parser for schema version 0.1 + struct ConfigurationSetParser_0_1 : public ConfigurationSetParser + { + ConfigurationSetParser_0_1(AppInstaller::YAML::Node&& document) : m_document(std::move(document)) {} + + virtual ~ConfigurationSetParser_0_1() noexcept = default; + + ConfigurationSetParser_0_1(const ConfigurationSetParser_0_1&) = delete; + ConfigurationSetParser_0_1& operator=(const ConfigurationSetParser_0_1&) = delete; + ConfigurationSetParser_0_1(ConfigurationSetParser_0_1&&) = default; + ConfigurationSetParser_0_1& operator=(ConfigurationSetParser_0_1&&) = default; + + // Retrieve the configuration units from the parser. + std::vector<Configuration::ConfigurationUnit> GetConfigurationUnits() override; + + protected: + void ParseConfigurationUnitsFromSubsection(const AppInstaller::YAML::Node& document, std::string_view subsection, ConfigurationUnitIntent intent, std::vector<Configuration::ConfigurationUnit>& result); + void GetStringValueForUnit(const AppInstaller::YAML::Node& item, std::string_view valueName, bool required, ConfigurationUnit* unit, void(ConfigurationUnit::* propertyFunction)(const hstring& value)); + void GetStringArrayForUnit(const AppInstaller::YAML::Node& item, std::string_view arrayName, ConfigurationUnit* unit, void(ConfigurationUnit::* propertyFunction)(std::vector<hstring>&& value)); + void GetValueSet(const AppInstaller::YAML::Node& item, std::string_view mapName, bool required, const Windows::Foundation::Collections::ValueSet& valueSet); + + AppInstaller::YAML::Node m_document; + }; +} diff --git a/src/Microsoft.Management.Configuration/ConfigurationUnit.cpp b/src/Microsoft.Management.Configuration/ConfigurationUnit.cpp @@ -0,0 +1,117 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "ConfigurationUnit.h" +#include "ConfigurationUnit.g.cpp" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + ConfigurationUnit::ConfigurationUnit() + { + GUID instanceIdentifier; + THROW_IF_FAILED(CoCreateGuid(&instanceIdentifier)); + m_instanceIdentifier = instanceIdentifier; + } + + ConfigurationUnit::ConfigurationUnit(const guid& instanceIdentifier) : + m_instanceIdentifier(instanceIdentifier), m_mutableFlag(false) + { + } + + hstring ConfigurationUnit::UnitName() + { + return m_unitName; + } + + void ConfigurationUnit::UnitName(const hstring& value) + { + m_mutableFlag.RequireMutable(); + m_unitName = value; + } + + guid ConfigurationUnit::InstanceIdentifier() + { + return m_instanceIdentifier; + } + + hstring ConfigurationUnit::Identifier() + { + return m_identifier; + } + + void ConfigurationUnit::Identifier(const hstring& value) + { + m_mutableFlag.RequireMutable(); + m_identifier = value; + } + + ConfigurationUnitIntent ConfigurationUnit::Intent() + { + return m_intent; + } + + void ConfigurationUnit::Intent(ConfigurationUnitIntent value) + { + m_mutableFlag.RequireMutable(); + m_intent = value; + } + + Windows::Foundation::Collections::IVectorView<hstring> ConfigurationUnit::Dependencies() + { + return m_dependencies.GetView(); + } + + void ConfigurationUnit::Dependencies(const Windows::Foundation::Collections::IVectorView<hstring>& value) + { + m_mutableFlag.RequireMutable(); + + std::vector<hstring> temp{ value.Size() }; + value.GetMany(0, temp); + m_dependencies = winrt::single_threaded_vector<hstring>(std::move(temp)); + } + + void ConfigurationUnit::Dependencies(std::vector<hstring>&& value) + { + m_dependencies = winrt::single_threaded_vector<hstring>(std::move(value)); + } + + Windows::Foundation::Collections::ValueSet ConfigurationUnit::Directives() + { + return m_directives; + } + + Windows::Foundation::Collections::ValueSet ConfigurationUnit::Settings() + { + return m_settings; + } + + IConfigurationUnitProcessorDetails ConfigurationUnit::Details() + { + return m_details; + } + + void ConfigurationUnit::Details(IConfigurationUnitProcessorDetails&& details) + { + m_details = std::move(details); + } + + ConfigurationUnitState ConfigurationUnit::State() + { + return ConfigurationUnitState::Unknown; + } + + ConfigurationUnitResultInformation ConfigurationUnit::ResultInformation() + { + return nullptr; + } + + bool ConfigurationUnit::ShouldApply() + { + return m_shouldApply; + } + + void ConfigurationUnit::ShouldApply(bool value) + { + m_shouldApply = value; + } +} diff --git a/src/Microsoft.Management.Configuration/ConfigurationUnit.h b/src/Microsoft.Management.Configuration/ConfigurationUnit.h @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "ConfigurationUnit.g.h" +#include "MutableFlag.h" +#include <winrt/Windows.Foundation.Collections.h> +#include <vector> + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + struct ConfigurationUnit : ConfigurationUnitT<ConfigurationUnit> + { + ConfigurationUnit(); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + ConfigurationUnit(const guid& instanceIdentifier); +#endif + + hstring UnitName(); + void UnitName(const hstring& value); + + guid InstanceIdentifier(); + + hstring Identifier(); + void Identifier(const hstring& value); + + ConfigurationUnitIntent Intent(); + void Intent(ConfigurationUnitIntent value); + + Windows::Foundation::Collections::IVectorView<hstring> Dependencies(); + void Dependencies(const Windows::Foundation::Collections::IVectorView<hstring>& value); + + Windows::Foundation::Collections::ValueSet Directives(); + + Windows::Foundation::Collections::ValueSet Settings(); + + IConfigurationUnitProcessorDetails Details(); + + ConfigurationUnitState State(); + + ConfigurationUnitResultInformation ResultInformation(); + + bool ShouldApply(); + void ShouldApply(bool value); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + void Dependencies(std::vector<hstring>&& value); + void Details(IConfigurationUnitProcessorDetails&& details); + + private: + hstring m_unitName; + guid m_instanceIdentifier; + hstring m_identifier; + ConfigurationUnitIntent m_intent = ConfigurationUnitIntent::Apply; + Windows::Foundation::Collections::IVector<hstring> m_dependencies{ winrt::single_threaded_vector<hstring>() }; + Windows::Foundation::Collections::ValueSet m_directives; + Windows::Foundation::Collections::ValueSet m_settings; + IConfigurationUnitProcessorDetails m_details{ nullptr }; + bool m_shouldApply = true; + + MutableFlag m_mutableFlag; +#endif + }; +} + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) +namespace winrt::Microsoft::Management::Configuration::factory_implementation +{ + struct ConfigurationUnit : ConfigurationUnitT<ConfigurationUnit, implementation::ConfigurationUnit> + { + }; +} +#endif+ \ No newline at end of file diff --git a/src/Microsoft.Management.Configuration/ConfigurationUnitResultInformation.cpp b/src/Microsoft.Management.Configuration/ConfigurationUnitResultInformation.cpp @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "ConfigurationUnitResultInformation.h" +#include "ConfigurationUnitResultInformation.g.cpp" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + void ConfigurationUnitResultInformation::Initialize(const Configuration::ConfigurationUnitResultInformation& other) + { + m_resultCode = other.ResultCode(); + m_description = other.Description(); + } + + void ConfigurationUnitResultInformation::Initialize(hresult resultCode, std::wstring_view description) + { + m_resultCode = resultCode; + m_description = description; + } + + void ConfigurationUnitResultInformation::Initialize(hresult resultCode, hstring description) + { + m_resultCode = resultCode; + m_description = description; + } + + hresult ConfigurationUnitResultInformation::ResultCode() + { + return m_resultCode; + } + + void ConfigurationUnitResultInformation::ResultCode(hresult resultCode) + { + m_resultCode = resultCode; + } + + hstring ConfigurationUnitResultInformation::Description() + { + return m_description; + } + + void ConfigurationUnitResultInformation::Description(hstring value) + { + m_description = value; + } +} diff --git a/src/Microsoft.Management.Configuration/ConfigurationUnitResultInformation.h b/src/Microsoft.Management.Configuration/ConfigurationUnitResultInformation.h @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "ConfigurationUnitResultInformation.g.h" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + struct ConfigurationUnitResultInformation : ConfigurationUnitResultInformationT<ConfigurationUnitResultInformation> + { + ConfigurationUnitResultInformation() = default; + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + void Initialize(const Configuration::ConfigurationUnitResultInformation& other); + void Initialize(hresult resultCode, std::wstring_view description); + void Initialize(hresult resultCode, hstring description); +#endif + + hresult ResultCode(); + void ResultCode(hresult resultCode); + + hstring Description(); + void Description(hstring value); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + private: + hresult m_resultCode; + hstring m_description; +#endif + }; +} diff --git a/src/Microsoft.Management.Configuration/DiagnosticInformation.cpp b/src/Microsoft.Management.Configuration/DiagnosticInformation.cpp @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "DiagnosticInformation.h" +#include "DiagnosticInformation.g.cpp" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + void DiagnosticInformation::Initialize(DiagnosticLevel level, std::wstring_view message) + { + m_level = level; + m_message = message; + } + + DiagnosticLevel DiagnosticInformation::Level() + { + return m_level; + } + + hstring DiagnosticInformation::Message() + { + return m_message; + } +} diff --git a/src/Microsoft.Management.Configuration/DiagnosticInformation.h b/src/Microsoft.Management.Configuration/DiagnosticInformation.h @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "DiagnosticInformation.g.h" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + struct DiagnosticInformation : DiagnosticInformationT<DiagnosticInformation> + { + DiagnosticInformation() = default; + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + void Initialize(DiagnosticLevel level, std::wstring_view message); +#endif + + DiagnosticLevel Level(); + hstring Message(); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + private: + DiagnosticLevel m_level = DiagnosticLevel::Verbose; + hstring m_message; +#endif + }; +} diff --git a/src/Microsoft.Management.Configuration/ExceptionResultHelpers.h b/src/Microsoft.Management.Configuration/ExceptionResultHelpers.h @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include <exception> +#include <AppInstallerLogging.h> + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + template <typename UnitResult> + void ExtractUnitResultInformation(std::exception_ptr exc, const UnitResult& unitResult) + { + try + { + std::rethrow_exception(exc); + } + catch (const winrt::hresult_error& hre) + { + unitResult->Initialize(hre.code(), hre.message()); + } + catch (...) + { + unitResult->Initialize(E_FAIL, hstring{}); + } + + AICLI_LOG(Config, Error, << "Unit Processor exception: " << AppInstaller::Logging::SetHRFormat << unitResult->ResultCode() << " [" << AppInstaller::Utility::ConvertToUTF8(unitResult->Description()) << "]"); + } +} diff --git a/src/Microsoft.Management.Configuration/GetConfigurationUnitSettingsResult.cpp b/src/Microsoft.Management.Configuration/GetConfigurationUnitSettingsResult.cpp @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "GetConfigurationUnitSettingsResult.h" +#include "GetConfigurationUnitSettingsResult.g.cpp" +#include "ConfigurationUnitResultInformation.h" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + GetConfigurationUnitSettingsResult::GetConfigurationUnitSettingsResult() : + m_resultInformation(*make_self<wil::details::module_count_wrapper<implementation::ConfigurationUnitResultInformation>>()) + { + } + + void GetConfigurationUnitSettingsResult::ResultInformation(const ConfigurationUnitResultInformation& resultInformation) + { + m_resultInformation = resultInformation; + } + + Configuration::ConfigurationUnitResultInformation GetConfigurationUnitSettingsResult::ResultInformation() + { + return m_resultInformation; + } + + Windows::Foundation::Collections::ValueSet GetConfigurationUnitSettingsResult::Settings() + { + return m_settings; + } + + void GetConfigurationUnitSettingsResult::Settings(Windows::Foundation::Collections::ValueSet&& value) + { + m_settings = std::move(value); + } +} diff --git a/src/Microsoft.Management.Configuration/GetConfigurationUnitSettingsResult.h b/src/Microsoft.Management.Configuration/GetConfigurationUnitSettingsResult.h @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "GetConfigurationUnitSettingsResult.g.h" +#include <winrt/Windows.Foundation.Collections.h> + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + struct GetConfigurationUnitSettingsResult : GetConfigurationUnitSettingsResultT<GetConfigurationUnitSettingsResult> + { + using ConfigurationUnitResultInformation = Configuration::ConfigurationUnitResultInformation; + + GetConfigurationUnitSettingsResult(); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + void ResultInformation(const ConfigurationUnitResultInformation& resultInformation); + void Settings(Windows::Foundation::Collections::ValueSet&& value); +#endif + + ConfigurationUnitResultInformation ResultInformation(); + Windows::Foundation::Collections::ValueSet Settings(); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + private: + ConfigurationUnitResultInformation m_resultInformation; + Windows::Foundation::Collections::ValueSet m_settings; +#endif + }; +} diff --git a/src/Microsoft.Management.Configuration/GetSettingsResult.cpp b/src/Microsoft.Management.Configuration/GetSettingsResult.cpp @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "GetSettingsResult.h" +#include "GetSettingsResult.g.cpp" +#include "ConfigurationUnitResultInformation.h" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + GetSettingsResult::GetSettingsResult() : + m_resultInformation(*make_self<wil::details::module_count_wrapper<implementation::ConfigurationUnitResultInformation>>()) + { + } + + Windows::Foundation::Collections::ValueSet GetSettingsResult::Settings() + { + return m_settings; + } + + void GetSettingsResult::Settings(Windows::Foundation::Collections::ValueSet value) + { + m_settings = std::move(value); + } + + Configuration::ConfigurationUnitResultInformation GetSettingsResult::ResultInformation() + { + return m_resultInformation; + } +} diff --git a/src/Microsoft.Management.Configuration/GetSettingsResult.h b/src/Microsoft.Management.Configuration/GetSettingsResult.h @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "GetSettingsResult.g.h" +#include "winrt/Windows.Foundation.Collections.h" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + struct GetSettingsResult : GetSettingsResultT<GetSettingsResult> + { + GetSettingsResult(); + + Windows::Foundation::Collections::ValueSet Settings(); + void Settings(Windows::Foundation::Collections::ValueSet value); + + Configuration::ConfigurationUnitResultInformation ResultInformation(); + + private: + Windows::Foundation::Collections::ValueSet m_settings = nullptr; + Configuration::ConfigurationUnitResultInformation m_resultInformation; + }; +} +namespace winrt::Microsoft::Management::Configuration::factory_implementation +{ + struct GetSettingsResult : GetSettingsResultT<GetSettingsResult, implementation::GetSettingsResult> + { + }; +} diff --git a/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.idl b/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.idl @@ -0,0 +1,616 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +namespace Microsoft.Management.Configuration +{ + [contractversion(1)] + apicontract Contract{}; + + // The current state of a configuration set. + [contract(Microsoft.Management.Configuration.Contract, 1)] + enum ConfigurationSetState + { + // The state of the configuration set is unknown. + Unknown, + // The configuration set is in the queue to be applied. + Pending, + // The configuration set is actively being applied. + InProgress, + // The configuration set has completed being applied. + Completed, + }; + + // The current state of a configuration unit. + [contract(Microsoft.Management.Configuration.Contract, 1)] + enum ConfigurationUnitState + { + // The state of the configuration unit is unknown. + Unknown, + // The configuration unit is in the queue to be applied. + Pending, + // The configuration unit is actively being applied. + InProgress, + // The configuration unit has completed being applied. + Completed, + // The configuration unit was not applied due to external factors. + Skipped, + }; + + // Defines the level of detail probing that is allowed about a configuration unit. + [contract(Microsoft.Management.Configuration.Contract, 1)] + enum ConfigurationUnitDetailLevel + { + // Only reads details from local data. + Local, + // Will query the catalog information for details, but will not download any modules. + Catalog, + // Will download modules, but not load them. + Download, + // Will download and load modules for details. + Load, + }; + + // Information on a result for a single unit of configuration. + [contract(Microsoft.Management.Configuration.Contract, 1)] + runtimeclass ConfigurationUnitResultInformation + { + // The error code of the failure. + HRESULT ResultCode; + + // The description of the failure. + String Description; + } + + // Provides information for a specific configuration unit setting. + [contract(Microsoft.Management.Configuration.Contract, 1)] + interface IConfigurationUnitSettingDetails + { + // The name of the setting. + String Name{ get; }; + // The description of the setting. + String Description{ get; }; + // Whether the setting is a key. This is used to determine if different settings are in conflict. + Boolean IsKey{ get; }; + // Whether a non-empty value for the setting is required. + Boolean IsRequired{ get; }; + // Whether the setting should be serialized in order to be applied on another system. + // When the current settings are retrieved from the system, this can be used to exclude settings that are not relevant to a future application of the unit of configuration. + Boolean IsInformational{ get; }; + // The data type for the value of this setting. + Windows.Foundation.PropertyType Type{ get; }; + // The semantics to be used for this setting. + // The goal is to enable richer conflict detection and authoring scenarios by having a deeper understanding of this value than "String". + // TODO: Create the actual semantics of the semantics value... + String Semantics{ get; }; + } + + // Provides information for a specific configuration unit within the runtime. + [contract(Microsoft.Management.Configuration.Contract, 1)] + interface IConfigurationUnitProcessorDetails + { + // The name of the unit of configuration. + String UnitName{ get; }; + // A description of the unit of configuration. + String UnitDescription{ get; }; + // The URI of the documentation for the unit of configuration. + Windows.Foundation.Uri UnitDocumentationUri{ get; }; + // The URI of the icon for the unit of configuration. + Windows.Foundation.Uri UnitIconUri{ get; }; + // The name of the module containing the unit of configuration. + String ModuleName{ get; }; + // The type of the module containing the unit of configuration. + String ModuleType{ get; }; + // The source of the module containing the unit of configuration. + String ModuleSource{ get; }; + // The description of the module containing the unit of configuration. + String ModuleDescription{ get; }; + // The URI of the documentation for the module containing the unit of configuration. + Windows.Foundation.Uri ModuleDocumentationUri{ get; }; + // The URI for the published module containing the unit of configuration. + Windows.Foundation.Uri PublishedModuleUri{ get; }; + // The version of the module containing the unit of configuration. + String Version{ get; }; + // The publishing date of the module containing the unit of configuration. + Windows.Foundation.DateTime PublishedDate{ get; }; + // Whether the module is already present on the system. + Boolean IsLocal{ get; }; + // The author of the module containing the unit of configuration. + String Author{ get; }; + // The publisher of the module containing the unit of configuration. + String Publisher{ get; }; + // The signing certificate chain of the module containing the unit of configuration. + Windows.Security.Cryptography.Certificates.CertificateChain SigningCertificateChain{ get; }; + // The settings information for the unit of configuration. + Windows.Foundation.Collections.IVectorView<IConfigurationUnitSettingDetails> Settings{ get; }; + } + + // Defines how the configuration unit is to be used within the configuration system. + [contract(Microsoft.Management.Configuration.Contract, 1)] + enum ConfigurationUnitIntent + { + // The configuration unit will only be used to Test the current system state. + Assert, + // The configuration unit will only be used to Get the current system state. + Inform, + // The configuration unit will be used to Apply the current system state. + // The configuration unit will be used to Test and Get the current system state as part of that process. + Apply, + }; + + // A single unit of configuration. + [contract(Microsoft.Management.Configuration.Contract, 1)] + runtimeclass ConfigurationUnit + { + // Creates an empty configuration unit for authoring purposes. + ConfigurationUnit(); + + // The name of the unit being configured; not a name for this instance. + // Can be changed if a mutable unit. + String UnitName; + + // An identifier used to uniquely identify the instance of a configuration unit on the system. + // Immutable, although the unit can be removed and an identical unit applied later with a different identifier. + Guid InstanceIdentifier{ get; }; + + // The identifier name of this instance within the set. + // Can be changed if a mutable unit. + String Identifier; + + // Describes how this configuration unit will be used. + // Can be changed if a mutable unit. + ConfigurationUnitIntent Intent; + + // The configuration units that this unit depends on. + // Can be set on a mutable configuration unit; this will copy the items into the internal storage. + Windows.Foundation.Collections.IVectorView<String> Dependencies; + + // Contains the values that are for use by the configuration system, related to this unit. + Windows.Foundation.Collections.ValueSet Directives{ get; }; + + // Contains the values that are for use by the configuration unit itself. + Windows.Foundation.Collections.ValueSet Settings{ get; }; + + // Contains information on the origin of the configuration unit. + // May be null if ConfigurationProcessor.GetDetailsAsync has not been called yet. + IConfigurationUnitProcessorDetails Details{ get; }; + + // The current state of the configuration unit. + ConfigurationUnitState State{ get; }; + + // Contains information on the result of the latest attempt to apply the configuration unit. + ConfigurationUnitResultInformation ResultInformation{ get; }; + + // Allows for control over whether this unit should be applied when the set containing it is applied. + Boolean ShouldApply; + } + + // The change event type that has occurred for a configuration set change. + [contract(Microsoft.Management.Configuration.Contract, 1)] + enum ConfigurationSetChangeEventType + { + Unknown, + // The change event was for the set state. Only ConfigurationSetChangeData.SetState is valid. + SetStateChanged, + // The change event was for the unit state. All ConfigurationSetChangeData properties are valid. + UnitStateChanged, + }; + + // The change data sent about changes to a specific set. + [contract(Microsoft.Management.Configuration.Contract, 1)] + runtimeclass ConfigurationSetChangeData + { + // The change event type that occurred. + ConfigurationSetChangeEventType Change{ get; }; + + // The state of the configuration set for this event (the ConfigurationSet can be used to get the current state, which may be different). + ConfigurationSetState SetState{ get; }; + + // The state of the configuration unit for this event (the ConfigurationUnit can be used to get the current state, which may be different). + ConfigurationUnitState UnitState{ get; }; + + // Contains information on the result of the attempt to apply the configuration unit. + ConfigurationUnitResultInformation ResultInformation{ get; }; + + // The configuration unit whose state changed. + ConfigurationUnit Unit{ get; }; + } + + // A configuration set contains a collection of configuration units and details about the set. + [contract(Microsoft.Management.Configuration.Contract, 1)] + runtimeclass ConfigurationSet + { + // Creates an empty, mutable configuration set for authoring purposes. + ConfigurationSet(); + + // The name of the set; if from a file this could be the file name. + // Can be changed if a mutable set. + String Name; + // The origin of the set; if it came from a repository it could be the remote URL (ex. https://github.com/microsoft/winget-cli.git). + // Can be changed if a mutable set. + String Origin; + // The location of the configuration set on the local filesystem. + // If this set is from history, the file may no longer exist or it's contents may have been changed. + // Can be changed if a mutable set. + String Path; + + // An identifier used to uniquely identify the instance of a configuration set on the system. + // Immutable, although the set can be removed and an identical set applied later with a different identifier. + Guid InstanceIdentifier{ get; }; + // The state that the set is in. + ConfigurationSetState State{ get; }; + // The time that this set was recorded with intent to apply. + Windows.Foundation.DateTime FirstApply{ get; }; + // The time that this set was last started to be applied. + Windows.Foundation.DateTime ApplyBegun{ get; }; + // The time that this set was last finished being applied (does not indicate success). + Windows.Foundation.DateTime ApplyEnded{ get; }; + + // The configuration units that are part of this set. + // Can be set on a mutable configuration set; this will copy the items into the internal storage. + Windows.Foundation.Collections.IVectorView<ConfigurationUnit> ConfigurationUnits; + + // Only changes for this set are sent to this event. + // This includes things like: start/stop of the entire set for application or test, start/stop of a unit for application or test. + event Windows.Foundation.TypedEventHandler<ConfigurationSet, ConfigurationSetChangeData> ConfigurationSetChange; + + // Writes the configuration set to the given stream. + void Serialize(Windows.Storage.Streams.IOutputStream stream); + + // Removes the configuration set from the recorded history, if present. + void Remove(); + } + + // The result of applying the settings with an IConfigurationUnitProcessor. + [contract(Microsoft.Management.Configuration.Contract, 1)] + runtimeclass ApplySettingsResult + { + ApplySettingsResult(); + + // Indicates whether a reboot is required after the settings were applied. + Boolean RebootRequired; + + // The result of applying the configuration unit. + ConfigurationUnitResultInformation ResultInformation{ get; }; + } + + // Informs the caller of the result of running a Test. + [contract(Microsoft.Management.Configuration.Contract, 1)] + enum ConfigurationTestResult + { + // The result is unknown. + Unknown, + // The system is in the state described by the configuration. + Positive, + // The system is not in the state described by the configuration. + Negative, + // Running the test failed. + Failed, + // The test was not run because it was not applicable. + NotRun, + }; + + // The result of testing the settings with an IConfigurationUnitProcessor. + [contract(Microsoft.Management.Configuration.Contract, 1)] + runtimeclass TestSettingsResult + { + TestSettingsResult(); + + // The result (if any) of running Test on the configuration unit. + ConfigurationTestResult TestResult; + + // The result of testing the configuration unit. + // This is not the response for the test, but rather contains information about the actual attempt to run the test. + ConfigurationUnitResultInformation ResultInformation{ get; }; + } + + // The result of getting the settings with an IConfigurationUnitProcessor. + [contract(Microsoft.Management.Configuration.Contract, 1)] + runtimeclass GetSettingsResult + { + GetSettingsResult(); + + // The current state of the system for the configuration unit. + Windows.Foundation.Collections.ValueSet Settings; + + // The result of getting the configuration unit settings. + // This is not the response for the retrieval, but rather contains information about the actual attempt to retrieve the settings. + ConfigurationUnitResultInformation ResultInformation{ get; }; + } + + // Provides access to a specific configuration unit within the runtime. + [contract(Microsoft.Management.Configuration.Contract, 1)] + interface IConfigurationUnitProcessor + { + // The configuration unit that the processor was created for. + ConfigurationUnit Unit{ get; }; + + // The directives overlay that the processor was created with. + Windows.Foundation.Collections.IMapView<String, Object> DirectivesOverlay{ get; }; + + // Determines if the system is already in the state described by the configuration unit. + TestSettingsResult TestSettings(); + + // Gets the current system state for the configuration unit. + GetSettingsResult GetSettings(); + + // Applies the state described in the configuration unit. + ApplySettingsResult ApplySettings(); + } + + // Controls the lifetime of operations for a single configuration set. + [contract(Microsoft.Management.Configuration.Contract, 1)] + interface IConfigurationSetProcessor + { + // Gets the configuration unit processor details for the given unit. + IConfigurationUnitProcessorDetails GetUnitProcessorDetails(ConfigurationUnit unit, ConfigurationUnitDetailLevel detailLevel); + + // Creates a configuration unit processor for the given unit. + // The optional `directivesOverlay` parameter allows for the `ConfigurationProcessor` to alter behavior without needing to change the unit itself. + // The overlay is searched first; any value not present there will then fall back to the unit's directives. + IConfigurationUnitProcessor CreateUnitProcessor(ConfigurationUnit unit, Windows.Foundation.Collections.IMapView<String, Object> directivesOverlay); + } + + // Allows different runtimes to provide specialized handling of configuration processing. + [contract(Microsoft.Management.Configuration.Contract, 1)] + interface IConfigurationProcessorFactory + { + // Creates a configuration set processor for the given set. + IConfigurationSetProcessor CreateSetProcessor(ConfigurationSet configurationSet); + } + + // The level of the diagnostic information. + [contract(Microsoft.Management.Configuration.Contract, 1)] + enum DiagnosticLevel + { + Verbose, + Informational, + Warning, + Error, + Critical, + }; + + // Enables diagnostic information from the configuration system to be inspected/stored by callers. + [contract(Microsoft.Management.Configuration.Contract, 1)] + runtimeclass DiagnosticInformation + { + // Indicates the importance of the diagnostic information. + DiagnosticLevel Level{ get; }; + + // The diagnostic message. + String Message{ get; }; + } + + // The change event type that has occurred. + [contract(Microsoft.Management.Configuration.Contract, 1)] + enum ConfigurationChangeEventType + { + Unknown, + SetAdded, + SetStateChanged, + SetRemoved, + }; + + // The change data sent about changes to sets. + [contract(Microsoft.Management.Configuration.Contract, 1)] + runtimeclass ConfigurationChangeData + { + // The change event type that occurred. + ConfigurationChangeEventType Change{ get; }; + + // The identifier used to uniquely identify the instance of a configuration set on the system. + Guid InstanceIdentifier{ get; }; + + // The state of the configuration set for this event (the ConfigurationSet can be used to get the current state, which may be different). + ConfigurationSetState State{ get; }; + } + + // The result of calling OpenConfigurationSet, containing either the set or details about the failure. + [contract(Microsoft.Management.Configuration.Contract, 1)] + runtimeclass OpenConfigurationSetResult + { + // The configuration set if successful; null otherwise. + ConfigurationSet Set{ get; }; + + // The result from opening the set. + HRESULT ResultCode{ get; }; + + // The field that is missing/invalid, if appropriate for the specific ResultCode. + String Field{ get; }; + } + + // The type of conflict between configuration sets that was detected. + [contract(Microsoft.Management.Configuration.Contract, 1)] + enum ConfigurationConflictType + { + Unknown, + // Indicates that the first configuration set has a matching name and origin to the second, which has already been applied. + // This is likely an update to the existing set, and should be applied as such, rather than an entirely new set. + MatchingOrigin, + // Indicates that the first configuration set is identical to the second, which has already been applied. + // This is based solely on the configuration unit settings. + IdenticalSetApplied, + // Indicates a conflict between the settings of two configuration units. + SettingsConflict, + }; + + // Describes a conflict between a setting of two configuration units. + [contract(Microsoft.Management.Configuration.Contract, 1)] + runtimeclass ConfigurationConflictSetting + { + // The name of the setting. + String Name{ get; }; + + // The value from the first configuration unit. + // These are the values from a `ValueSet`, and are thus required to be a `PropertyValue` or a 'ValueSet`. + Object FirstValue{ get; }; + + // The value from the second configuration unit. + // These are the values from a `ValueSet`, and are thus required to be a `PropertyValue` or a 'ValueSet`. + Object SecondValue{ get; }; + } + + // Describes a conflict between two configuration sets. + [contract(Microsoft.Management.Configuration.Contract, 1)] + runtimeclass ConfigurationConflict + { + // The type of conflict detected. + ConfigurationConflictType Conflict{ get; }; + + // The first of the configuration sets involved in the conflict. + ConfigurationSet FirstSet{ get; }; + + // The second of the configuration sets involved in the conflict. + ConfigurationSet SecondSet{ get; }; + + // The first of the configuration units involved in the conflict. + ConfigurationUnit FirstUnit{ get; }; + + // The second of the configuration units involved in the conflict. + ConfigurationUnit SecondUnit{ get; }; + + // Contains information about the particular settings that are conflicting. + Windows.Foundation.Collections.IVectorView<ConfigurationConflictSetting> Settings{ get; }; + } + + // Flags to control how a configuration set should be applied to the system. + [contract(Microsoft.Management.Configuration.Contract, 1)] + enum ApplyConfigurationSetFlags + { + None = 0x0, + // Forces a new configuration set instance to be recorded when the set being applied matches a previous set's origin. + // The default behavior is to assume that the incoming set is an update to the existing set and overwrite it. + DoNotOverwriteMatchingOriginSet = 0x1, + }; + + // The result of applying the settings for a configuration unit. + [contract(Microsoft.Management.Configuration.Contract, 1)] + runtimeclass ApplyConfigurationUnitResult + { + // The configuration unit that was applied. + ConfigurationUnit Unit{ get; }; + + // The state of the configuration unit with regards to the current execution of ApplySet. + ConfigurationUnitState State{ get; }; + + // Will be true if the configuration unit was in the desired state (Test returns true) prior to the apply action. + Boolean PreviouslyInDesiredState{ get; }; + + // Indicates whether a reboot is required after the configuration unit was applied. + Boolean RebootRequired{ get; }; + + // The result of applying the configuration unit. + ConfigurationUnitResultInformation ResultInformation{ get; }; + } + + // The result of applying the settings for a configuration set. + [contract(Microsoft.Management.Configuration.Contract, 1)] + runtimeclass ApplyConfigurationSetResult + { + // Results for each configuration unit in the set. + Windows.Foundation.Collections.IVectorView<ApplyConfigurationUnitResult> UnitResults{ get; }; + + // The overall result from applying the configuration set. + HRESULT ResultCode{ get; }; + } + + // The result of testing the settings for a configuration unit. + [contract(Microsoft.Management.Configuration.Contract, 1)] + runtimeclass TestConfigurationUnitResult + { + // The configuration unit that was tested. + ConfigurationUnit Unit{ get; }; + + // The result of testing the configuration unit. + // This is not the response for the test, but rather contains information about the actual attempt to run the test. + ConfigurationUnitResultInformation ResultInformation{ get; }; + + // The result (if any) of running Test on the configuration unit. + ConfigurationTestResult TestResult{ get; }; + } + + // The result of testing the settings for a configuration set. + [contract(Microsoft.Management.Configuration.Contract, 1)] + runtimeclass TestConfigurationSetResult + { + // Results for each configuration unit in the set. + Windows.Foundation.Collections.IVectorView<TestConfigurationUnitResult> UnitResults{ get; }; + + // The result (if any) of running Test on the configuration set. + // If this value is NotRun, every unit result will be NotRun. + // If this value is Positive, every unit result will be Positive (or NotRun with at least one being Positive). + // Any Negative result for a unit will result in this value being Negative. + // Any Failed result for a unit will result in this value being Failed (overriding the Negative statement above). + ConfigurationTestResult TestResult{ get; }; + } + + // The result of getting the settings for a configuration unit. + [contract(Microsoft.Management.Configuration.Contract, 1)] + runtimeclass GetConfigurationUnitSettingsResult + { + // The result of getting the configuration unit settings. + // This is not the response for the retrieval, but rather contains information about the actual attempt to retrieve the settings. + ConfigurationUnitResultInformation ResultInformation{ get; }; + + // The current state of the system for the configuration unit. + Windows.Foundation.Collections.ValueSet Settings { get; }; + } + + // The configuration processor is responsible for the interactions with the system. + [contract(Microsoft.Management.Configuration.Contract, 1)] + runtimeclass ConfigurationProcessor + { + ConfigurationProcessor(IConfigurationProcessorFactory factory); + + // Diagnostics event; useful for logging and/or verbose output. + event Windows.Foundation.EventHandler<DiagnosticInformation> Diagnostics; + + // Only top level configuration changes are sent to this event. + // This includes things like: creation of a new set for intent to run, start/stop of a set for application or test, deletion of a not started set. + event Windows.Foundation.TypedEventHandler<ConfigurationSet, ConfigurationChangeData> ConfigurationChange; + + // Gets the configuration sets that have already been applied or the intent to be applied (this may include in progress sets or those that are waiting on others). + // These configuration sets will be marked as immutable. + Windows.Foundation.Collections.IVector<ConfigurationSet> GetConfigurationHistory(); + Windows.Foundation.IAsyncOperation< Windows.Foundation.Collections.IVector<ConfigurationSet> > GetConfigurationHistoryAsync(); + + // Loads a mutable configuration set from the given stream. + OpenConfigurationSetResult OpenConfigurationSet(Windows.Storage.Streams.IInputStream stream); + Windows.Foundation.IAsyncOperation<OpenConfigurationSetResult> OpenConfigurationSetAsync(Windows.Storage.Streams.IInputStream stream); + + // Checks for conflicts amongst the configuration sets provided, optionally including the configuration sets already applied to the system. + Windows.Foundation.Collections.IVector<ConfigurationConflict> CheckForConflicts(Windows.Foundation.Collections.IVectorView<ConfigurationSet> configurationSets, Boolean includeConfigurationHistory); + Windows.Foundation.IAsyncOperation< Windows.Foundation.Collections.IVector<ConfigurationConflict> > CheckForConflictsAsync(Windows.Foundation.Collections.IVectorView<ConfigurationSet> configurationSets, Boolean includeConfigurationHistory); + + // Gets the details for all configuration units in a set. + void GetSetDetails(ConfigurationSet configurationSet, ConfigurationUnitDetailLevel detailLevel); + Windows.Foundation.IAsyncAction GetSetDetailsAsync(ConfigurationSet configurationSet, ConfigurationUnitDetailLevel detailLevel); + + // Gets the details for a configuration unit. + void GetUnitDetails(ConfigurationUnit unit, ConfigurationUnitDetailLevel detailLevel); + Windows.Foundation.IAsyncAction GetUnitDetailsAsync(ConfigurationUnit unit, ConfigurationUnitDetailLevel detailLevel); + + // Applies the configuration set state. + ApplyConfigurationSetResult ApplySet(ConfigurationSet configurationSet, ApplyConfigurationSetFlags flags); + Windows.Foundation.IAsyncOperationWithProgress<ApplyConfigurationSetResult, ConfigurationSetChangeData> ApplySetAsync(ConfigurationSet configurationSet, ApplyConfigurationSetFlags flags); + + // Tests the configuration set state. + TestConfigurationSetResult TestSet(ConfigurationSet configurationSet); + Windows.Foundation.IAsyncOperationWithProgress<TestConfigurationSetResult, TestConfigurationUnitResult> TestSetAsync(ConfigurationSet configurationSet); + + // Gets the current configuration unit settings. + GetConfigurationUnitSettingsResult GetUnitSettings(ConfigurationUnit unit); + Windows.Foundation.IAsyncOperation<GetConfigurationUnitSettingsResult> GetUnitSettingsAsync(ConfigurationUnit unit); + } + + /// Force midl3 to generate vector marshalling info. + declare + { + interface Windows.Foundation.Collections.IVector<ConfigurationConflict>; + interface Windows.Foundation.Collections.IVector<ConfigurationSet>; + interface Windows.Foundation.Collections.IVectorView<ApplyConfigurationUnitResult>; + interface Windows.Foundation.Collections.IVectorView<ConfigurationConflictSetting>; + interface Windows.Foundation.Collections.IVectorView<ConfigurationSet>; + interface Windows.Foundation.Collections.IVectorView<ConfigurationUnit>; + interface Windows.Foundation.Collections.IVectorView<IConfigurationUnitSettingDetails>; + interface Windows.Foundation.Collections.IVectorView<TestConfigurationUnitResult>; + } +} diff --git a/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj b/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj @@ -0,0 +1,223 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.210505.3\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.210505.3\build\native\Microsoft.Windows.CppWinRT.props')" /> + <PropertyGroup Label="Globals"> + <CppWinRTOptimized>true</CppWinRTOptimized> + <CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge> + <CppWinRTGenerateWindowsMetadata>true</CppWinRTGenerateWindowsMetadata> + <MinimalCoreWin>true</MinimalCoreWin> + <ProjectGuid>{CA460806-5E41-4E97-9A3D-1D74B433B663}</ProjectGuid> + <ProjectName>Microsoft.Management.Configuration</ProjectName> + <RootNamespace>Microsoft.Management.Configuration</RootNamespace> + <DefaultLanguage>en-US</DefaultLanguage> + <MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion> + <ApplicationTypeRevision>10.0</ApplicationTypeRevision> + <WindowsTargetPlatformVersion>10.0.22000.0</WindowsTargetPlatformVersion> + <WindowsTargetPlatformMinVersion>10.0.17763.0</WindowsTargetPlatformMinVersion> + <WindowsSDKDesktopARMSupport>true</WindowsSDKDesktopARMSupport> + <WindowsSDKDesktopARM64Support>true</WindowsSDKDesktopARM64Support> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|ARM"> + <Configuration>Debug</Configuration> + <Platform>ARM</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|ARM64"> + <Configuration>Debug</Configuration> + <Platform>ARM64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|ARM"> + <Configuration>Release</Configuration> + <Platform>ARM</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|ARM64"> + <Configuration>Release</Configuration> + <Platform>ARM64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <PlatformToolset>v140</PlatformToolset> + <PlatformToolset Condition="'$(VisualStudioVersion)' == '15.0'">v141</PlatformToolset> + <PlatformToolset Condition="'$(VisualStudioVersion)' == '16.0'">v142</PlatformToolset> + <PlatformToolset Condition="'$(VisualStudioVersion)' == '17.0'">v143</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + <GenerateManifest>false</GenerateManifest> + <DesktopCompatible>true</DesktopCompatible> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration"> + <UseDebugLibraries>true</UseDebugLibraries> + <LinkIncremental>true</LinkIncremental> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration"> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <LinkIncremental>false</LinkIncremental> + <SpectreMitigation>Spectre</SpectreMitigation> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="PropertySheet.props" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup> + <IncludePath>$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath> + <OutDir>$(SolutionDir)$(PlatformTarget)\$(Configuration)\$(ProjectName)\</OutDir> + <IntDir>$(PlatformTarget)\$(Configuration)\</IntDir> + <TargetName>$(RootNamespace)</TargetName> + <CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors> + <RunCodeAnalysis>true</RunCodeAnalysis> + <CodeAnalysisRuleSet>..\CodeAnalysis.ruleset</CodeAnalysisRuleSet> + </PropertyGroup> + <ItemDefinitionGroup> + <ClCompile> + <PrecompiledHeader>Use</PrecompiledHeader> + <PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile> + <PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile> + <WarningLevel>Level4</WarningLevel> + <AdditionalOptions>%(AdditionalOptions) /bigobj</AdditionalOptions> + <TreatWarningAsError>true</TreatWarningAsError> + <SDLCheck>true</SDLCheck> + <PreprocessorDefinitions>_WINRT_DLL;WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories> + <AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)..\AppInstallerSharedLib\Public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateWindowsMetadata>false</GenerateWindowsMetadata> + <ModuleDefinitionFile>Microsoft_Management_Configuration.def</ModuleDefinitionFile> + <WindowsMetadataFile>$(OutDir)$(ProjectName).winmd</WindowsMetadataFile> + <AdditionalDependencies>Advapi32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'"> + <ClCompile> + <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ProgramDataBaseFileName>$(OutDir)$(TargetName)Debug.pdb</ProgramDataBaseFileName> + <RuntimeTypeInfo Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">false</RuntimeTypeInfo> + <RuntimeTypeInfo Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">false</RuntimeTypeInfo> + <RuntimeTypeInfo Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</RuntimeTypeInfo> + <RuntimeTypeInfo Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</RuntimeTypeInfo> + </ClCompile> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)'=='Release'"> + <ClCompile> + <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeTypeInfo Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">false</RuntimeTypeInfo> + <RuntimeTypeInfo Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">false</RuntimeTypeInfo> + <RuntimeTypeInfo Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</RuntimeTypeInfo> + <RuntimeTypeInfo Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</RuntimeTypeInfo> + </ClCompile> + <Link> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClInclude Include="ApplyConfigurationSetResult.h" /> + <ClInclude Include="ApplyConfigurationUnitResult.h" /> + <ClInclude Include="ApplySettingsResult.h" /> + <ClInclude Include="ConfigThreadGlobals.h" /> + <ClInclude Include="ConfigurationChangeData.h" /> + <ClInclude Include="ConfigurationConflict.h" /> + <ClInclude Include="ConfigurationConflictSetting.h" /> + <ClInclude Include="ConfigurationProcessor.h" /> + <ClInclude Include="ConfigurationSet.h" /> + <ClInclude Include="ConfigurationSetApplyProcessor.h" /> + <ClInclude Include="ConfigurationSetChangeData.h" /> + <ClInclude Include="ConfigurationSetParser.h" /> + <ClInclude Include="ConfigurationSetParserError.h" /> + <ClInclude Include="ConfigurationSetParser_0_1.h" /> + <ClInclude Include="ConfigurationUnit.h" /> + <ClInclude Include="ConfigurationUnitResultInformation.h" /> + <ClInclude Include="DiagnosticInformation.h" /> + <ClInclude Include="ExceptionResultHelpers.h" /> + <ClInclude Include="GetConfigurationUnitSettingsResult.h" /> + <ClInclude Include="GetSettingsResult.h" /> + <ClInclude Include="MutableFlag.h" /> + <ClInclude Include="OpenConfigurationSetResult.h" /> + <ClInclude Include="pch.h" /> + <ClInclude Include="TestConfigurationSetResult.h" /> + <ClInclude Include="TestConfigurationUnitResult.h" /> + <ClInclude Include="TestSettingsResult.h" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="ApplyConfigurationSetResult.cpp" /> + <ClCompile Include="ApplyConfigurationUnitResult.cpp" /> + <ClCompile Include="ApplySettingsResult.cpp" /> + <ClCompile Include="ConfigThreadGlobals.cpp" /> + <ClCompile Include="ConfigurationChangeData.cpp" /> + <ClCompile Include="ConfigurationConflict.cpp" /> + <ClCompile Include="ConfigurationConflictSetting.cpp" /> + <ClCompile Include="ConfigurationProcessor.cpp" /> + <ClCompile Include="ConfigurationSet.cpp" /> + <ClCompile Include="ConfigurationSetApplyProcessor.cpp" /> + <ClCompile Include="ConfigurationSetChangeData.cpp" /> + <ClCompile Include="ConfigurationSetParser.cpp" /> + <ClCompile Include="ConfigurationSetParser_0_1.cpp" /> + <ClCompile Include="ConfigurationUnit.cpp" /> + <ClCompile Include="ConfigurationUnitResultInformation.cpp" /> + <ClCompile Include="DiagnosticInformation.cpp" /> + <ClCompile Include="GetConfigurationUnitSettingsResult.cpp" /> + <ClCompile Include="GetSettingsResult.cpp" /> + <ClCompile Include="MutableFlag.cpp" /> + <ClCompile Include="OpenConfigurationSetResult.cpp" /> + <ClCompile Include="pch.cpp"> + <PrecompiledHeader>Create</PrecompiledHeader> + </ClCompile> + <ClCompile Include="$(GeneratedFilesDir)module.g.cpp" /> + <ClCompile Include="TestConfigurationSetResult.cpp" /> + <ClCompile Include="TestConfigurationUnitResult.cpp" /> + <ClCompile Include="TestSettingsResult.cpp" /> + </ItemGroup> + <ItemGroup> + <None Include="Microsoft_Management_Configuration.def" /> + </ItemGroup> + <ItemGroup> + <None Include="packages.config" /> + <None Include="PropertySheet.props" /> + </ItemGroup> + <ItemGroup> + <Midl Include="Microsoft.Management.Configuration.idl" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\AppInstallerSharedLib\AppInstallerSharedLib.vcxproj"> + <Project>{f3f6e699-bc5d-4950-8a05-e49dd9eb0d51}</Project> + </ProjectReference> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + <Import Project="$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.210505.3\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.210505.3\build\native\Microsoft.Windows.CppWinRT.targets')" /> + <Import Project="$(SolutionDir)\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('$(SolutionDir)\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" /> + </ImportGroup> + <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> + <PropertyGroup> + <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> + </PropertyGroup> + <Error Condition="!Exists('$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.210505.3\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.210505.3\build\native\Microsoft.Windows.CppWinRT.props'))" /> + <Error Condition="!Exists('$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.210505.3\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.210505.3\build\native\Microsoft.Windows.CppWinRT.targets'))" /> + <Error Condition="!Exists('$(SolutionDir)\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" /> + </Target> +</Project>+ \ No newline at end of file diff --git a/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj.filters b/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj.filters @@ -0,0 +1,176 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <ClCompile Include="pch.cpp" /> + <ClCompile Include="$(GeneratedFilesDir)module.g.cpp" /> + <ClCompile Include="ApplyConfigurationSetResult.cpp"> + <Filter>API Source</Filter> + </ClCompile> + <ClCompile Include="ApplyConfigurationUnitResult.cpp"> + <Filter>API Source</Filter> + </ClCompile> + <ClCompile Include="ConfigurationChangeData.cpp"> + <Filter>API Source</Filter> + </ClCompile> + <ClCompile Include="ConfigurationConflict.cpp"> + <Filter>API Source</Filter> + </ClCompile> + <ClCompile Include="ConfigurationConflictSetting.cpp"> + <Filter>API Source</Filter> + </ClCompile> + <ClCompile Include="ConfigurationSet.cpp"> + <Filter>API Source</Filter> + </ClCompile> + <ClCompile Include="ConfigurationSetChangeData.cpp"> + <Filter>API Source</Filter> + </ClCompile> + <ClCompile Include="ConfigurationUnit.cpp"> + <Filter>API Source</Filter> + </ClCompile> + <ClCompile Include="ConfigurationUnitResultInformation.cpp"> + <Filter>API Source</Filter> + </ClCompile> + <ClCompile Include="DiagnosticInformation.cpp"> + <Filter>API Source</Filter> + </ClCompile> + <ClCompile Include="GetConfigurationUnitSettingsResult.cpp"> + <Filter>API Source</Filter> + </ClCompile> + <ClCompile Include="MutableFlag.cpp"> + <Filter>Internals</Filter> + </ClCompile> + <ClCompile Include="TestConfigurationSetResult.cpp"> + <Filter>API Source</Filter> + </ClCompile> + <ClCompile Include="TestConfigurationUnitResult.cpp"> + <Filter>API Source</Filter> + </ClCompile> + <ClCompile Include="ConfigurationProcessor.cpp"> + <Filter>API Source</Filter> + </ClCompile> + <ClCompile Include="ConfigurationSetParser.cpp"> + <Filter>Parser</Filter> + </ClCompile> + <ClCompile Include="ConfigurationSetParser_0_1.cpp"> + <Filter>Parser</Filter> + </ClCompile> + <ClCompile Include="OpenConfigurationSetResult.cpp"> + <Filter>API Source</Filter> + </ClCompile> + <ClCompile Include="ConfigThreadGlobals.cpp"> + <Filter>Internals</Filter> + </ClCompile> + <ClCompile Include="ConfigurationSetApplyProcessor.cpp"> + <Filter>Internals</Filter> + </ClCompile> + <ClCompile Include="ApplySettingsResult.cpp"> + <Filter>API Source</Filter> + </ClCompile> + <ClCompile Include="GetSettingsResult.cpp"> + <Filter>API Source</Filter> + </ClCompile> + <ClCompile Include="TestSettingsResult.cpp"> + <Filter>API Source</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="pch.h" /> + <ClInclude Include="ApplyConfigurationSetResult.h"> + <Filter>API Headers</Filter> + </ClInclude> + <ClInclude Include="ApplyConfigurationUnitResult.h"> + <Filter>API Headers</Filter> + </ClInclude> + <ClInclude Include="ConfigurationChangeData.h"> + <Filter>API Headers</Filter> + </ClInclude> + <ClInclude Include="ConfigurationConflict.h"> + <Filter>API Headers</Filter> + </ClInclude> + <ClInclude Include="ConfigurationConflictSetting.h"> + <Filter>API Headers</Filter> + </ClInclude> + <ClInclude Include="ConfigurationSet.h"> + <Filter>API Headers</Filter> + </ClInclude> + <ClInclude Include="ConfigurationSetChangeData.h"> + <Filter>API Headers</Filter> + </ClInclude> + <ClInclude Include="ConfigurationUnit.h"> + <Filter>API Headers</Filter> + </ClInclude> + <ClInclude Include="ConfigurationUnitResultInformation.h"> + <Filter>API Headers</Filter> + </ClInclude> + <ClInclude Include="DiagnosticInformation.h"> + <Filter>API Headers</Filter> + </ClInclude> + <ClInclude Include="GetConfigurationUnitSettingsResult.h"> + <Filter>API Headers</Filter> + </ClInclude> + <ClInclude Include="MutableFlag.h"> + <Filter>Internals</Filter> + </ClInclude> + <ClInclude Include="TestConfigurationSetResult.h"> + <Filter>API Headers</Filter> + </ClInclude> + <ClInclude Include="TestConfigurationUnitResult.h"> + <Filter>API Headers</Filter> + </ClInclude> + <ClInclude Include="ConfigurationProcessor.h"> + <Filter>API Headers</Filter> + </ClInclude> + <ClInclude Include="ConfigurationSetParser.h"> + <Filter>Parser</Filter> + </ClInclude> + <ClInclude Include="ConfigurationSetParser_0_1.h"> + <Filter>Parser</Filter> + </ClInclude> + <ClInclude Include="OpenConfigurationSetResult.h"> + <Filter>API Headers</Filter> + </ClInclude> + <ClInclude Include="ConfigurationSetParserError.h"> + <Filter>Parser</Filter> + </ClInclude> + <ClInclude Include="ConfigThreadGlobals.h"> + <Filter>Internals</Filter> + </ClInclude> + <ClInclude Include="ConfigurationSetApplyProcessor.h"> + <Filter>Internals</Filter> + </ClInclude> + <ClInclude Include="ExceptionResultHelpers.h"> + <Filter>Internals</Filter> + </ClInclude> + <ClInclude Include="ApplySettingsResult.h"> + <Filter>API Headers</Filter> + </ClInclude> + <ClInclude Include="GetSettingsResult.h"> + <Filter>API Headers</Filter> + </ClInclude> + <ClInclude Include="TestSettingsResult.h"> + <Filter>API Headers</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <Midl Include="Microsoft.Management.Configuration.idl" /> + </ItemGroup> + <ItemGroup> + <None Include="Microsoft_Management_Configuration.def" /> + <None Include="packages.config" /> + <None Include="PropertySheet.props" /> + </ItemGroup> + <ItemGroup> + <Filter Include="API Headers"> + <UniqueIdentifier>{0826fc0e-120c-4e31-bde7-ce0a1278b1b1}</UniqueIdentifier> + </Filter> + <Filter Include="API Source"> + <UniqueIdentifier>{6dbfe76d-646d-43b0-b162-e1dedad10ab8}</UniqueIdentifier> + </Filter> + <Filter Include="Internals"> + <UniqueIdentifier>{c5f2f74e-de80-4235-abbd-bacd6771eaf2}</UniqueIdentifier> + </Filter> + <Filter Include="Parser"> + <UniqueIdentifier>{b31f8336-b4d8-4c05-b08d-6b82c550a30b}</UniqueIdentifier> + </Filter> + </ItemGroup> +</Project>+ \ No newline at end of file diff --git a/src/Microsoft.Management.Configuration/Microsoft_Management_Configuration.def b/src/Microsoft.Management.Configuration/Microsoft_Management_Configuration.def @@ -0,0 +1,3 @@ +EXPORTS +DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE +DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE diff --git a/src/Microsoft.Management.Configuration/MutableFlag.cpp b/src/Microsoft.Management.Configuration/MutableFlag.cpp @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "MutableFlag.h" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + void MutableFlag::RequireMutable() const + { + THROW_HR_IF(E_NOT_VALID_STATE, !m_isMutable); + } +} diff --git a/src/Microsoft.Management.Configuration/MutableFlag.h b/src/Microsoft.Management.Configuration/MutableFlag.h @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + // Helper to enable objects to enforce being immutable. + struct MutableFlag + { + MutableFlag(bool isMutable = true) : m_isMutable(isMutable) {} + MutableFlag(const MutableFlag&) = default; + MutableFlag& operator=(const MutableFlag&) = default; + MutableFlag(MutableFlag&&) = default; + MutableFlag& operator=(MutableFlag&&) = default; + + // If the state is not mutable, throws. + void RequireMutable() const; + + private: + bool m_isMutable; + }; +} diff --git a/src/Microsoft.Management.Configuration/OpenConfigurationSetResult.cpp b/src/Microsoft.Management.Configuration/OpenConfigurationSetResult.cpp @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "OpenConfigurationSetResult.h" +#include "OpenConfigurationSetResult.g.cpp" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + void OpenConfigurationSetResult::Initialize(Configuration::ConfigurationSet configurationSet) + { + m_set = std::move(configurationSet); + } + + void OpenConfigurationSetResult::Initialize(hresult resultCode, hstring field) + { + m_resultCode = resultCode; + m_field = field; + } + + Configuration::ConfigurationSet OpenConfigurationSetResult::Set() + { + return m_set; + } + + hresult OpenConfigurationSetResult::ResultCode() + { + return m_resultCode; + } + + hstring OpenConfigurationSetResult::Field() + { + return m_field; + } +} diff --git a/src/Microsoft.Management.Configuration/OpenConfigurationSetResult.h b/src/Microsoft.Management.Configuration/OpenConfigurationSetResult.h @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "OpenConfigurationSetResult.g.h" +#include "ConfigurationSet.h" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + struct OpenConfigurationSetResult : OpenConfigurationSetResultT<OpenConfigurationSetResult> + { + OpenConfigurationSetResult() = default; + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + void Initialize(Configuration::ConfigurationSet configurationSet); + void Initialize(hresult resultCode, hstring field = {}); +#endif + + Configuration::ConfigurationSet Set(); + hresult ResultCode(); + hstring Field(); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + private: + Configuration::ConfigurationSet m_set = nullptr; + hresult m_resultCode; + hstring m_field; +#endif + }; +} diff --git a/src/Microsoft.Management.Configuration/PropertySheet.props b/src/Microsoft.Management.Configuration/PropertySheet.props @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ImportGroup Label="PropertySheets" /> + <PropertyGroup Label="UserMacros" /> + <!-- + To customize common C++/WinRT project properties: + * right-click the project node + * expand the Common Properties item + * select the C++/WinRT property page + + For more advanced scenarios, and complete documentation, please see: + https://github.com/Microsoft/cppwinrt/tree/master/nuget + --> + <PropertyGroup /> + <ItemDefinitionGroup /> +</Project>+ \ No newline at end of file diff --git a/src/Microsoft.Management.Configuration/TestConfigurationSetResult.cpp b/src/Microsoft.Management.Configuration/TestConfigurationSetResult.cpp @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "TestConfigurationSetResult.h" +#include "TestConfigurationSetResult.g.cpp" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + TestConfigurationSetResult::TestConfigurationSetResult() : m_unitResults(single_threaded_vector<TestConfigurationUnitResult>()) + { + } + + void TestConfigurationSetResult::AppendUnitResult(const TestConfigurationUnitResult& unitResult) + { + m_unitResults.Append(unitResult); + + ConfigurationTestResult unitValue = unitResult.TestResult(); + + // Also aggregate the result of this incoming test into the overall result + switch (m_testResult) + { + case ConfigurationTestResult::Unknown: + case ConfigurationTestResult::NotRun: + // In these "default" cases, just take the unit result + m_testResult = unitValue; + break; + case ConfigurationTestResult::Positive: + if (unitValue == ConfigurationTestResult::Negative || unitValue == ConfigurationTestResult::Failed) + { + m_testResult = unitValue; + } + break; + case ConfigurationTestResult::Negative: + if (unitValue == ConfigurationTestResult::Failed) + { + m_testResult = unitValue; + } + break; + case ConfigurationTestResult::Failed: + // If a unit failed, the set failed + break; + default: + THROW_HR(E_UNEXPECTED); + } + } + + Windows::Foundation::Collections::IVectorView<TestConfigurationUnitResult> TestConfigurationSetResult::UnitResults() + { + return m_unitResults.GetView(); + } + + ConfigurationTestResult TestConfigurationSetResult::TestResult() + { + return m_testResult; + } + + void TestConfigurationSetResult::TestResult(ConfigurationTestResult value) + { + m_testResult = value; + } +} diff --git a/src/Microsoft.Management.Configuration/TestConfigurationSetResult.h b/src/Microsoft.Management.Configuration/TestConfigurationSetResult.h @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "TestConfigurationSetResult.g.h" +#include <winrt/Windows.Foundation.Collections.h> + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + struct TestConfigurationSetResult : TestConfigurationSetResultT<TestConfigurationSetResult> + { + TestConfigurationSetResult(); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + void AppendUnitResult(const TestConfigurationUnitResult& unitResult); + void TestResult(ConfigurationTestResult value); +#endif + + Windows::Foundation::Collections::IVectorView<TestConfigurationUnitResult> UnitResults(); + ConfigurationTestResult TestResult(); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + private: + Windows::Foundation::Collections::IVector<TestConfigurationUnitResult> m_unitResults = nullptr; + ConfigurationTestResult m_testResult = ConfigurationTestResult::Unknown; +#endif + }; +} diff --git a/src/Microsoft.Management.Configuration/TestConfigurationUnitResult.cpp b/src/Microsoft.Management.Configuration/TestConfigurationUnitResult.cpp @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "TestConfigurationUnitResult.h" +#include "TestConfigurationUnitResult.g.cpp" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + void TestConfigurationUnitResult::Initialize(ConfigurationUnit unit, ConfigurationUnitResultInformation resultInformation) + { + m_unit = unit; + m_resultInformation = resultInformation; + } + + ConfigurationUnit TestConfigurationUnitResult::Unit() + { + return m_unit; + } + + ConfigurationUnitResultInformation TestConfigurationUnitResult::ResultInformation() + { + return m_resultInformation; + } + + void TestConfigurationUnitResult::ResultInformation(const ConfigurationUnitResultInformation& value) + { + m_resultInformation = value; + } + + ConfigurationTestResult TestConfigurationUnitResult::TestResult() + { + return m_testResult; + } + + void TestConfigurationUnitResult::TestResult(ConfigurationTestResult value) + { + m_testResult = value; + } +} diff --git a/src/Microsoft.Management.Configuration/TestConfigurationUnitResult.h b/src/Microsoft.Management.Configuration/TestConfigurationUnitResult.h @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "TestConfigurationUnitResult.g.h" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + struct TestConfigurationUnitResult : TestConfigurationUnitResultT<TestConfigurationUnitResult> + { + using ConfigurationUnit = Configuration::ConfigurationUnit; + using ConfigurationUnitResultInformation = Configuration::ConfigurationUnitResultInformation; + + TestConfigurationUnitResult() = default; + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + void Initialize(ConfigurationUnit unit, ConfigurationUnitResultInformation resultInformation); + void ResultInformation(const ConfigurationUnitResultInformation& value); + void TestResult(ConfigurationTestResult value); +#endif + + ConfigurationUnit Unit(); + ConfigurationUnitResultInformation ResultInformation(); + ConfigurationTestResult TestResult(); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + private: + ConfigurationUnit m_unit = nullptr; + ConfigurationUnitResultInformation m_resultInformation = nullptr; + ConfigurationTestResult m_testResult = ConfigurationTestResult::Unknown; +#endif + }; +} diff --git a/src/Microsoft.Management.Configuration/TestSettingsResult.cpp b/src/Microsoft.Management.Configuration/TestSettingsResult.cpp @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "TestSettingsResult.h" +#include "TestSettingsResult.g.cpp" +#include "ConfigurationUnitResultInformation.h" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + TestSettingsResult::TestSettingsResult() : + m_resultInformation(*make_self<wil::details::module_count_wrapper<implementation::ConfigurationUnitResultInformation>>()) + { + } + + ConfigurationTestResult TestSettingsResult::TestResult() + { + return m_testResult; + } + + void TestSettingsResult::TestResult(ConfigurationTestResult const& value) + { + m_testResult = value; + } + + Configuration::ConfigurationUnitResultInformation TestSettingsResult::ResultInformation() + { + return m_resultInformation; + } +} diff --git a/src/Microsoft.Management.Configuration/TestSettingsResult.h b/src/Microsoft.Management.Configuration/TestSettingsResult.h @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "TestSettingsResult.g.h" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + struct TestSettingsResult : TestSettingsResultT<TestSettingsResult> + { + TestSettingsResult(); + + ConfigurationTestResult TestResult(); + void TestResult(ConfigurationTestResult const& value); + + Configuration::ConfigurationUnitResultInformation ResultInformation(); + + private: + ConfigurationTestResult m_testResult = ConfigurationTestResult::Unknown; + Configuration::ConfigurationUnitResultInformation m_resultInformation; + }; +} + +namespace winrt::Microsoft::Management::Configuration::factory_implementation +{ + struct TestSettingsResult : TestSettingsResultT<TestSettingsResult, implementation::TestSettingsResult> + { + }; +} diff --git a/src/Microsoft.Management.Configuration/packages.config b/src/Microsoft.Management.Configuration/packages.config @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="Microsoft.Windows.CppWinRT" version="2.0.210505.3" targetFramework="native" /> + <package id="Microsoft.Windows.ImplementationLibrary" version="1.0.210204.1" targetFramework="native" /> +</packages>+ \ No newline at end of file diff --git a/src/Microsoft.Management.Configuration/pch.cpp b/src/Microsoft.Management.Configuration/pch.cpp @@ -0,0 +1,3 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" diff --git a/src/Microsoft.Management.Configuration/pch.h b/src/Microsoft.Management.Configuration/pch.h @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include <unknwn.h> +#include <winrt/Windows.Foundation.h> +#include <winrt/Windows.Foundation.Collections.h> +#include <winrt/Windows.Storage.Streams.h> + +#pragma warning( push ) +#pragma warning ( disable : 4467 6388) +// 4467 Allow use of uuid attribute for com object creation. +// 6388 Allow CreateInstance. +#include <wil/cppwinrt_wrl.h> +#include <wil/resource.h> +#pragma warning( pop ) + +#include <atomic> +#include <filesystem> +#include <fstream> +#include <functional> +#include <map> +#include <memory> +#include <mutex> +#include <optional> +#include <sstream> +#include <stack> +#include <stdexcept> +#include <string> +#include <string_view> +#include <utility> +#include <vector> diff --git a/src/YamlCppLib/YamlCppLib.vcxproj b/src/YamlCppLib/YamlCppLib.vcxproj @@ -172,6 +172,7 @@ <LanguageStandard>stdcpp17</LanguageStandard> <WarningLevel>Level3</WarningLevel> <AdditionalOptions>/D YAML_DECLARE_STATIC /D HAVE_CONFIG_H %(AdditionalOptions)</AdditionalOptions> + <RuntimeTypeInfo>false</RuntimeTypeInfo> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'"> @@ -180,6 +181,7 @@ <LanguageStandard>stdcpp17</LanguageStandard> <WarningLevel>Level3</WarningLevel> <AdditionalOptions>/D YAML_DECLARE_STATIC /D HAVE_CONFIG_H %(AdditionalOptions)</AdditionalOptions> + <RuntimeTypeInfo>false</RuntimeTypeInfo> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Fuzzing|ARM'"> @@ -188,6 +190,7 @@ <LanguageStandard>stdcpp17</LanguageStandard> <WarningLevel>Level3</WarningLevel> <AdditionalOptions>/D YAML_DECLARE_STATIC /D HAVE_CONFIG_H %(AdditionalOptions)</AdditionalOptions> + <RuntimeTypeInfo>false</RuntimeTypeInfo> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'"> @@ -196,6 +199,7 @@ <LanguageStandard>stdcpp17</LanguageStandard> <WarningLevel>Level3</WarningLevel> <AdditionalOptions>/D YAML_DECLARE_STATIC /D HAVE_CONFIG_H %(AdditionalOptions)</AdditionalOptions> + <RuntimeTypeInfo>false</RuntimeTypeInfo> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'"> @@ -204,6 +208,7 @@ <LanguageStandard>stdcpp17</LanguageStandard> <WarningLevel>Level3</WarningLevel> <AdditionalOptions>/D YAML_DECLARE_STATIC /D HAVE_CONFIG_H %(AdditionalOptions)</AdditionalOptions> + <RuntimeTypeInfo>false</RuntimeTypeInfo> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Fuzzing|ARM64'"> @@ -212,6 +217,7 @@ <LanguageStandard>stdcpp17</LanguageStandard> <WarningLevel>Level3</WarningLevel> <AdditionalOptions>/D YAML_DECLARE_STATIC /D HAVE_CONFIG_H %(AdditionalOptions)</AdditionalOptions> + <RuntimeTypeInfo>false</RuntimeTypeInfo> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> @@ -220,6 +226,7 @@ <LanguageStandard>stdcpp17</LanguageStandard> <WarningLevel>Level3</WarningLevel> <AdditionalOptions>/D YAML_DECLARE_STATIC /D HAVE_CONFIG_H %(AdditionalOptions)</AdditionalOptions> + <RuntimeTypeInfo>false</RuntimeTypeInfo> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> @@ -228,6 +235,7 @@ <LanguageStandard>stdcpp17</LanguageStandard> <WarningLevel>Level3</WarningLevel> <AdditionalOptions>/D YAML_DECLARE_STATIC /D HAVE_CONFIG_H %(AdditionalOptions)</AdditionalOptions> + <RuntimeTypeInfo>false</RuntimeTypeInfo> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Fuzzing|Win32'"> @@ -236,6 +244,7 @@ <LanguageStandard>stdcpp17</LanguageStandard> <WarningLevel>Level3</WarningLevel> <AdditionalOptions>/D YAML_DECLARE_STATIC /D HAVE_CONFIG_H %(AdditionalOptions)</AdditionalOptions> + <RuntimeTypeInfo>false</RuntimeTypeInfo> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> @@ -244,6 +253,7 @@ <LanguageStandard>stdcpp17</LanguageStandard> <WarningLevel>Level3</WarningLevel> <AdditionalOptions>/D YAML_DECLARE_STATIC /D HAVE_CONFIG_H %(AdditionalOptions)</AdditionalOptions> + <RuntimeTypeInfo>false</RuntimeTypeInfo> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> @@ -252,6 +262,7 @@ <LanguageStandard>stdcpp17</LanguageStandard> <WarningLevel>Level3</WarningLevel> <AdditionalOptions>/D YAML_DECLARE_STATIC /D HAVE_CONFIG_H %(AdditionalOptions)</AdditionalOptions> + <RuntimeTypeInfo>false</RuntimeTypeInfo> </ClCompile> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Fuzzing|x64'"> @@ -261,6 +272,7 @@ <WarningLevel>Level3</WarningLevel> <AdditionalOptions>/D YAML_DECLARE_STATIC /D HAVE_CONFIG_H /fsanitize-coverage=inline-8bit-counters /fsanitize-coverage=edge /fsanitize-coverage=trace-cmp /fsanitize-coverage=trace-div %(AdditionalOptions)</AdditionalOptions> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + <RuntimeTypeInfo>false</RuntimeTypeInfo> </ClCompile> </ItemDefinitionGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />