winget-cli

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

Configuration-COM-API.md (33864B)


      1 <!-- TEMPLATE
      2     The purpose of this spec is to describe new APIs, in a way
      3     that will transfer to docs.microsoft.com (DMC).
      4 
      5     There are two audiences for the spec. The first are people that want to evaluate and
      6     give feedback on the API, as part of the submission process.
      7     When it's complete it will be incorporated into the public documentation at
      8     http://docs.microsoft.com (DMC).
      9     Hopefully we'll be able to copy it mostly verbatim. So the second audience is
     10     everyone that reads there to learn how and why to use this API.
     11     Some of this text also shows up in Visual Studio Intellisense.
     12 
     13     For example, much of the examples and descriptions in the `RadialGradientBrush` API spec
     14     (https://github.com/microsoft/microsoft-ui-xaml-specs/blob/master/active/RadialGradientBrush/RadialGradientBrush.md)
     15     were carried over to the public API page on DMC
     16     (https://docs.microsoft.com/windows/winui/api/microsoft.ui.xaml.media.radialgradientbrush?view=winui-2.5)
     17 
     18     Once the API is on DMC, that becomes the official copy, and this spec becomes an archive.
     19     For example if the description is updated, that only needs to happen on DMC and needn't
     20     be duplicated here.
     21 
     22     Examples:
     23     * New class (RadialGradientBrush):
     24       https://github.com/microsoft/microsoft-ui-xaml-specs/blob/master/active/RadialGradientBrush/RadialGradientBrush.md
     25     * New member on an existing class (UIElement.ProtectedCursor):
     26       https://github.com/microsoft/microsoft-ui-xaml-specs/blob/master/active/UIElement/ElementCursor.md
     27 
     28     Style guide:
     29     * Use second person; speak to the developer who will be learning/using this API.
     30     (For example "you use this to..." rather than "the developer uses this to...")
     31     * Use hard returns to keep the page width within ~100 columns.
     32     (Otherwise it's more difficult to leave comments in a GitHub PR.)
     33     * Talk about an API's behavior, not its implementation.
     34     (Speak to the developer using this API, not to the team implementing it.)
     35     * A picture is worth a thousand words.
     36     * An example is worth a million words.
     37     * Keep examples realistic but simple; don't add unrelated complications.
     38     (An example that passes a stream needn't show the process of launching the File-Open dialog.)
     39 
     40 -->
     41 
     42 Microsoft.Management.Configuration API
     43 ===
     44 
     45 # Background
     46 This API is being added to enable the Developer+ configuration scenarios. It enables interacting with
     47 configuration sets in three contexts:
     48 
     49 1. Loading an existing configuration set from a stream
     50 2. Loading previously applied configuration sets from the local history
     51 3. Authoring a new/editing an existing configuration
     52 
     53 These configuration sets are composed of configuration units, which describe the individual configurable
     54 items and the values to configure.
     55 
     56 Configuration actions consist of:
     57 
     58 1. Test :: Determining whether the system state matches the described state
     59 2. Get :: Extracting the current system state with respect to the configuration scope
     60 3. Set :: Applying the described state to the system
     61 
     62 This API is also intended to support multiple processes watching for state changes, both for the
     63 configuration set lifetimes and the individual configuration unit states.
     64 
     65 # Conceptual pages (How To)
     66 
     67 _(Add conceptual documentation that will go to docs.microsoft.com "how to" page if needed)_
     68 
     69 # API Pages
     70 
     71 <!-- TEMPLATE
     72 
     73   Each of the L2 sections in this "API Pages" section corresponds to a page on DMC.
     74 
     75   It's not necessary to have a section for every class member though:
     76   * If its purpose and usage is obvious from it's name/type, it's not necessary to
     77     create a section for it.
     78   * If its purpose and usage is fully explained by brief description, either
     79       put it in a table in the "Other [class] members" section
     80       put it with /// comments in the IDL section
     81 
     82   Create an L2 section here for each API that needs more description or examples.
     83   For a new class with members, the members should go in their own L2 section.
     84 
     85   Example layout
     86     ## MyClass
     87     ## MyClass.Member1
     88     ## MyClass.Member2
     89     ## Other MyClass members
     90     ## MyOtherClass
     91     ## ...
     92 
     93   Notes:
     94   * The first line of each of these sections should become that first line on the DMC page,
     95     which then becomes the description you see in Intellisense.
     96   * Each page can have description, examples, and remarks.
     97     Remarks are where the documentation calls out special considerations that the developer
     98     should be aware of.
     99   * It can be helpful at the top of an API page (or after the Intellisense text) to add the
    100     API signature in C#
    101   * Add a "_Spec note: ..._" to add a note that's useful in this spec but shouldn't go to DMC.
    102   * Show _examples_, not _samples_; an example is a snippet, a sample is a full working app.
    103 
    104 -->
    105 
    106 ## ConfigurationSetState enumeration
    107 
    108 The state of a configuration set in the configuration history.
    109 
    110 | Name | Description |
    111 |-|-|
    112 | Unknown | Primarily used for a configuration set that has not been applied. |
    113 | Pending | The configuration set has been recorded into the history, but has not yet begun applying. |
    114 | InProgress | The configuration set has begun being applied to the system. |
    115 | Completed | The configuration set has completed being applied. |
    116 
    117 ## ConfigurationUnitState enumeration
    118 
    119 The state of a configuration unit in the configuration history.
    120 
    121 | Name | Description |
    122 |-|-|
    123 | Unknown | Primarily used for a configuration unit that has not been applied. |
    124 | Pending | The configuration unit has been recorded into the history, but has not yet begun applying. |
    125 | InProgress | The configuration unit has begun being applied to the system. |
    126 | Completed | The configuration unit has completed being applied; the result information will contain additional details. |
    127 | Skipped | The configuration unit was skipped; the result information will contain additional details on the reason. |
    128 
    129 ## ConfigurationUnitDetailLevel enumeration
    130 
    131 Defines the level of detail probing that is allowed about a configuration unit.
    132 
    133 | Name | Description |
    134 |-|-|
    135 | Local | Only reads details from local data. |
    136 | Catalog | Will query the catalog information for details, but will not download any modules. |
    137 | Download | Will download modules, but not load them. |
    138 | Load | Will download and load modules for details. |
    139 
    140 ## ConfigurationUnitResultInformation class
    141 
    142 Information on a result for a single unit of configuration.
    143 
    144 The class is used both in reporting results through the `ConfigurationSet.ConfigurationSetChange` event as they occur
    145 and in viewing past results via the `ConfigurationUnit.ResultInformation` property on a historical record.
    146 
    147 ## IConfigurationUnitSettingDetails interface
    148 
    149 Provides information for a specific configuration unit setting.
    150 
    151 The properties on this interface are useful for creating a rich authoring experience.
    152 
    153 ## IConfigurationUnitSettingDetails.Semantics schema
    154 
    155 > _TODO: Define the meaning/schema for this value_
    156 
    157 ## IConfigurationUnitProcessorDetails interface
    158 
    159 Provides information for a specific configuration unit within the runtime.
    160 
    161 The properties on this interface are useful for informing the user about the provenance of the code that is
    162 responsible for processing the configuration unit.
    163 
    164 ## ConfigurationUnit class
    165 
    166 A single unit of configuration.
    167 
    168 Represents the smallest actionable configuration element.
    169 
    170 ## ConfigurationUnit constructor
    171 
    172 Creates an empty configuration unit for authoring purposes.
    173 
    174 ```C#
    175 ConfigurationUnit();
    176 ```
    177 
    178 ## ConfigurationUnit properties
    179 
    180 | Name | Description |
    181 |-|-|
    182 | UnitName | The name of the unit being configured; not a name for this instance. |
    183 | InstanceIdentifier | An identifier used to uniquely identify the instance of a configuration unit on the system. |
    184 | Identifier | The identifier name of this instance within the set. This value is referenced by other unit's `Dependencies`. |
    185 | Dependencies | The identifiers of the configuration units that this unit depends on. |
    186 | Directives | Contains the values that are for use by the configuration system, related to this unit. |
    187 | Settings | Contains the values that are for use by the configuration unit itself. |
    188 | Details | Contains information on the origin of the configuration unit. You must call `ConfigurationProcessor.Get*DetailsAsync` to populate this value. |
    189 | State | The current state of the configuration unit. |
    190 | ResultInformation | Contains information on the result of the latest attempt to apply the configuration unit. |
    191 | ShouldApply | Allows for control over whether this unit should be applied when the set containing it is applied. |
    192 
    193 ## ConfigurationUnit.Directives known values
    194 
    195 > _TODO: List of well known directives_
    196 
    197 ## ConfigurationSetChangeEventType enumeration
    198 
    199 The change event type that has occurred for a configuration set change.
    200 
    201 | Name | Description |
    202 |-|-|
    203 | Unknown | For future use if the caller is not aware of newer change types. |
    204 | SetStateChanged | The state of the configuration set has changed. |
    205 | UnitStateChanged | The state of a configuration unit has changed. |
    206 
    207 ## ConfigurationSetChangeData class
    208 
    209 The change data sent about changes to a specific set.
    210 
    211 This class is sent to subscribers of the `ConfigurationSet.ConfigurationSetChange` event, containing information
    212 about the specific change that occurred.
    213 
    214 ## ConfigurationSet class
    215 
    216 A configuration set contains a collection of configuration units and details about the set.
    217 
    218 Represents a self contained group of configuration units that are operated on together.
    219 
    220 ## ConfigurationSet constructors
    221 
    222 Creates an empty configuration set for authoring purposes.
    223 
    224 ```C#
    225 ConfigurationSet();
    226 ```
    227 
    228 Loads a configuration set from the given stream.
    229 
    230 ```C#
    231 ConfigurationSet(Windows.Storage.Streams.IInputStream stream);
    232 ```
    233 
    234 ## ConfigurationSet.ConfigurationSetChange event
    235 
    236 State changes for this set and it's units are sent to subscribers of this event.
    237 
    238 ```C#
    239 event Windows.Foundation.TypedEventHandler<ConfigurationSet, ConfigurationSetChangeData> ConfigurationSetChange;
    240 ```
    241 
    242 ## ConfigurationSet.Serialize method
    243 
    244 Serializes the configuration set to the given output stream.
    245 
    246 ```C#
    247 void Serialize(Windows.Storage.Streams.IOutputStream stream);
    248 ```
    249 
    250 ## ConfigurationSet.Remove method
    251 
    252 Removes the configuration set from the recorded history, if present.
    253 
    254 ```C#
    255 void Remove();
    256 ```
    257 
    258 You can use this method to remove a configuration set that is no longer relevant. For example, it may have been for
    259 a repository that is no longer being used by the user and future conflicts with it's configuration are not important.
    260 
    261 ## ConfigurationSet properties
    262 
    263 | Name | Description |
    264 |-|-|
    265 | Name | The name of the set; if from a file this could be the file name. |
    266 | 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). |
    267 | InstanceIdentifier | An identifier used to uniquely identify the instance of a configuration set on the system. |
    268 | State | The state that the set is in. |
    269 | InitialIntent | The time that this set was recorded with intent to apply. |
    270 | ApplyBegun | The time that this set was last started to be applied. |
    271 | ApplyEnded | The time that this set was last finished being applied (does not indicate success). |
    272 | ConfigurationUnits | The configuration units that are part of this set. |
    273 
    274 ## IConfigurationUnitProcessor interface
    275 
    276 Provides access to a specific configuration unit within the runtime.
    277 
    278 This interface is the primary mechanism used to actually read and write configuration to the system,
    279 but it is not expected that you would use this directly as a consumer of Microsoft.Management.Configuration.
    280 
    281 ## IConfigurationSetProcessor interface
    282 
    283 Contains the lifetime of the processing action for a configuration set.
    284 
    285 This interface is used to contain the lifetime of a processing action,
    286 but it is not expected that you would use this directly as a consumer of Microsoft.Management.Configuration.
    287 
    288 ## IConfigurationProcessorFactory interface
    289 
    290 Allows different runtimes to provide specialized handling of configuration processing.
    291 
    292 It is not expected that you would use this interface directly, but rather the `ConfigurationProcessor` class.
    293 
    294 _Spec note: A separate binary (written by us) will contain the implementation(s) of this interface._
    295 
    296 ## DiagnosticLevel enumeration
    297 
    298 Indicates the importance of diagnostic information.
    299 
    300 | Name | Description |
    301 |-|-|
    302 | Verbose | Most useful for debugging scenarios; likely too much for general use. |
    303 | Informational | Details that can be useful for understanding what is happening. |
    304 | Warning | Indicates some abnormal condition, but that is not expected to impact functionality. |
    305 | Error | An error has occurred, but this does not necessarily mean that it will halt the operation. |
    306 | Critical | A serious, fatal condition has been encountered. |
    307 
    308 ## DiagnosticInformation class
    309 
    310 Contains diagnostic information from the configuration system that can be passed along to the user or log files.
    311 This is not intended as primary information, and is thus not localized.
    312 
    313 ## ConfigurationConflictType enumeration
    314 
    315 The type of conflict between configuration sets that was detected.
    316 
    317 | Name | Description |
    318 |-|-|
    319 | Unknown | For future use if the caller is not aware of newer conflict types. |
    320 | MatchingOrigin | Indicates that the first configuration set has a matching name and origin to the second, which has already been applied. |
    321 | IdenticalSetApplied | Indicates that the first configuration set is identical to the second, which has already been applied. |
    322 | SettingsConflict | Indicates a conflict between the settings of two configuration units. |
    323 
    324 ## ConfigurationConflictSetting class
    325 
    326 Describes a conflict between a setting of two configuration units.
    327 
    328 ## ConfigurationConflict class
    329 
    330 Describes a conflict between two configuration sets.
    331 
    332 ## ApplyConfigurationSetFlags enumeration
    333 
    334 Flags to control how a configuration set should be applied to the system.
    335 
    336 | Name | Description |
    337 |-|-|
    338 | None | The configuration set should be applied in the default manner. |
    339 | 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. |
    340 
    341 ## ConfigurationChangeEventType enumeration
    342 
    343 The configuration set change event type that has occurred.
    344 
    345 | Name | Description |
    346 |-|-|
    347 | Unknown | For future use if the caller is not aware of newer change types. |
    348 | SetAdded | A new configuration set was recorded in the history with the intent to be applied. |
    349 | SetStateChanged | A configuration set has changed state. |
    350 | SetRemoved | A configuration set has been removed from the history. |
    351 
    352 ## ConfigurationChangeEventType class
    353 
    354 The change data sent about changes to sets.
    355 
    356 ## ConfigurationProcessor class
    357 
    358 The configuration processor is responsible for the interactions with the system.
    359 
    360 You must use this class to do anything beyond reading configuration sets. It is the entrypoint for all actions that
    361 will interact with the actual system configuration.
    362 
    363 ## ConfigurationProcessor constructor
    364 
    365 Creates a configuration processor using the given configuration factory.
    366 
    367 ```C#
    368 ConfigurationProcessor(IConfigurationProcessorFactory factory);
    369 ```
    370 
    371 > _TODO: Add details on the mechanics of creating the `IConfigurationProcessorFactory` objects that we provide._
    372 
    373 ## ConfigurationProcessor.CheckForConflicts(Async) method
    374 
    375 Checks for conflicts amongst the configuration sets provided, optionally including the configuration sets already applied to the system.
    376 
    377 ```C#
    378 Windows.Foundation.Collections.IVectorView<ConfigurationConflict> CheckForConflicts(Windows.Foundation.Collections.IVectorView<ConfigurationSet> configurationSets, Boolean includeConfigurationHistory);
    379 
    380 Windows.Foundation.IAsyncOperation< Windows.Foundation.Collections.IVectorView<ConfigurationConflict> > CheckForConflictsAsync(Windows.Foundation.Collections.IVectorView<ConfigurationSet> configurationSets, Boolean includeConfigurationHistory);
    381 ```
    382 
    383 This method should be used on any configuration set that is opened in order to determine if it would cause a conflict
    384 with previously applied configurations. It should be called *after* setting the `Name` and `Origin` in order to determine
    385 if it is a potential update.
    386 
    387 ## ConfigurationProcessor.GetSetDetails(Async) method
    388 
    389 Gets the details for all configuration units in a set.
    390 
    391 ```C#
    392 void GetSetDetails(ConfigurationSet configurationSet, ConfigurationUnitDetailLevel detailLevel);
    393 
    394 Windows.Foundation.IAsyncAction GetSetDetailsAsync(ConfigurationSet configurationSet, ConfigurationUnitDetailLevel detailLevel);
    395 ```
    396 
    397 This is a convenience/optimization method that will do the same thing as calling `GetUnitDetails(Async)` on each
    398 configuration unit in the set. See `GetUnitDetails(Async)` for more information on what it will do.
    399 
    400 ## ConfigurationProcessor.GetUnitDetails(Async) method
    401 
    402 Gets the details for all configuration units in a set.
    403 
    404 ```C#
    405 void GetUnitDetails(ConfigurationUnit unit, ConfigurationUnitDetailLevel detailLevel);
    406 
    407 Windows.Foundation.IAsyncAction GetUnitDetailsAsync(ConfigurationUnit unit, ConfigurationUnitDetailLevel detailLevel);
    408 ```
    409 
    410 This method will get the details about a specific configuration unit and make them available via `ConfigurationUnit.Details`.
    411 The `detailLevel` parameter allows control over how deeply to probe for details. It is an analog for the amount of
    412 trust to place in the configuration unit processor.
    413 
    414 ## ConfigurationProcessor.ApplySet(Async) method
    415 
    416 Applies the configuration set state to the system.
    417 
    418 ```C#
    419 ApplyConfigurationSetResult ApplySet(ConfigurationSet configurationSet, ApplyConfigurationSetFlags flags);
    420 
    421 Windows.Foundation.IAsyncOperationWithProgress<ApplyConfigurationSetResult, ConfigurationSetChangeData> ApplySetAsync(ConfigurationSet configurationSet, ApplyConfigurationSetFlags flags);
    422 ```
    423 
    424 Using the async method and it's progress is more efficient than subscribing to the `ConfigurationSetChange` event before calling this method.
    425 
    426 ## ConfigurationProcessor.TestSet(Async) method
    427 
    428 Tests if the system state matches the state described by the configuration set.
    429 
    430 ```C#
    431 TestConfigurationSetResult TestSet(ConfigurationSet configurationSet);
    432 
    433 Windows.Foundation.IAsyncOperationWithProgress<TestConfigurationSetResult, TestConfigurationUnitResult> TestSetAsync(ConfigurationSet configurationSet);
    434 ```
    435 
    436 ## ConfigurationProcessor.GetSettings(Async) method
    437 
    438 Gets the current configuration unit settings from the system state.
    439 
    440 ```C#
    441 GetConfigurationUnitSettingsResult GetSettings(ConfigurationUnit unit);
    442 
    443 Windows.Foundation.IAsyncOperation<GetConfigurationUnitSettingsResult> GetSettingsAsync(ConfigurationUnit unit);
    444 ```
    445 
    446 ## ConfigurationProcessor.Diagnostics event
    447 
    448 Enables listening to internal diagnostics events for logging purposes.
    449 
    450 ## ConfigurationProcessor.ConfigurationChange event
    451 
    452 Signals changes to the set of configuration sets in the history, as well as changes to the state of configuration sets in the history.
    453 
    454 ## ConfigurationProcessor.GetConfigurationHistory method
    455 
    456 Gets the configuration sets from the recorded history.
    457 
    458 ```C#
    459 Windows.Foundation.Collections.IVectorView<ConfigurationSet> GetConfigurationHistory();
    460 ```
    461 
    462 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.
    463 
    464 # API Details
    465 
    466 [Link to the MIDL3 file.](../../src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.idl)
    467 
    468 # Appendix
    469 
    470 <!-- TEMPLATE
    471   Anything else that you want to write down about implementation notes and for posterity,
    472   but that isn't necessary to understand the purpose and usage of the API.
    473 
    474   This or the Background section are a good place to describe alternative designs
    475   and why they were rejected.
    476 -->
    477 
    478 # Sample
    479 
    480 This sample illustrates some of the expected usage patterns.
    481 
    482 ```C#
    483 using Microsoft.Management.Configuration;
    484 using System;
    485 using System.Collections.Generic;
    486 using System.IO;
    487 using System.Reflection;
    488 using Windows.Foundation;
    489 using Windows.Foundation.Collections;
    490 using Windows.Storage;
    491 using Windows.Storage.Streams;
    492 
    493 namespace ConfigurationSample
    494 {
    495     internal static class Helpers
    496     {
    497         internal static IConfigurationProcessorFactory CreateIConfigurationProcessorFactory()
    498         {
    499             throw new NotImplementedException();
    500         }
    501 
    502         internal static ConfigurationSet OpenConfigurationSet(string filePath, ConfigurationProcessor processor)
    503         {
    504             var fileOperation = FileRandomAccessStream.OpenAsync(filePath, FileAccessMode.Read);
    505             fileOperation.AsTask().Wait();
    506             var file = fileOperation.GetResults();
    507             OpenConfigurationSetResult result = processor.OpenConfigurationSet(file);
    508 
    509             if (result.Set != null)
    510             {
    511                 return result.Set;
    512             }
    513 
    514             Console.WriteLine($"Failed opening configuration set: 0x{result.ResultCode:X} at {result.Field}");
    515             return null;
    516         }
    517 
    518         internal static void SetWatcher(ConfigurationSet set, ConfigurationSetChangeData data)
    519         {
    520             Console.WriteLine($"  - Set: {set.Name} [{set.InstanceIdentifier}]");
    521             Console.WriteLine($"    Change: {data.Change}");
    522             Console.WriteLine($"    Set State: {data.SetState}");
    523             switch (data.Change)
    524             {
    525                 case ConfigurationSetChangeEventType.UnitStateChanged:
    526                     Console.WriteLine($"    Unit: {data.Unit.UnitName} [{data.Unit.InstanceIdentifier}]");
    527                     Console.WriteLine($"    Unit State: {data.UnitState}");
    528                     if (data.UnitState == ConfigurationUnitState.Completed && data.ResultInformation.ResultCode != null)
    529                     {
    530                         Console.WriteLine($"    Failure: {data.ResultInformation.Description} [{data.ResultInformation.ResultCode.HResult}]");
    531                     }
    532                     break;
    533             }
    534         }
    535     }
    536 
    537     internal class ApplyProgressWatcher
    538     {
    539         private bool isFirstProgress = true;
    540 
    541         internal void Watcher(IAsyncOperationWithProgress<ApplyConfigurationSetResult, ConfigurationSetChangeData> operation, ConfigurationSetChangeData data)
    542         {
    543             if (isFirstProgress)
    544             {
    545                 isFirstProgress = false;
    546 
    547                 // If our first progress callback contains partial results, output them as if they had been called through progress
    548                 ApplyConfigurationSetResult partialResult = operation.GetResults();
    549 
    550                 foreach (ApplyConfigurationUnitResult unitResult in partialResult.UnitResults)
    551                 {
    552                     HandleUnitProgress(unitResult.Unit, unitResult.State, unitResult.ResultInformation);
    553                 }
    554             }
    555 
    556             switch (data.Change)
    557             {
    558                 case ConfigurationSetChangeEventType.SetStateChanged:
    559                     Console.WriteLine($"  - Set State: {data.SetState}");
    560                     break;
    561                 case ConfigurationSetChangeEventType.UnitStateChanged:
    562                     HandleUnitProgress(data.Unit, data.UnitState, data.ResultInformation);
    563                     break;
    564             }
    565         }
    566 
    567         private void HandleUnitProgress(ConfigurationUnit unit, ConfigurationUnitState state, ConfigurationUnitResultInformation resultInformation)
    568         {
    569             switch (state)
    570             {
    571                 case ConfigurationUnitState.Pending:
    572                     break;
    573                 case ConfigurationUnitState.InProgress:
    574                 case ConfigurationUnitState.Completed:
    575                 case ConfigurationUnitState.Skipped:
    576                     Console.WriteLine($"  - Unit: {unit.UnitName} [{unit.InstanceIdentifier}]");
    577                     Console.WriteLine($"    Unit State: {state}");
    578                     if (resultInformation.ResultCode != null)
    579                     {
    580                         Console.WriteLine($"    HRESULT: [0x{resultInformation.ResultCode.HResult:X8}]");
    581                         Console.WriteLine($"    Reason: {resultInformation.Description}");
    582                     }
    583                     break;
    584                 case ConfigurationUnitState.Unknown:
    585                     break;
    586             }
    587         }
    588     }
    589 
    590     internal class Program
    591     {
    592         static void LoadAndOutput(string[] args)
    593         {
    594             ConfigurationProcessor processor = new ConfigurationProcessor(Helpers.CreateIConfigurationProcessorFactory());
    595 
    596             // Open the given configuration file
    597             ConfigurationSet configSet = Helpers.OpenConfigurationSet(args[1], processor);
    598             if (configSet == null)
    599             {
    600                 return;
    601             }
    602 
    603             // Output some of the information from the set
    604             Console.WriteLine($"Configuration Set: {args[1]}");
    605 
    606             foreach (ConfigurationUnit unit in configSet.ConfigurationUnits)
    607             {
    608                 Console.WriteLine($"  - Configuration Unit: {unit.UnitName}");
    609                 if (!string.IsNullOrEmpty(unit.Identifier))
    610                 {
    611                     Console.WriteLine($"    Identifier: {unit.Identifier}");
    612                 }
    613                 Console.WriteLine($"    Intent: {unit.Intent}");
    614                 IReadOnlyList<string> dependencies = unit.Dependencies;
    615                 if (dependencies.Count > 0)
    616                 {
    617                     Console.WriteLine("    Dependencies:");
    618                     foreach (string dependency in dependencies)
    619                     {
    620                         Console.WriteLine($"      {dependency}");
    621                     }
    622                 }
    623                 ValueSet directives = unit.Directives;
    624                 if (directives.Count > 0)
    625                 {
    626                     Console.WriteLine("    Directives:");
    627                     foreach (var directive in unit.Directives)
    628                     {
    629                         Console.WriteLine($"      {directive.Key}: {directive.Value}");
    630                     }
    631                 }
    632                 ValueSet settings = unit.Settings;
    633                 if (settings.Count > 0)
    634                 {
    635                     Console.WriteLine("    Settings:");
    636                     foreach (var setting in unit.Settings)
    637                     {
    638                         Console.WriteLine($"      {setting.Key}: {setting.Value}");
    639                     }
    640                 }
    641             }
    642         }
    643 
    644         static void LoadAndCheckConflicts(string[] args)
    645         {
    646             // Create the factory and processor
    647             ConfigurationProcessor processor = new ConfigurationProcessor(Helpers.CreateIConfigurationProcessorFactory());
    648 
    649             // Open the given configuration file
    650             ConfigurationSet configSet = Helpers.OpenConfigurationSet(args[1], processor);
    651             if (configSet == null)
    652             {
    653                 return;
    654             }
    655 
    656             // Set a name and origin for this set so that we can see it in the conflict info
    657             configSet.Name = Path.GetFileName(args[1]);
    658             configSet.Origin = args[1];
    659 
    660             // Check for conflicts with existing configurations
    661             List<ConfigurationSet> configSets = new List<ConfigurationSet>() { configSet };
    662             IList<ConfigurationConflict> conflicts = processor.CheckForConflicts(configSets, true);
    663 
    664             Console.WriteLine($"Conflicts with Configuration Set: {args[1]}");
    665 
    666             foreach (ConfigurationConflict conflict in conflicts)
    667             {
    668                 Console.WriteLine($"  - Conflict: {conflict.Conflict}");
    669                 Console.WriteLine($"    First Set: {conflict.FirstSet.Name} [{conflict.FirstSet.Origin}]");
    670                 Console.WriteLine($"    Second Set: {conflict.SecondSet.Name} [{conflict.SecondSet.Origin}]");
    671                 if (conflict.Conflict == ConfigurationConflictType.SettingsConflict)
    672                 {
    673                     Console.WriteLine($"    First Unit: {conflict.FirstUnit.UnitName} [{conflict.FirstUnit.InstanceIdentifier}]");
    674                     Console.WriteLine($"    Second Unit: {conflict.SecondUnit.UnitName} [{conflict.SecondUnit.InstanceIdentifier}]");
    675                     foreach (ConfigurationConflictSetting setting in conflict.Settings)
    676                     {
    677                         Console.WriteLine($"    - Setting: {setting.Name}");
    678                         Console.WriteLine($"      First Value: {setting.FirstValue}");
    679                         Console.WriteLine($"      Second Value: {setting.SecondValue}");
    680                     }
    681                 }
    682             }
    683         }
    684 
    685         static void LoadAndApply(string[] args)
    686         {
    687             // Create the factory and processor
    688             ConfigurationProcessor processor = new ConfigurationProcessor(Helpers.CreateIConfigurationProcessorFactory());
    689 
    690             // Open the given configuration file
    691             ConfigurationSet configSet = Helpers.OpenConfigurationSet(args[1], processor);
    692             if (configSet == null)
    693             {
    694                 return;
    695             }
    696 
    697             Console.WriteLine($"Applying Configuration Set: {args[1]}");
    698 
    699             ApplyProgressWatcher watcher = new ApplyProgressWatcher();
    700 
    701             var operation = processor.ApplySetAsync(configSet, ApplyConfigurationSetFlags.None);
    702             operation.Progress = watcher.Watcher;
    703             operation.AsTask().Wait();
    704             ApplyConfigurationSetResult result = operation.GetResults();
    705 
    706             Console.WriteLine($"  - Done: {result.ResultCode.HResult}");
    707         }
    708 
    709         static void GetHistoryAndWatchEverything(string[] args)
    710         {
    711             Console.WriteLine("Watching all configuration [press Enter to stop]:");
    712 
    713             // Create the factory and processor
    714             ConfigurationProcessor processor = new ConfigurationProcessor(Helpers.CreateIConfigurationProcessorFactory());
    715 
    716             List<ConfigurationSet> list = new List<ConfigurationSet>();
    717 
    718             // Attach to the top level change event
    719             processor.ConfigurationChange += (ConfigurationSet incomingSet, ConfigurationChangeData data) =>
    720             {
    721                 int existingSetIndex = -1;
    722 
    723                 lock (list)
    724                 {
    725                     for (int i = 0; i < list.Count; ++i)
    726                     {
    727                         if (list[i].InstanceIdentifier == data.InstanceIdentifier)
    728                         {
    729                             existingSetIndex = i;
    730                             break;
    731                         }
    732                     }
    733 
    734                     if (data.Change == ConfigurationChangeEventType.SetAdded || data.Change == ConfigurationChangeEventType.SetStateChanged)
    735                     {
    736                         if (existingSetIndex == -1)
    737                         {
    738                             incomingSet.ConfigurationSetChange += Helpers.SetWatcher;
    739                             list.Add(incomingSet);
    740                         }
    741                     }
    742                     else // Removed
    743                     {
    744                         if (existingSetIndex != -1)
    745                         {
    746                             list[existingSetIndex].ConfigurationSetChange -= Helpers.SetWatcher;
    747                             list.RemoveAt(existingSetIndex);
    748                         }
    749                     }
    750                 }
    751 
    752                 Console.WriteLine($"  - Set: {data.InstanceIdentifier}");
    753                 Console.WriteLine($"    Change: {data.Change}");
    754             };
    755 
    756             foreach (ConfigurationSet set in processor.GetConfigurationHistory())
    757             {
    758                 int existingSetIndex = -1;
    759 
    760                 lock (list)
    761                 {
    762                     for (int i = 0; i < list.Count; ++i)
    763                     {
    764                         if (list[i].InstanceIdentifier == set.InstanceIdentifier)
    765                         {
    766                             existingSetIndex = i;
    767                             break;
    768                         }
    769                     }
    770 
    771                     if (existingSetIndex == -1)
    772                     {
    773                         set.ConfigurationSetChange += Helpers.SetWatcher;
    774                         list.Add(set);
    775                     }
    776                 }
    777 
    778                 if (existingSetIndex == -1)
    779                 {
    780                     Console.WriteLine($"  - Set: {set.Name} [{set.InstanceIdentifier}]");
    781                     Console.WriteLine($"    State: {set.State}");
    782                 }
    783             }
    784 
    785             // Wait for user to press enter
    786             Console.ReadLine();
    787         }
    788 
    789         static void Main(string[] args)
    790         {
    791             var method = typeof(Program).GetMethod(args[0], BindingFlags.NonPublic | BindingFlags.Static);
    792 
    793             if (method != null)
    794             {
    795                 method.Invoke(null, new object[]{ args });
    796             }
    797             else
    798             {
    799                 Console.WriteLine($"{args[0]} is not a sample");
    800             }
    801         }
    802     }
    803 }
    804 
    805 ```