commit 248d4a06301d37d3c133d43508931dd38575f788 parent ea350282ffa171bf10925778cdfe39fd42933927 Author: yao-msft <50888816+yao-msft@users.noreply.github.com> Date: Mon, 21 Apr 2025 12:11:02 -0700 Add api to find unit processors based on search path (#5386) Added EnvironmentVariables customization support for ProcessExecution. Added ProcessorRunSettings to customize each processor run settings. Currently only used to change path environment variables for processor runs. Added support to find unit processors based on search path. If search paths not provided, the api will list all unit processors that can be found. Added e2e tests. ###### Microsoft Reviewers: [Open in CodeFlow](https://microsoft.github.io/open-pr/?codeflow=https://github.com/microsoft/winget-cli/pull/5386) --------- Co-authored-by: John McPherson <johnmcp@microsoft.com> Diffstat:
31 files changed, 902 insertions(+), 61 deletions(-)
diff --git a/src/AppInstallerCLICore/Commands/DebugCommand.cpp b/src/AppInstallerCLICore/Commands/DebugCommand.cpp @@ -104,10 +104,13 @@ namespace AppInstaller::CLI OutputProxyStubInterfaceRegistration<winrt::Windows::Foundation::Collections::IIterable<winrt::Microsoft::Management::Configuration::IApplyGroupMemberSettingsResult>>(context); OutputProxyStubInterfaceRegistration<winrt::Windows::Foundation::Collections::IIterable<winrt::Microsoft::Management::Configuration::ITestSettingsResult>>(context); OutputProxyStubInterfaceRegistration<winrt::Windows::Foundation::Collections::IIterable<winrt::Microsoft::Management::Configuration::ConfigurationEnvironment>>(context); + OutputProxyStubInterfaceRegistration<winrt::Windows::Foundation::Collections::IIterable<winrt::Microsoft::Management::Configuration::IConfigurationUnitProcessorDetails>>(context); OutputProxyStubInterfaceRegistration<winrt::Microsoft::Management::Configuration::IConfigurationUnitProcessorDetails2>(context); OutputProxyStubInterfaceRegistration<winrt::Microsoft::Management::Configuration::IGetAllSettingsConfigurationUnitProcessor>(context); OutputProxyStubInterfaceRegistration<winrt::Microsoft::Management::Configuration::IGetAllUnitsConfigurationUnitProcessor>(context); + OutputProxyStubInterfaceRegistration<winrt::Microsoft::Management::Configuration::IFindUnitProcessorsSetProcessor>(context); OutputProxyStubInterfaceRegistration<winrt::Microsoft::Management::Configuration::IConfigurationStatics2>(context); + OutputProxyStubInterfaceRegistration<winrt::Microsoft::Management::Configuration::IConfigurationStatics3>(context); OutputProxyStubInterfaceRegistration<winrt::Microsoft::Management::Configuration::SetProcessorFactory::IPwshConfigurationSetProcessorFactoryProperties>(context); // TODO: Fix the layering inversion created by the COM deployment API (probably in order to operate winget.exe against the COM server). @@ -135,6 +138,7 @@ namespace AppInstaller::CLI { OutputIIDMapping<winrt::Microsoft::Management::Configuration::IConfigurationStatics>(context); OutputIIDMapping<winrt::Microsoft::Management::Configuration::IConfigurationStatics2>(context); + OutputIIDMapping<winrt::Microsoft::Management::Configuration::IConfigurationStatics3>(context); } Resource::LocString DumpErrorResourceCommand::ShortDescription() const diff --git a/src/AppInstallerCLICore/Commands/TestCommand.cpp b/src/AppInstallerCLICore/Commands/TestCommand.cpp @@ -6,6 +6,7 @@ #include "TestCommand.h" #include "AppInstallerRuntime.h" +#include "TableOutput.h" #include "Public/ConfigurationSetProcessorFactoryRemoting.h" #include "Workflows/ConfigurationFlow.h" #include <winrt/Microsoft.Management.Configuration.h> @@ -140,6 +141,80 @@ namespace AppInstaller::CLI WriteConfigFile; } }; + + void InvokeFindUnitProcessors(Execution::Context& context) + { + auto& configurationContext = context.Get<Execution::Data::ConfigurationContext>(); + + winrt::Microsoft::Management::Configuration::FindUnitProcessorsOptions findOptions; + + if (context.Args.Contains(Execution::Args::Type::InstallLocation)) + { + findOptions.SearchPaths(Utility::ConvertToUTF16(context.Args.GetArg(Execution::Args::Type::InstallLocation))); + findOptions.SearchPathsExclusive(true); + findOptions.UnitDetailFlags(winrt::Microsoft::Management::Configuration::ConfigurationUnitDetailFlags::Local); + } + + auto result = configurationContext.Processor().FindUnitProcessors(findOptions); + + if (result.Size() > 0) + { + Execution::TableOutput<2> table(context.Reporter, + { + "Type"_lis, + "Description"_lis + }); + + for (const auto& resultUnitProcessor : result) + { + table.OutputLine({ + Utility::ConvertToUTF8(resultUnitProcessor.UnitType()), + Utility::ConvertToUTF8(resultUnitProcessor.UnitDescription()) + }); + } + + table.Complete(); + } + else + { + context.Reporter.Info() << "No unit processors found."_lis << std::endl; + } + } + + // Command to directly invoke find unit processors. + struct TestConfigurationFindUnitProcessorsCommand final : public Command + { + TestConfigurationFindUnitProcessorsCommand(std::string_view parent) : Command("config-find-unit-processors", {}, parent) {} + + std::vector<Argument> GetArguments() const override + { + return { + Argument{ Execution::Args::Type::InstallLocation, Resource::String::LocationArgumentDescription }, + }; + } + + Resource::LocString ShortDescription() const override + { + return "Run find unit processors"_lis; + } + + Resource::LocString LongDescription() const override + { + return "Runs find unit processors. Search paths could be provided."_lis; + } + + protected: + void ExecuteInternal(Execution::Context& context) const override + { + context << + VerifyIsFullPackage << + CreateConfigurationProcessorWithoutFactory << + CreateOrOpenConfigurationSet{ "0.3" } << + EnsureDSCv3Processor << + CreateConfigurationProcessor << + InvokeFindUnitProcessors; + } + }; } std::vector<std::unique_ptr<Command>> TestCommand::GetCommands() const @@ -148,6 +223,7 @@ namespace AppInstaller::CLI { std::make_unique<TestAppShutdownCommand>(FullName()), std::make_unique<TestConfigurationExportCommand>(FullName()), + std::make_unique<TestConfigurationFindUnitProcessorsCommand>(FullName()), }); } diff --git a/src/AppInstallerCLICore/ConfigurationDynamicRuntimeFactory.cpp b/src/AppInstallerCLICore/ConfigurationDynamicRuntimeFactory.cpp @@ -155,7 +155,7 @@ namespace AppInstaller::CLI::ConfigurationRemoting IConfigurationSetProcessorFactory::Diagnostics_revoker DiagnosticsEventRevoker; }; - struct DynamicSetProcessor : winrt::implements<DynamicSetProcessor, IConfigurationSetProcessor> + struct DynamicSetProcessor : winrt::implements<DynamicSetProcessor, IConfigurationSetProcessor, IFindUnitProcessorsSetProcessor> { using ProcessorMap = std::map<Security::IntegrityLevel, DynamicProcessorInfo>; @@ -257,6 +257,21 @@ namespace AppInstaller::CLI::ConfigurationRemoting return itr->second.Processor.CreateUnitProcessor(unit); } + Collections::IVector<IConfigurationUnitProcessorDetails> FindUnitProcessors(const FindUnitProcessorsOptions& findOptions) + { + IFindUnitProcessorsSetProcessor findUnitProcessorsSetProcessor; + + if (m_setProcessors[m_currentIntegrityLevel].Processor.try_as<IFindUnitProcessorsSetProcessor>(findUnitProcessorsSetProcessor)) + { + return findUnitProcessorsSetProcessor.FindUnitProcessors(findOptions); + } + else + { + AICLI_LOG(Config, Error, << "Set Processor does not support FindUnitProcessors operation"); + THROW_HR(WINGET_CONFIG_ERROR_NOT_SUPPORTED_BY_PROCESSOR); + } + } + private: // Converts the string representation of SecurityContext to the target integrity level for this instance Security::IntegrityLevel SecurityContextToIntegrityLevel(SecurityContext securityContext) diff --git a/src/AppInstallerCLICore/ExecutionReporter.cpp b/src/AppInstallerCLICore/ExecutionReporter.cpp @@ -175,7 +175,6 @@ namespace AppInstaller::CLI::Execution std::string response; if (!std::getline(m_in, response)) { - m_in.get(); THROW_HR(APPINSTALLER_CLI_ERROR_PROMPT_INPUT_ERROR); } diff --git a/src/AppInstallerCLIE2ETests/ConfigureCommand.cs b/src/AppInstallerCLIE2ETests/ConfigureCommand.cs @@ -44,6 +44,7 @@ namespace AppInstallerCLIE2ETests WinGetSettingsHelper.ConfigureFeature("dsc3", true); this.DeleteResourceArtifacts(); EnsureTestResourcePresence(); + TestCommon.SetupTestSource(false); } /// <summary> @@ -54,6 +55,7 @@ namespace AppInstallerCLIE2ETests { WinGetSettingsHelper.ConfigureFeature("dsc3", false); this.DeleteResourceArtifacts(); + TestCommon.TearDownTestSource(); } /// <summary> @@ -366,6 +368,39 @@ namespace AppInstallerCLIE2ETests Assert.AreEqual("Contents!", File.ReadAllText(targetFilePath)); } + /// <summary> + /// Find unit processors tests. + /// </summary> + [Test] + public void ConfigureFindUnitProcessors() + { + // Find all unit processors. + var result = TestCommon.RunAICLICommand("test config-find-unit-processors", string.Empty); + Assert.AreEqual(0, result.ExitCode); + Assert.True(result.StdOut.Contains("Microsoft/OSInfo")); + + // Setup TestExeInstaller with dsc resources. + var installDir = TestCommon.GetRandomTestDir(); + result = TestCommon.RunAICLICommand("install", $"AppInstallerTest.TestExeInstaller --override \"/InstallDir {installDir} /GenerateDscResourceFiles\""); + Assert.AreEqual(0, result.ExitCode); + + // Find unit processors filtering to install location. + result = TestCommon.RunAICLICommand("test config-find-unit-processors", $"-l {installDir}"); + Assert.AreEqual(0, result.ExitCode); + Assert.False(result.StdOut.Contains("Microsoft/OSInfo")); + Assert.True(result.StdOut.Contains("AppInstallerTest/TestResource")); + + // Find unit processors filtering to unknown location. + var unknownDir = TestCommon.GetRandomTestDir(); + result = TestCommon.RunAICLICommand("test config-find-unit-processors", $"-l {unknownDir}"); + Assert.AreEqual(0, result.ExitCode); + Assert.True(result.StdOut.Contains("No unit processors found.")); + + // Clean up + result = TestCommon.RunAICLICommand("uninstall", "AppInstallerTest.TestExeInstaller"); + Assert.AreEqual(0, result.ExitCode); + } + private void DeleteResourceArtifacts() { // Delete all .txt files in the test directory; they are placed there by the tests diff --git a/src/AppInstallerCLIPackage/Package.appxmanifest b/src/AppInstallerCLIPackage/Package.appxmanifest @@ -114,10 +114,13 @@ <Interface Name="Windows.Foundation.Collections.IIterable`1<Microsoft.Management.Configuration.IApplyGroupMemberSettingsResult>" InterfaceId="5086070C-F468-5B00-8352-50FB420BA8B0" /> <Interface Name="Windows.Foundation.Collections.IIterable`1<Microsoft.Management.Configuration.ITestSettingsResult>" InterfaceId="2D28E6AA-7036-5D78-9B58-9456F1E332FE" /> <Interface Name="Windows.Foundation.Collections.IIterable`1<Microsoft.Management.Configuration.ConfigurationEnvironment>" InterfaceId="47B18106-976B-5532-8E81-F58D304DFA43" /> + <Interface Name="Windows.Foundation.Collections.IIterable`1<Microsoft.Management.Configuration.IConfigurationUnitProcessorDetails>" InterfaceId="055865E9-B633-5AD6-9C8F-55DFCD668E74" /> <Interface Name="Microsoft.Management.Configuration.IConfigurationUnitProcessorDetails2" InterfaceId="E89623ED-76E2-5145-B920-D09659554E35" /> <Interface Name="Microsoft.Management.Configuration.IGetAllSettingsConfigurationUnitProcessor" InterfaceId="72EB8304-D8D3-57D4-9940-7C1C4AD8C40C" /> <Interface Name="Microsoft.Management.Configuration.IGetAllUnitsConfigurationUnitProcessor" InterfaceId="D5CB3357-8AD6-5A3C-8695-057C01867D5F" /> + <Interface Name="Microsoft.Management.Configuration.IFindUnitProcessorsSetProcessor" InterfaceId="620628DF-A5DE-591A-B738-FD8370B4E95C" /> <Interface Name="Microsoft.Management.Configuration.IConfigurationStatics2" InterfaceId="540BE073-F2EF-5375-83AA-8E23086B0669" /> + <Interface Name="Microsoft.Management.Configuration.IConfigurationStatics3" InterfaceId="C84E137B-90E5-5F63-931D-F0497BBCA847" /> <Interface Name="Microsoft.Management.Configuration.SetProcessorFactory.IPwshConfigurationSetProcessorFactoryProperties" InterfaceId="2C298A30-BD3B-5D00-BCD1-2EB633AB7E3B" /> </ProxyStub> </Extension> diff --git a/src/AppInstallerTestExeInstaller/main.cpp b/src/AppInstallerTestExeInstaller/main.cpp @@ -44,12 +44,21 @@ void WriteModifyInvalidOperationScript(std::wofstream& script) { << L"EXIT /B 1\n"; } -void WriteUninstallerScript(std::wofstream& uninstallerScript, const path& uninstallerOutputTextFilePath, const std::wstring& registryKey, const path& modifyScriptPath, const path& repairCompletedTextFilePath) { +void WriteUninstallerScript( + std::wofstream& uninstallerScript, + const path& uninstallerOutputTextFilePath, + const std::wstring& registryKey, + const path& modifyScriptPath, + const path& repairCompletedTextFilePath, + const path& dscResourceExecutablePath, + const path& dscResourceManifestPath) { uninstallerScript << "ECHO. >" << uninstallerOutputTextFilePath << "\n"; uninstallerScript << "ECHO AppInstallerTestExeInstaller.exe uninstalled successfully.\n"; uninstallerScript << "REG DELETE " << registryKey << " /f\n"; uninstallerScript << "if exist \"" << modifyScriptPath.wstring() << "\" del \"" << modifyScriptPath.wstring() << "\"\n"; uninstallerScript << "if exist \"" << repairCompletedTextFilePath.wstring() << "\" del \"" << repairCompletedTextFilePath.wstring() << "\"\n"; + uninstallerScript << "if exist \"" << dscResourceExecutablePath.wstring() << "\" del \"" << dscResourceExecutablePath.wstring() << "\"\n"; + uninstallerScript << "if exist \"" << dscResourceManifestPath.wstring() << "\" del \"" << dscResourceManifestPath.wstring() << "\"\n"; } path GenerateUninstaller(std::wostream& out, const path& installDirectory, const std::wstring& productID, bool useHKLM) @@ -68,6 +77,12 @@ path GenerateUninstaller(std::wostream& out, const path& installDirectory, const path modifyScriptPath = installDirectory; modifyScriptPath /= "ModifyTestExe.bat"; + path dscResourceExecutablePath = installDirectory; + dscResourceExecutablePath /= "AppInstallerTestResource.exe"; + + path dscResourceManifestPath = installDirectory; + dscResourceManifestPath /= "AppInstallerTest.dsc.resource.json"; + std::wstring registryKey{ useHKLM ? L"HKEY_LOCAL_MACHINE\\" : L"HKEY_CURRENT_USER\\" }; registryKey += RegistrySubkey; if (!productID.empty()) @@ -84,7 +99,7 @@ path GenerateUninstaller(std::wostream& out, const path& installDirectory, const uninstallerScript << L"for %%A in (%*) do (\n"; WriteModifyRepairScript(uninstallerScript, repairCompletedTextFilePath, false /*isModifyScript*/); uninstallerScript << ")\n"; - WriteUninstallerScript(uninstallerScript, uninstallerOutputTextFilePath, registryKey, modifyScriptPath, repairCompletedTextFilePath); + WriteUninstallerScript(uninstallerScript, uninstallerOutputTextFilePath, registryKey, modifyScriptPath, repairCompletedTextFilePath, dscResourceExecutablePath, dscResourceManifestPath); uninstallerScript.close(); @@ -113,6 +128,94 @@ path GenerateModifyPath(const path& installDirectory) return modifyScriptPath; } +void GenerateDSCv3ProviderFiles(const path& installDirectory) +{ + path dscResourceExecutablePath = installDirectory; + dscResourceExecutablePath /= "AppInstallerTestResource.exe"; + + WCHAR currentExecutable[MAX_PATH]; + GetModuleFileName(nullptr, currentExecutable, MAX_PATH); + path currentExecutablePath{ currentExecutable }; + copy_file(currentExecutablePath, dscResourceExecutablePath); + + path dscResourceManifestPath = installDirectory; + dscResourceManifestPath /= "AppInstallerTest.dsc.resource.json"; + + std::wstring DscResourceJsonContent = + LR"( + { + "$schema" : "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/bundled/resource/manifest.json", + "description" : "AppInstallerTest dsc Resource.", + "export" : + { + "args" : + [ + "/DscExport" + ], + "executable" : "AppInstallerTestResource.exe" + }, + "get" : + { + "args" : + [ + "/DscGet" + ], + "executable" : "AppInstallerTestResource.exe", + "input" : "stdin" + }, + "set" : + { + "args" : + [ + "/DscSet" + ] , + "executable" : "AppInstallerTestResource.exe", + "handlesExist" : true, + "implementsPretest" : true, + "input" : "stdin", + "return" : "state" + }, + "test" : + { + "args" : + [ + "/DscTest" + ] , + "executable" : "AppInstallerTestResource.exe", + "input" : "stdin", + "return" : "state" + }, + "schema": { + "embedded": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "AppInstallerTestResource", + "description": "App Installer Test Resource", + "type": "object", + "required": [], + "additionalProperties": false, + "properties": { + "_inDesiredState": { + "description": "Indicates whether an instance is in the desired state.", + "type": "boolean" + }, + "data": { + "type": "string", + "description": "Test data." + } + } + } + }, + "type" : "AppInstallerTest/TestResource", + "version" : "1.0.0" + } + )"; + + + std::wofstream dscResourceJson(dscResourceManifestPath); + dscResourceJson << DscResourceJsonContent; + dscResourceJson.close(); +} + void WriteToUninstallRegistry( std::wostream& out, const std::wstring& productID, @@ -293,7 +396,17 @@ void HandleRepairOperation(const std::wstring& productID, const std::wstringstre WriteToFile(outFilePath, outContent); } -void HandleInstallationOperation(std::wostream& out, const path& installDirectory, const std::wstringstream& outContent, const std::wstring& productCode, bool useHKLM, const std::wstring& displayName, const std::wstring& displayVersion, bool noRepair, bool noModify) +void HandleInstallationOperation( + std::wostream& out, + const path& installDirectory, + const std::wstringstream& outContent, + const std::wstring& productCode, + bool useHKLM, + const std::wstring& displayName, + const std::wstring& displayVersion, + bool noRepair, + bool noModify, + bool generateDscResourceFiles) { path outFilePath = installDirectory; outFilePath /= "TestExeInstalled.txt"; @@ -302,6 +415,11 @@ void HandleInstallationOperation(std::wostream& out, const path& installDirector file << outContent.str(); file.close(); + if (generateDscResourceFiles) + { + GenerateDSCv3ProviderFiles(installDirectory); + } + path uninstallerPath = GenerateUninstaller(out, installDirectory, productCode, useHKLM); path modifyPath = GenerateModifyPath(installDirectory); @@ -324,6 +442,7 @@ int wmain(int argc, const wchar_t** argv) bool isRepair = false; bool noRepair = false; bool noModify = false; + bool generateDscResourceFiles = false; // Output to cout by default, but swap to a file if requested std::wostream* out = &std::wcout; @@ -339,6 +458,7 @@ int wmain(int argc, const wchar_t** argv) if (++i < argc) { installDirectory = argv[i]; + std::filesystem::create_directories(installDirectory); outContent << argv[i] << ' '; } } @@ -442,6 +562,40 @@ int wmain(int argc, const wchar_t** argv) { noOperation = true; } + + // Also output dsc resource files + else if (_wcsicmp(argv[i], L"/GenerateDscResourceFiles") == 0) + { + generateDscResourceFiles = true; + } + + // Dsc resource get + else if (_wcsicmp(argv[i], L"/DscGet") == 0) + { + std::cout << R"({"data":"TestData"})" << std::endl; + return 0; + } + + // Dsc resource set + else if (_wcsicmp(argv[i], L"/DscSet") == 0) + { + std::cout << R"({"_inDesiredState":true})" << std::endl; + return 0; + } + + // Dsc resource test + else if (_wcsicmp(argv[i], L"/DscTest") == 0) + { + std::cout << R"({"_inDesiredState":true})" << std::endl; + return 0; + } + + // Dsc resource export + else if (_wcsicmp(argv[i], L"/DscExport") == 0) + { + std::cout << R"({"data":"TestData"})" << std::endl; + return 0; + } } if (noOperation) @@ -487,7 +641,7 @@ int wmain(int argc, const wchar_t** argv) } else { - HandleInstallationOperation(*out, installDirectory, outContent, productCode, useHKLM, displayName, displayVersion, noRepair, noModify); + HandleInstallationOperation(*out, installDirectory, outContent, productCode, useHKLM, displayName, displayVersion, noRepair, noModify, generateDscResourceFiles); } return exitCode; diff --git a/src/Microsoft.Management.Configuration.Processor/DSCv3/Helpers/ProcessExecution.cs b/src/Microsoft.Management.Configuration.Processor/DSCv3/Helpers/ProcessExecution.cs @@ -54,6 +54,12 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Helpers public string? Input { get; init; } = null; /// <summary> + /// Gets the list of custom environment variables to use for the process. + /// </summary> + [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1010:Opening square brackets should be spaced correctly", Justification = "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3687 pending SC 1.2 release")] + public IEnumerable<ProcessExecutionEnvironmentVariable> EnvironmentVariables { get; init; } = []; + + /// <summary> /// Gets the argument string passed to the process. /// </summary> public string SerializedArguments @@ -168,6 +174,24 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Helpers startInfo.RedirectStandardInput = true; } + foreach (var env in this.EnvironmentVariables) + { + switch (env.ValueType) + { + case ProcessExecutionEnvironmentVariableValueType.Override: + startInfo.EnvironmentVariables[env.Name] = env.Value; + break; + + case ProcessExecutionEnvironmentVariableValueType.Prepend: + startInfo.EnvironmentVariables[env.Name] = MergeStringsWithSeparator(env.Value, startInfo.EnvironmentVariables[env.Name] ?? string.Empty, env.Separator); + break; + + case ProcessExecutionEnvironmentVariableValueType.Append: + startInfo.EnvironmentVariables[env.Name] = MergeStringsWithSeparator(startInfo.EnvironmentVariables[env.Name] ?? string.Empty, env.Value, env.Separator); + break; + } + } + this.Process.Start(); this.Process.BeginOutputReadLine(); this.Process.BeginErrorReadLine(); @@ -238,5 +262,28 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Helpers return stringBuilder.ToString(); } + + private static string MergeStringsWithSeparator(string first, string second, string separator) + { + if (string.IsNullOrEmpty(separator)) + { + return first + second; + } + else + { + if (first.EndsWith(separator) && second.StartsWith(separator)) + { + return first + second.Substring(separator.Length); + } + else if (first.EndsWith(separator) || second.StartsWith(separator)) + { + return first + second; + } + else + { + return first + separator + second; + } + } + } } } diff --git a/src/Microsoft.Management.Configuration.Processor/DSCv3/Helpers/ProcessExecutionEnvironmentVariable.cs b/src/Microsoft.Management.Configuration.Processor/DSCv3/Helpers/ProcessExecutionEnvironmentVariable.cs @@ -0,0 +1,34 @@ +// ----------------------------------------------------------------------------- +// <copyright file="ProcessExecutionEnvironmentVariable.cs" company="Microsoft Corporation"> +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// </copyright> +// ----------------------------------------------------------------------------- + +namespace Microsoft.Management.Configuration.Processor.DSCv3.Helpers +{ + /// <summary> + /// Contains custom environment variable info for ProcessExecution. + /// </summary> + internal class ProcessExecutionEnvironmentVariable + { + /// <summary> + /// Gets the name of the environment variable. + /// </summary> + required public string Name { get; init; } + + /// <summary> + /// Gets the value of the environment variable. + /// </summary> + required public string Value { get; init; } + + /// <summary> + /// Gets the value type of the environment variable. + /// </summary> + public ProcessExecutionEnvironmentVariableValueType ValueType { get; init; } = ProcessExecutionEnvironmentVariableValueType.Override; + + /// <summary> + /// Gets the separator of the environment variable if value type is prepend or append. + /// </summary> + public string Separator { get; init; } = ";"; + } +} diff --git a/src/Microsoft.Management.Configuration.Processor/DSCv3/Helpers/ProcessExecutionEnvironmentVariableValueType.cs b/src/Microsoft.Management.Configuration.Processor/DSCv3/Helpers/ProcessExecutionEnvironmentVariableValueType.cs @@ -0,0 +1,29 @@ +// ----------------------------------------------------------------------------- +// <copyright file="ProcessExecutionEnvironmentVariableValueType.cs" company="Microsoft Corporation"> +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// </copyright> +// ----------------------------------------------------------------------------- + +namespace Microsoft.Management.Configuration.Processor.DSCv3.Helpers +{ + /// <summary> + /// The environment variable value type. + /// </summary> + internal enum ProcessExecutionEnvironmentVariableValueType + { + /// <summary> + /// Prepend. + /// </summary> + Prepend, + + /// <summary> + /// Append. + /// </summary> + Append, + + /// <summary> + /// Override. + /// </summary> + Override, + } +} diff --git a/src/Microsoft.Management.Configuration.Processor/DSCv3/Helpers/ProcessorRunSettings.cs b/src/Microsoft.Management.Configuration.Processor/DSCv3/Helpers/ProcessorRunSettings.cs @@ -0,0 +1,59 @@ +// ----------------------------------------------------------------------------- +// <copyright file="ProcessorRunSettings.cs" company="Microsoft Corporation"> +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// </copyright> +// ----------------------------------------------------------------------------- + +namespace Microsoft.Management.Configuration.Processor.DSCv3.Helpers +{ + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Text; + using Microsoft.Management.Configuration.Processor.DSCv3.Model; + using Microsoft.Management.Configuration.Processor.Helpers; + + /// <summary> + /// Contains settings for the DSC v3 processor components to share. + /// </summary> + internal class ProcessorRunSettings + { + /// <summary> + /// Gets the paths for finding DSC resources and executables. + /// </summary> + public string ResourceSearchPaths { get; private set; } = string.Empty; + + /// <summary> + /// Gets a value indicating whether the resource search paths are exclusive. + /// </summary> + public bool ResourceSearchPathsExclusive { get; private set; } = false; + + /// <summary> + /// Creates ProcessorRunSettings from FindUnitProcessorsOptions. + /// </summary> + /// <param name="findOptions">The find unit processors options.</param> + /// <returns>A ProcessorRunSettings.</returns> + public static ProcessorRunSettings CreateFromFindUnitProcessorsOptions(FindUnitProcessorsOptions findOptions) + { + return new ProcessorRunSettings + { + ResourceSearchPaths = findOptions.SearchPaths, + ResourceSearchPathsExclusive = findOptions.SearchPathsExclusive, + }; + } + + /// <summary> + /// Creates ProcessorRunSettings from a ResourceDetails. + /// </summary> + /// <param name="resourceDetails">The resource details to be used.</param> + /// <returns>A ProcessorRunSettings.</returns> + public static ProcessorRunSettings CreateFromResourceDetails(ResourceDetails resourceDetails) + { + return new ProcessorRunSettings + { + ResourceSearchPaths = Path.GetDirectoryName(resourceDetails.Path) ?? string.Empty, + ResourceSearchPathsExclusive = false, + }; + } + } +} diff --git a/src/Microsoft.Management.Configuration.Processor/DSCv3/Helpers/ProcessorSettings.cs b/src/Microsoft.Management.Configuration.Processor/DSCv3/Helpers/ProcessorSettings.cs @@ -186,7 +186,7 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Helpers if (result == null) { - result = new ResourceDetails(configurationUnitInternal); + result = new ResourceDetails(configurationUnitInternal.QualifiedName); } result.EnsureDetails(this, detailFlags); @@ -209,6 +209,52 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Helpers } } + /// <summary> + /// Gets all ResourceDetails matching find options. + /// </summary> + /// <param name="findOptions">The find options.</param> + /// <returns>A list of ResourceDetails.</returns> + public List<ResourceDetails> FindAllResourceDetails(FindUnitProcessorsOptions findOptions) + { + List<ResourceDetails> result = new List<ResourceDetails>(); + + var resourceItemList = this.DSCv3.GetAllResources(ProcessorRunSettings.CreateFromFindUnitProcessorsOptions(findOptions)); + + foreach (var item in resourceItemList) + { + ResourceDetails? details = null; + bool inDictionary = false; + lock (this.resourceDetailsDictionary) + { + inDictionary = this.resourceDetailsDictionary.TryGetValue(item.Type, out details); + } + + if (details == null) + { + details = new ResourceDetails(item.Type); + } + + if (!details.Exists) + { + details.SetResourceListItem(item); + } + + details.EnsureDetails(this, findOptions.UnitDetailFlags); + + if (!inDictionary) + { + lock (this.resourceDetailsDictionary) + { + this.resourceDetailsDictionary.Add(item.Type, details); + } + } + + result.Add(details); + } + + return result; + } + private static string? GetDscExecutablePathForPackage(string packageFamilyName) { string localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); diff --git a/src/Microsoft.Management.Configuration.Processor/DSCv3/Helpers/ResourceDetails.cs b/src/Microsoft.Management.Configuration.Processor/DSCv3/Helpers/ResourceDetails.cs @@ -16,7 +16,7 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Helpers /// </summary> internal class ResourceDetails { - private readonly ConfigurationUnitInternal configurationUnitInternal; + private readonly string resourceTypeName; private object detailsUpdateLock = new object(); private IResourceListItem? resourceListItem = null; @@ -38,10 +38,10 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Helpers /// <summary> /// Initializes a new instance of the <see cref="ResourceDetails"/> class. /// </summary> - /// <param name="configurationUnitInternal">The internal configuration unit data.</param> - public ResourceDetails(ConfigurationUnitInternal configurationUnitInternal) + /// <param name="resourceTypeName">The resource type name.</param> + public ResourceDetails(string resourceTypeName) { - this.configurationUnitInternal = configurationUnitInternal; + this.resourceTypeName = resourceTypeName; } /// <summary> @@ -60,6 +60,38 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Helpers } /// <summary> + /// Gets the path of the resource. + /// </summary> + public string? Path + { + get + { + lock (this.detailsUpdateLock) + { + return this.resourceListItem?.Path; + } + } + } + + /// <summary> + /// Sets the resource list item directly to avoid duplicate "dsc resource list" calls. + /// </summary> + /// <param name="item">The resource list item.</param> + public void SetResourceListItem(IResourceListItem item) + { + lock (this.detailsUpdateLock) + { + if (this.resourceListItem != null) + { + throw new InvalidOperationException("Resource list item is already set"); + } + + this.resourceListItem = item; + this.currentDetailLevel |= ConfigurationUnitDetailFlags.Local; + } + } + + /// <summary> /// Ensures that the given detail level is present. /// </summary> /// <param name="processorSettings">The processor settings to use when getting details.</param> @@ -92,7 +124,7 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Helpers return null; } - ConfigurationUnitProcessorDetails result = new ConfigurationUnitProcessorDetails() { UnitType = this.configurationUnitInternal.QualifiedName }; + ConfigurationUnitProcessorDetails result = new ConfigurationUnitProcessorDetails() { UnitType = this.resourceTypeName }; lock (this.detailsUpdateLock) { @@ -134,7 +166,7 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Helpers private bool GetLocalDetails(ProcessorSettings processorSettings) { - IResourceListItem? resourceListItem = processorSettings.DSCv3.GetResourceByType(this.configurationUnitInternal.QualifiedName); + IResourceListItem? resourceListItem = processorSettings.DSCv3.GetResourceByType(this.resourceTypeName, null); if (resourceListItem != null) { diff --git a/src/Microsoft.Management.Configuration.Processor/DSCv3/Model/IDSCv3.cs b/src/Microsoft.Management.Configuration.Processor/DSCv3/Model/IDSCv3.cs @@ -30,35 +30,47 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Model /// Gets a single resource by its type name. /// </summary> /// <param name="resourceType">The type name of the resource.</param> + /// <param name="runSettings">The processor run settings.</param> /// <returns>A single resource item.</returns> - public IResourceListItem? GetResourceByType(string resourceType); + public IResourceListItem? GetResourceByType(string resourceType, ProcessorRunSettings? runSettings); + + /// <summary> + /// Gets all resource items. + /// </summary> + /// <param name="runSettings">The processor run settings.</param> + /// <returns>A list of resource items.</returns> + public List<IResourceListItem> GetAllResources(ProcessorRunSettings? runSettings); /// <summary> /// Tests a configuration unit. /// </summary> /// <param name="unitInternal">The unit to test.</param> + /// <param name="runSettings">The processor run settings.</param> /// <returns>A test result.</returns> - public IResourceTestItem TestResource(ConfigurationUnitInternal unitInternal); + public IResourceTestItem TestResource(ConfigurationUnitInternal unitInternal, ProcessorRunSettings? runSettings); /// <summary> /// Gets a configuration unit settings. /// </summary> /// <param name="unitInternal">The unit to get.</param> + /// <param name="runSettings">The processor run settings.</param> /// <returns>A get result.</returns> - public IResourceGetItem GetResourceSettings(ConfigurationUnitInternal unitInternal); + public IResourceGetItem GetResourceSettings(ConfigurationUnitInternal unitInternal, ProcessorRunSettings? runSettings); /// <summary> /// Sets a configuration unit settings. /// </summary> /// <param name="unitInternal">The unit to set.</param> + /// <param name="runSettings">The processor run settings.</param> /// <returns>A set result.</returns> - public IResourceSetItem SetResourceSettings(ConfigurationUnitInternal unitInternal); + public IResourceSetItem SetResourceSettings(ConfigurationUnitInternal unitInternal, ProcessorRunSettings? runSettings); /// <summary> /// Exports configuration unit. /// </summary> /// <param name="unitInternal">The unit to export.</param> + /// <param name="runSettings">The processor run settings.</param> /// <returns>A list of export results.</returns> - public IList<IResourceExportItem> ExportResource(ConfigurationUnitInternal unitInternal); + public IList<IResourceExportItem> ExportResource(ConfigurationUnitInternal unitInternal, ProcessorRunSettings? runSettings); } } diff --git a/src/Microsoft.Management.Configuration.Processor/DSCv3/Model/IResourceListItem.cs b/src/Microsoft.Management.Configuration.Processor/DSCv3/Model/IResourceListItem.cs @@ -42,5 +42,10 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Model /// Gets the author of the resource. /// </summary> public string? Author { get; } + + /// <summary> + /// Gets the path of the resource. + /// </summary> + public string? Path { get; } } } diff --git a/src/Microsoft.Management.Configuration.Processor/DSCv3/Schema_2024_04/DSCv3.cs b/src/Microsoft.Management.Configuration.Processor/DSCv3/Schema_2024_04/DSCv3.cs @@ -35,6 +35,9 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04 private const string FileParameter = "-f"; private const string StdInputIdentifier = "-"; + private const string PathEnvironmentVariable = "PATH"; + private const string DSCResourcePathEnvironmentVariable = "DSC_RESOURCE_PATH"; + private readonly ProcessorSettings processorSettings; /// <summary> @@ -55,9 +58,9 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04 } /// <inheritdoc /> - public IResourceListItem? GetResourceByType(string resourceType) + public IResourceListItem? GetResourceByType(string resourceType, ProcessorRunSettings? runSettings) { - ResourceListItem? result = this.GetResourceByType(resourceType, null); + ResourceListItem? result = this.GetResourceByTypeInternal(resourceType, null, runSettings); if (result != null) { return result; @@ -66,11 +69,11 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04 // Check for this resource within adapters List<ResourceListItem> results = new List<ResourceListItem>(); - foreach (ResourceListItem resource in this.GetAllResources()) + foreach (ResourceListItem resource in this.GetAllResources(runSettings)) { if (resource.Kind == Definitions.ResourceKind.Adapter) { - result = this.GetResourceByType(resourceType, resource.Type); + result = this.GetResourceByTypeInternal(resourceType, resource.Type, runSettings); if (result != null) { results.Add(result); @@ -87,13 +90,14 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04 } /// <inheritdoc /> - public IResourceTestItem TestResource(ConfigurationUnitInternal unitInternal) + public IResourceTestItem TestResource(ConfigurationUnitInternal unitInternal, ProcessorRunSettings? runSettings) { ProcessExecution processExecution = new ProcessExecution() { ExecutablePath = this.processorSettings.EffectiveDscExecutablePath, Arguments = new[] { PlainTextTraces, this.DiagnosticTraceLevel, ResourceCommand, TestCommand, ResourceParameter, unitInternal.QualifiedName, FileParameter, StdInputIdentifier }, Input = ConvertValueSetToJSON(unitInternal.GetExpandedSettings()), + EnvironmentVariables = CreateEnvironmentVariablesFromProcessorRunSettings(runSettings), }; if (this.RunSynchronously(processExecution)) @@ -105,13 +109,14 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04 } /// <inheritdoc /> - public IResourceGetItem GetResourceSettings(ConfigurationUnitInternal unitInternal) + public IResourceGetItem GetResourceSettings(ConfigurationUnitInternal unitInternal, ProcessorRunSettings? runSettings) { ProcessExecution processExecution = new ProcessExecution() { ExecutablePath = this.processorSettings.EffectiveDscExecutablePath, Arguments = new[] { PlainTextTraces, this.DiagnosticTraceLevel, ResourceCommand, GetCommand, ResourceParameter, unitInternal.QualifiedName, FileParameter, StdInputIdentifier }, Input = ConvertValueSetToJSON(unitInternal.GetExpandedSettings()), + EnvironmentVariables = CreateEnvironmentVariablesFromProcessorRunSettings(runSettings), }; if (this.RunSynchronously(processExecution)) @@ -123,13 +128,14 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04 } /// <inheritdoc /> - public IResourceSetItem SetResourceSettings(ConfigurationUnitInternal unitInternal) + public IResourceSetItem SetResourceSettings(ConfigurationUnitInternal unitInternal, ProcessorRunSettings? runSettings) { ProcessExecution processExecution = new ProcessExecution() { ExecutablePath = this.processorSettings.EffectiveDscExecutablePath, Arguments = new[] { PlainTextTraces, this.DiagnosticTraceLevel, ResourceCommand, SetCommand, ResourceParameter, unitInternal.QualifiedName, FileParameter, StdInputIdentifier }, Input = ConvertValueSetToJSON(unitInternal.GetExpandedSettings()), + EnvironmentVariables = CreateEnvironmentVariablesFromProcessorRunSettings(runSettings), }; if (this.RunSynchronously(processExecution)) @@ -141,7 +147,7 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04 } /// <inheritdoc /> - public IList<IResourceExportItem> ExportResource(ConfigurationUnitInternal unitInternal) + public IList<IResourceExportItem> ExportResource(ConfigurationUnitInternal unitInternal, ProcessorRunSettings? runSettings) { // 3.0 can't handle input to export; 3.1 will fix that. ValueSet expandedSettings = unitInternal.GetExpandedSettings(); @@ -154,6 +160,7 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04 { ExecutablePath = this.processorSettings.EffectiveDscExecutablePath, Arguments = new[] { PlainTextTraces, this.DiagnosticTraceLevel, ResourceCommand, ExportCommand, ResourceParameter, unitInternal.QualifiedName }, + EnvironmentVariables = CreateEnvironmentVariablesFromProcessorRunSettings(runSettings), }; if (this.RunSynchronously(processExecution)) @@ -164,6 +171,21 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04 return ConfigurationDocument.CreateFrom(GetRequiredSingleOutputLineAsJSON(processExecution, Exceptions.InvokeDscResourceException.Set, unitInternal.QualifiedName), GetDefaultJsonOptions()).InterfaceResources; } + /// <inheritdoc /> + public List<IResourceListItem> GetAllResources(ProcessorRunSettings? runSettings) + { + ProcessExecution processExecution = new ProcessExecution() + { + ExecutablePath = this.processorSettings.EffectiveDscExecutablePath, + Arguments = new[] { PlainTextTraces, this.DiagnosticTraceLevel, ResourceCommand, ListCommand }, + EnvironmentVariables = CreateEnvironmentVariablesFromProcessorRunSettings(runSettings), + }; + + this.RunSynchronously(processExecution); + + return GetOutputLinesAs<ResourceListItem>(processExecution).ToList<IResourceListItem>(); + } + private static void ThrowOnMultipleOutputLines(ProcessExecution processExecution, string method, string resourceName) { if (processExecution.Output.Count > 1) @@ -232,6 +254,23 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04 return JsonSerializer.Serialize(valueSet.ToHashtable()); } + private static List<ProcessExecutionEnvironmentVariable> CreateEnvironmentVariablesFromProcessorRunSettings(ProcessorRunSettings? runSettings) + { + List<ProcessExecutionEnvironmentVariable> result = new List<ProcessExecutionEnvironmentVariable>(); + if (runSettings is not null && !string.IsNullOrEmpty(runSettings.ResourceSearchPaths)) + { + // For exclusive search paths, adding to PATH is still needed as anything referenced in the manifest is still searched from PATH. + result.Add(new ProcessExecutionEnvironmentVariable { Name = PathEnvironmentVariable, Value = runSettings.ResourceSearchPaths, ValueType = ProcessExecutionEnvironmentVariableValueType.Prepend }); + + if (runSettings.ResourceSearchPathsExclusive) + { + result.Add(new ProcessExecutionEnvironmentVariable { Name = DSCResourcePathEnvironmentVariable, Value = runSettings.ResourceSearchPaths, ValueType = ProcessExecutionEnvironmentVariableValueType.Override }); + } + } + + return result; + } + /// <summary> /// Runs the process, waiting until it completes. /// </summary> @@ -248,12 +287,13 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04 return processExecution.ExitCode != 0; } - private ResourceListItem? GetResourceByType(string resourceType, string? adapter) + private ResourceListItem? GetResourceByTypeInternal(string resourceType, string? adapter, ProcessorRunSettings? runSettings) { ProcessExecution processExecution = new ProcessExecution() { ExecutablePath = this.processorSettings.EffectiveDscExecutablePath, Arguments = new[] { PlainTextTraces, this.DiagnosticTraceLevel, ResourceCommand, ListCommand, adapter != null ? $"-a {adapter}" : string.Empty, resourceType }, + EnvironmentVariables = CreateEnvironmentVariablesFromProcessorRunSettings(runSettings), }; this.RunSynchronously(processExecution); @@ -265,18 +305,5 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Schema_2024_04 return GetOptionalSingleOutputLineAs<ResourceListItem>(processExecution); } - - private List<ResourceListItem> GetAllResources() - { - ProcessExecution processExecution = new ProcessExecution() - { - ExecutablePath = this.processorSettings.EffectiveDscExecutablePath, - Arguments = new[] { PlainTextTraces, this.DiagnosticTraceLevel, ResourceCommand, ListCommand }, - }; - - this.RunSynchronously(processExecution); - - return GetOutputLinesAs<ResourceListItem>(processExecution); - } } } diff --git a/src/Microsoft.Management.Configuration.Processor/DSCv3/Set/DSCv3ConfigurationSetProcessor.cs b/src/Microsoft.Management.Configuration.Processor/DSCv3/Set/DSCv3ConfigurationSetProcessor.cs @@ -15,7 +15,7 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Set /// <summary> /// Configuration set processor. /// </summary> - internal sealed partial class DSCv3ConfigurationSetProcessor : ConfigurationSetProcessorBase, IConfigurationSetProcessor + internal sealed partial class DSCv3ConfigurationSetProcessor : ConfigurationSetProcessorBase, IConfigurationSetProcessor, IFindUnitProcessorsSetProcessor { private readonly ProcessorSettings processorSettings; @@ -44,7 +44,7 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Set throw new Exceptions.FindDscResourceNotFoundException(configurationUnitInternal.QualifiedName, null); } - return new DSCv3ConfigurationUnitProcessor(this.processorSettings, configurationUnitInternal, this.IsLimitMode) { SetProcessorFactory = this.SetProcessorFactory }; + return new DSCv3ConfigurationUnitProcessor(this.processorSettings, resourceDetails, configurationUnitInternal, this.IsLimitMode) { SetProcessorFactory = this.SetProcessorFactory }; } /// <inheritdoc /> @@ -62,5 +62,20 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Set return resourceDetails.GetConfigurationUnitProcessorDetails(); } + + /// <inheritdoc /> + protected override IList<IConfigurationUnitProcessorDetails> FindUnitProcessorsInternal(FindUnitProcessorsOptions findOptions) + { + this.OnDiagnostics(DiagnosticLevel.Verbose, $"Finding unit processors with following options. SearchPaths: {findOptions.SearchPaths}, SearchPathsExclusive: {findOptions.SearchPathsExclusive}, DetailFlags: [{findOptions.UnitDetailFlags}]"); + List<IConfigurationUnitProcessorDetails> result = new List<IConfigurationUnitProcessorDetails>(); + + var resourceDetailsList = this.processorSettings.FindAllResourceDetails(findOptions); + foreach (var resourceDetails in resourceDetailsList) + { + result.Add(resourceDetails.GetConfigurationUnitProcessorDetails() !); + } + + return result; + } } } diff --git a/src/Microsoft.Management.Configuration.Processor/DSCv3/Unit/DSCv3ConfigurationUnitProcessor.cs b/src/Microsoft.Management.Configuration.Processor/DSCv3/Unit/DSCv3ConfigurationUnitProcessor.cs @@ -21,17 +21,20 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Unit internal sealed partial class DSCv3ConfigurationUnitProcessor : ConfigurationUnitProcessorBase, IConfigurationUnitProcessor, IGetAllSettingsConfigurationUnitProcessor, IGetAllUnitsConfigurationUnitProcessor, IDiagnosticsSink { private readonly ProcessorSettings processorSettings; + private readonly ResourceDetails resourceDetails; /// <summary> /// Initializes a new instance of the <see cref="DSCv3ConfigurationUnitProcessor"/> class. /// </summary> /// <param name="processorSettings">The processor settings to use.</param> + /// <param name="resourceDetails">The resource to use.</param> /// <param name="unitInternal">Internal unit.</param> /// <param name="isLimitMode">Whether it is under limit mode.</param> - internal DSCv3ConfigurationUnitProcessor(ProcessorSettings processorSettings, ConfigurationUnitInternal unitInternal, bool isLimitMode = false) + internal DSCv3ConfigurationUnitProcessor(ProcessorSettings processorSettings, ResourceDetails resourceDetails, ConfigurationUnitInternal unitInternal, bool isLimitMode = false) : base(unitInternal, isLimitMode) { this.processorSettings = processorSettings; + this.resourceDetails = resourceDetails; } /// <inheritdoc /> @@ -43,25 +46,25 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Unit /// <inheritdoc /> protected override ValueSet GetSettingsInternal() { - return this.processorSettings.DSCv3.GetResourceSettings(this.UnitInternal).Settings; + return this.processorSettings.DSCv3.GetResourceSettings(this.UnitInternal, ProcessorRunSettings.CreateFromResourceDetails(this.resourceDetails)).Settings; } /// <inheritdoc /> protected override bool TestSettingsInternal() { - return this.processorSettings.DSCv3.TestResource(this.UnitInternal).InDesiredState; + return this.processorSettings.DSCv3.TestResource(this.UnitInternal, ProcessorRunSettings.CreateFromResourceDetails(this.resourceDetails)).InDesiredState; } /// <inheritdoc /> protected override bool ApplySettingsInternal() { - return this.processorSettings.DSCv3.SetResourceSettings(this.UnitInternal).RebootRequired; + return this.processorSettings.DSCv3.SetResourceSettings(this.UnitInternal, ProcessorRunSettings.CreateFromResourceDetails(this.resourceDetails)).RebootRequired; } /// <inheritdoc /> protected override IList<ValueSet>? GetAllSettingsInternal() { - var exportResult = this.processorSettings.DSCv3.ExportResource(this.UnitInternal); + var exportResult = this.processorSettings.DSCv3.ExportResource(this.UnitInternal, ProcessorRunSettings.CreateFromResourceDetails(this.resourceDetails)); string expectedType = this.UnitInternal.QualifiedName.ToLowerInvariant(); List<ValueSet> result = new List<ValueSet>(); @@ -82,7 +85,7 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Unit /// <inheritdoc /> protected override IList<ConfigurationUnit>? GetAllUnitsInternal() { - var exportResult = this.processorSettings.DSCv3.ExportResource(this.UnitInternal); + var exportResult = this.processorSettings.DSCv3.ExportResource(this.UnitInternal, ProcessorRunSettings.CreateFromResourceDetails(this.resourceDetails)); List<ConfigurationUnit> result = new List<ConfigurationUnit>(); diff --git a/src/Microsoft.Management.Configuration.Processor/Set/ConfigurationSetProcessorBase.cs b/src/Microsoft.Management.Configuration.Processor/Set/ConfigurationSetProcessorBase.cs @@ -116,6 +116,27 @@ namespace Microsoft.Management.Configuration.Processor.Set } /// <summary> + /// Gets all configuration units for the given unit type. + /// Returned units may be of types other than the one passed in. + /// </summary> + /// <param name="findOptions">Find unit processors options.</param> + /// <returns>A list of unit processor details.</returns> + public IList<IConfigurationUnitProcessorDetails> FindUnitProcessors(FindUnitProcessorsOptions findOptions) + { + try + { + this.OnDiagnostics(DiagnosticLevel.Verbose, $"Invoking `FindUnitProcessors` ..."); + + return this.FindUnitProcessorsInternal(findOptions); + } + catch (Exception ex) + { + this.OnDiagnostics(DiagnosticLevel.Error, ex.ToString()); + throw; + } + } + + /// <summary> /// Creates a configuration unit processor for the given unit. /// </summary> /// <param name="unit">Configuration unit.</param> @@ -131,6 +152,17 @@ namespace Microsoft.Management.Configuration.Processor.Set protected abstract IConfigurationUnitProcessorDetails? GetUnitProcessorDetailsInternal(ConfigurationUnit unit, ConfigurationUnitDetailFlags detailFlags); /// <summary> + /// Finds unit processors based on the input FindUnitProcessorsOptions. + /// Derive from IFindUnitProcessorsSetProcessor and implement an override to support this. + /// </summary> + /// <param name="findOptions">Find unit processors options.</param> + /// <returns>A list of unit processor details.</returns> + protected virtual IList<IConfigurationUnitProcessorDetails> FindUnitProcessorsInternal(FindUnitProcessorsOptions findOptions) + { + throw new NotImplementedException("Configuration set processor did not implement FindUnitProcessorsInternal."); + } + + /// <summary> /// Sends diagnostics to factory. /// </summary> /// <param name="level">The level of this diagnostic message.</param> diff --git a/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestDSCv3.cs b/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestDSCv3.cs @@ -101,34 +101,45 @@ namespace Microsoft.Management.Configuration.UnitTests.Helpers /// </summary> public ExportResourceDelegateType? ExportResourceDelegate { get; set; } + /// <summary> + /// Gets or sets the GetAllResources result. + /// </summary> + public List<IResourceListItem>? GetAllResourcesResult { get; set; } + /// <inheritdoc/> - public IResourceListItem? GetResourceByType(string resourceType) + public IResourceListItem? GetResourceByType(string resourceType, ProcessorRunSettings? runSettings) { return this.GetResourceByTypeResult ?? this.GetResourceByTypeDelegate?.Invoke(resourceType); } /// <inheritdoc/> - public IResourceGetItem GetResourceSettings(ConfigurationUnitInternal unitInternal) + public IResourceGetItem GetResourceSettings(ConfigurationUnitInternal unitInternal, ProcessorRunSettings? runSettings) { return this.GetResourceSettingsResult ?? this.GetResourceSettingsDelegate?.Invoke(unitInternal) ?? throw new System.NotImplementedException(); } /// <inheritdoc/> - public IResourceSetItem SetResourceSettings(ConfigurationUnitInternal unitInternal) + public IResourceSetItem SetResourceSettings(ConfigurationUnitInternal unitInternal, ProcessorRunSettings? runSettings) { return this.SetResourceSettingsResult ?? this.SetResourceSettingsDelegate?.Invoke(unitInternal) ?? throw new System.NotImplementedException(); } /// <inheritdoc/> - public IResourceTestItem TestResource(ConfigurationUnitInternal unitInternal) + public IResourceTestItem TestResource(ConfigurationUnitInternal unitInternal, ProcessorRunSettings? runSettings) { return this.TestResourceResult ?? this.TestResourceDelegate?.Invoke(unitInternal) ?? throw new System.NotImplementedException(); } /// <inheritdoc/> - public IList<IResourceExportItem> ExportResource(ConfigurationUnitInternal unitInternal) + public IList<IResourceExportItem> ExportResource(ConfigurationUnitInternal unitInternal, ProcessorRunSettings? runSettings) { return this.ExportResourceResult ?? this.ExportResourceDelegate?.Invoke(unitInternal) ?? throw new System.NotImplementedException(); } + + /// <inheritdoc/> + public List<IResourceListItem> GetAllResources(ProcessorRunSettings? runSettings) + { + return this.GetAllResourcesResult ?? throw new System.NotImplementedException(); + } } } diff --git a/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestResourceListItem.cs b/src/Microsoft.Management.Configuration.UnitTests/Helpers/TestResourceListItem.cs @@ -42,5 +42,10 @@ namespace Microsoft.Management.Configuration.UnitTests.Helpers /// Gets or sets the author. /// </summary> public string? Author { get; set; } + + /// <summary> + /// Gets or sets the path. + /// </summary> + public string? Path { get; set; } } } diff --git a/src/Microsoft.Management.Configuration/ConfigurationProcessor.cpp b/src/Microsoft.Management.Configuration/ConfigurationProcessor.cpp @@ -769,7 +769,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation return *result; } - + Configuration::GetAllConfigurationUnitSettingsResult ConfigurationProcessor::GetAllUnitSettings(const ConfigurationUnit& unit) { THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); @@ -958,6 +958,47 @@ namespace winrt::Microsoft::Management::Configuration::implementation return *result; } + Windows::Foundation::Collections::IVector<IConfigurationUnitProcessorDetails> ConfigurationProcessor::FindUnitProcessors(const FindUnitProcessorsOptions& findOptions) + { + THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); + return FindUnitProcessorsImpl(findOptions); + } + + Windows::Foundation::IAsyncOperation<Windows::Foundation::Collections::IVector<IConfigurationUnitProcessorDetails>> ConfigurationProcessor::FindUnitProcessorsAsync(const FindUnitProcessorsOptions& findOptions) + { + THROW_HR_IF(E_NOT_VALID_STATE, !m_factory); + + auto strong_this{ get_strong() }; + FindUnitProcessorsOptions localOptions = findOptions; + + co_await winrt::resume_background(); + + co_return FindUnitProcessorsImpl(localOptions, { co_await winrt::get_cancellation_token() }); + } + + Windows::Foundation::Collections::IVector<IConfigurationUnitProcessorDetails> ConfigurationProcessor::FindUnitProcessorsImpl( + const FindUnitProcessorsOptions& findOptions, + AppInstaller::WinRT::AsyncCancellation cancellation) + { + auto threadGlobals = m_threadGlobals.SetForCurrentThread(); + + IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(nullptr); + + cancellation.ThrowIfCancelled(); + + IFindUnitProcessorsSetProcessor findUnitProcessorsSetProcessor; + + if (setProcessor.try_as<IFindUnitProcessorsSetProcessor>(findUnitProcessorsSetProcessor)) + { + return findUnitProcessorsSetProcessor.FindUnitProcessors(findOptions); + } + else + { + AICLI_LOG(Config, Error, << "Set Processor does not support FindUnitProcessors operation"); + THROW_HR(WINGET_CONFIG_ERROR_NOT_SUPPORTED_BY_PROCESSOR); + } + } + IConfigurationGroupProcessor ConfigurationProcessor::GetSetGroupProcessor(const Configuration::ConfigurationSet& configurationSet) { IConfigurationSetProcessor setProcessor = m_factory.CreateSetProcessor(configurationSet); diff --git a/src/Microsoft.Management.Configuration/ConfigurationProcessor.h b/src/Microsoft.Management.Configuration/ConfigurationProcessor.h @@ -87,6 +87,9 @@ namespace winrt::Microsoft::Management::Configuration::implementation Configuration::GetAllConfigurationUnitsResult GetAllUnits(const ConfigurationUnit& unit); Windows::Foundation::IAsyncOperation<Configuration::GetAllConfigurationUnitsResult> GetAllUnitsAsync(const ConfigurationUnit& unit); + Windows::Foundation::Collections::IVector<Configuration::IConfigurationUnitProcessorDetails> FindUnitProcessors(const Configuration::FindUnitProcessorsOptions& findOptions); + Windows::Foundation::IAsyncOperation<Windows::Foundation::Collections::IVector<Configuration::IConfigurationUnitProcessorDetails>> FindUnitProcessorsAsync(const Configuration::FindUnitProcessorsOptions& findOptions); + HRESULT STDMETHODCALLTYPE SetLifetimeWatcher(IUnknown* watcher); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) @@ -121,11 +124,13 @@ namespace winrt::Microsoft::Management::Configuration::implementation AppInstaller::WinRT::AsyncProgress<TestConfigurationSetResult, TestConfigurationUnitResult> progress = {}); GetConfigurationUnitSettingsResult GetUnitSettingsImpl(const ConfigurationUnit& unit, AppInstaller::WinRT::AsyncCancellation cancellation = {}); - + GetAllConfigurationUnitSettingsResult GetAllUnitSettingsImpl(const ConfigurationUnit& unit, AppInstaller::WinRT::AsyncCancellation cancellation = {}); Configuration::GetAllConfigurationUnitsResult GetAllUnitsImpl(const ConfigurationUnit& unit, AppInstaller::WinRT::AsyncCancellation cancellation = {}); + Windows::Foundation::Collections::IVector<Configuration::IConfigurationUnitProcessorDetails> FindUnitProcessorsImpl(const Configuration::FindUnitProcessorsOptions& findOptions, AppInstaller::WinRT::AsyncCancellation cancellation = {}); + IConfigurationGroupProcessor GetSetGroupProcessor(const Configuration::ConfigurationSet& configurationSet); void SendDiagnosticsImpl(const IDiagnosticInformation& information); diff --git a/src/Microsoft.Management.Configuration/ConfigurationStaticFunctions.cpp b/src/Microsoft.Management.Configuration/ConfigurationStaticFunctions.cpp @@ -6,7 +6,8 @@ #include "ConfigurationUnit.h" #include "ConfigurationSet.h" #include "ConfigurationProcessor.h" -#include "ConfigurationParameter.h" +#include "ConfigurationParameter.h" +#include "FindUnitProcessorsOptions.h" #include <AppInstallerStrings.h> #include <winget/ConfigurationSetProcessorHandlers.h> @@ -52,6 +53,11 @@ namespace winrt::Microsoft::Management::Configuration::implementation return *make_self<wil::details::module_count_wrapper<implementation::ConfigurationParameter>>(); } + Configuration::FindUnitProcessorsOptions ConfigurationStaticFunctions::CreateFindUnitProcessorsOptions() + { + return *make_self<wil::details::module_count_wrapper<implementation::FindUnitProcessorsOptions>>(); + } + HRESULT STDMETHODCALLTYPE ConfigurationStaticFunctions::SetExperimentalState(UINT32 state) { m_state = static_cast<AppInstaller::WinRT::ConfigurationStaticsInternalsStateFlags>(state); diff --git a/src/Microsoft.Management.Configuration/ConfigurationStaticFunctions.h b/src/Microsoft.Management.Configuration/ConfigurationStaticFunctions.h @@ -16,7 +16,8 @@ namespace winrt::Microsoft::Management::Configuration::implementation Configuration::ConfigurationProcessor CreateConfigurationProcessor(IConfigurationSetProcessorFactory const& factory); bool IsConfigurationAvailable() { return true; } Windows::Foundation::IAsyncActionWithProgress<uint32_t> EnsureConfigurationAvailableAsync(); - Configuration::ConfigurationParameter CreateConfigurationParameter(); + Configuration::ConfigurationParameter CreateConfigurationParameter(); + Configuration::FindUnitProcessorsOptions CreateFindUnitProcessorsOptions(); // IConfigurationStaticsInternals HRESULT STDMETHODCALLTYPE SetExperimentalState(UINT32 state); diff --git a/src/Microsoft.Management.Configuration/FindUnitProcessorsOptions.cpp b/src/Microsoft.Management.Configuration/FindUnitProcessorsOptions.cpp @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "FindUnitProcessorsOptions.h" +#include "FindUnitProcessorsOptions.g.cpp" + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + hstring FindUnitProcessorsOptions::SearchPaths() const + { + return m_searchPaths; + } + + void FindUnitProcessorsOptions::SearchPaths(hstring const& value) + { + m_searchPaths = value; + } + + bool FindUnitProcessorsOptions::SearchPathsExclusive() const + { + return m_searchPathsExclusive; + } + + void FindUnitProcessorsOptions::SearchPathsExclusive(bool value) + { + m_searchPathsExclusive = value; + } + + Microsoft::Management::Configuration::ConfigurationUnitDetailFlags FindUnitProcessorsOptions::UnitDetailFlags() const + { + return m_detailFlags; + } + + void FindUnitProcessorsOptions::UnitDetailFlags(Microsoft::Management::Configuration::ConfigurationUnitDetailFlags value) + { + m_detailFlags = value; + } + + HRESULT STDMETHODCALLTYPE FindUnitProcessorsOptions::SetLifetimeWatcher(IUnknown* watcher) + { + return AppInstaller::WinRT::LifetimeWatcherBase::SetLifetimeWatcher(watcher); + } +} diff --git a/src/Microsoft.Management.Configuration/FindUnitProcessorsOptions.h b/src/Microsoft.Management.Configuration/FindUnitProcessorsOptions.h @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "FindUnitProcessorsOptions.g.h" +#include <winget/ILifetimeWatcher.h> +#include <winrt/Windows.Foundation.h> + +namespace winrt::Microsoft::Management::Configuration::implementation +{ + struct FindUnitProcessorsOptions : FindUnitProcessorsOptionsT<FindUnitProcessorsOptions, winrt::cloaked<AppInstaller::WinRT::ILifetimeWatcher>>, AppInstaller::WinRT::LifetimeWatcherBase + { + FindUnitProcessorsOptions() = default; + + hstring SearchPaths() const; + void SearchPaths(hstring const& value); + + bool SearchPathsExclusive() const; + void SearchPathsExclusive(bool value); + + Microsoft::Management::Configuration::ConfigurationUnitDetailFlags UnitDetailFlags() const; + void UnitDetailFlags(Microsoft::Management::Configuration::ConfigurationUnitDetailFlags value); + + HRESULT STDMETHODCALLTYPE SetLifetimeWatcher(IUnknown* watcher); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + private: + hstring m_searchPaths; + bool m_searchPathsExclusive = false; + Microsoft::Management::Configuration::ConfigurationUnitDetailFlags m_detailFlags = Microsoft::Management::Configuration::ConfigurationUnitDetailFlags::None; +#endif + }; +} + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) +namespace winrt::Microsoft::Management::Configuration::factory_implementation +{ + struct FindUnitProcessorsOptions : FindUnitProcessorsOptionsT<FindUnitProcessorsOptions, implementation::FindUnitProcessorsOptions> + { + }; +} +#endif diff --git a/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.idl b/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.idl @@ -574,6 +574,31 @@ namespace Microsoft.Management.Configuration IConfigurationUnitProcessor CreateUnitProcessor(ConfigurationUnit unit); } + // The definition of find unit processors option. + [contract(Microsoft.Management.Configuration.Contract, 4)] + runtimeclass FindUnitProcessorsOptions + { + FindUnitProcessorsOptions(); + + // Search paths for finding configuration unit processors. The value has the same format as + // PATH environment variable. Paths are absolute paths separated by semicolons(;). + String SearchPaths; + + // If the search paths provided are exclusive. + Boolean SearchPathsExclusive; + + // Defines the type of detail probing that is allowed when finding unit processors. + ConfigurationUnitDetailFlags UnitDetailFlags; + } + + // Find unit processors. + [contract(Microsoft.Management.Configuration.Contract, 4)] + interface IFindUnitProcessorsSetProcessor requires IConfigurationSetProcessor + { + // Find unit processors. + Windows.Foundation.Collections.IVector<IConfigurationUnitProcessorDetails> FindUnitProcessors(FindUnitProcessorsOptions findOptions); + } + // The result of applying an individual unit settings with an IConfigurationGroupProcessor. [contract(Microsoft.Management.Configuration.Contract, 1)] interface IApplyGroupMemberSettingsResult @@ -987,6 +1012,10 @@ namespace Microsoft.Management.Configuration // Returned units may be of types other than the one passed in. GetAllConfigurationUnitsResult GetAllUnits(ConfigurationUnit unit); Windows.Foundation.IAsyncOperation<GetAllConfigurationUnitsResult> GetAllUnitsAsync(ConfigurationUnit unit); + + // Find unit processors. + Windows.Foundation.Collections.IVector<IConfigurationUnitProcessorDetails> FindUnitProcessors(FindUnitProcessorsOptions findOptions); + Windows.Foundation.IAsyncOperation< Windows.Foundation.Collections.IVector<IConfigurationUnitProcessorDetails> > FindUnitProcessorsAsync(FindUnitProcessorsOptions findOptions); } } @@ -1022,8 +1051,16 @@ namespace Microsoft.Management.Configuration } // Top level entry point for configuration, enabling easier usage in out-of-process scenarios. + [contract(Microsoft.Management.Configuration.Contract, 4)] + interface IConfigurationStatics3 requires IConfigurationStatics2 + { + // Creates an empty configuration parameter. + FindUnitProcessorsOptions CreateFindUnitProcessorsOptions(); + } + + // Top level entry point for configuration, enabling easier usage in out-of-process scenarios. [contract(Microsoft.Management.Configuration.Contract, 1)] - runtimeclass ConfigurationStaticFunctions : [default]IConfigurationStatics, IConfigurationStatics2 + runtimeclass ConfigurationStaticFunctions : [default]IConfigurationStatics, IConfigurationStatics2, IConfigurationStatics3 { ConfigurationStaticFunctions(); } @@ -1044,6 +1081,7 @@ namespace Microsoft.Management.Configuration interface Windows.Foundation.Collections.IVectorView<TestConfigurationUnitResult>; interface Windows.Foundation.Collections.IVectorView<ConfigurationSet>; interface Windows.Foundation.Collections.IVector<ConfigurationEnvironment>; + interface Windows.Foundation.Collections.IVector<IConfigurationUnitProcessorDetails>; } // Provides a way to centralize the distribution of interfaces relevant to specific implementations of IConfigurationSetProcessorFactory. diff --git a/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj b/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj @@ -229,6 +229,7 @@ <ClInclude Include="DiagnosticInformationInstance.h" /> <ClInclude Include="ExceptionResultHelpers.h" /> <ClInclude Include="Filesystem.h" /> + <ClInclude Include="FindUnitProcessorsOptions.h" /> <ClInclude Include="GetAllConfigurationUnitSettingsResult.h" /> <ClInclude Include="GetAllConfigurationUnitsResult.h" /> <ClInclude Include="GetConfigurationSetDetailsResult.h" /> @@ -285,6 +286,7 @@ <ClCompile Include="DefaultSetGroupProcessor.cpp" /> <ClCompile Include="DiagnosticInformationInstance.cpp" /> <ClCompile Include="Filesystem.cpp" /> + <ClCompile Include="FindUnitProcessorsOptions.cpp" /> <ClCompile Include="GetAllConfigurationUnitSettingsResult.cpp" /> <ClCompile Include="GetAllConfigurationUnitsResult.cpp" /> <ClCompile Include="GetConfigurationSetDetailsResult.cpp" /> diff --git a/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj.filters b/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj.filters @@ -159,6 +159,9 @@ <ClCompile Include="GetAllConfigurationUnitsResult.cpp"> <Filter>API Source</Filter> </ClCompile> + <ClCompile Include="FindUnitProcessorsOptions.cpp"> + <Filter>API Source</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="pch.h" /> @@ -327,6 +330,9 @@ <ClInclude Include="GetAllConfigurationUnitsResult.h"> <Filter>API Headers</Filter> </ClInclude> + <ClInclude Include="FindUnitProcessorsOptions.h"> + <Filter>API Headers</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <Midl Include="Microsoft.Management.Configuration.idl" /> diff --git a/src/WindowsPackageManager/ConfigurationStaticFunctions.cpp b/src/WindowsPackageManager/ConfigurationStaticFunctions.cpp @@ -43,7 +43,8 @@ namespace ConfigurationShim DECLSPEC_UUID(WINGET_OUTOFPROC_COM_CLSID_ConfigurationStaticFunctions) ConfigurationStaticFunctionsShim : winrt::implements<ConfigurationStaticFunctionsShim, winrt::Microsoft::Management::Configuration::IConfigurationStatics, - winrt::Microsoft::Management::Configuration::IConfigurationStatics2> + winrt::Microsoft::Management::Configuration::IConfigurationStatics2, + winrt::Microsoft::Management::Configuration::IConfigurationStatics3> { ConfigurationStaticFunctionsShim() { @@ -55,7 +56,7 @@ namespace ConfigurationShim if (IsConfigurationAvailable()) { - m_statics = winrt::Microsoft::Management::Configuration::ConfigurationStaticFunctions().as<winrt::Microsoft::Management::Configuration::IConfigurationStatics2>(); + m_statics = winrt::Microsoft::Management::Configuration::ConfigurationStaticFunctions().as<winrt::Microsoft::Management::Configuration::IConfigurationStatics3>(); } } @@ -224,6 +225,20 @@ namespace ConfigurationShim return result; } + winrt::Microsoft::Management::Configuration::FindUnitProcessorsOptions CreateFindUnitProcessorsOptions() + { + THROW_HR_IF(CO_E_CLASS_DISABLED, !s_canBeCreated); + + if (!m_statics) + { + THROW_HR(APPINSTALLER_CLI_ERROR_PACKAGE_IS_STUB); + } + + auto result = m_statics.CreateFindUnitProcessorsOptions(); + result.as<AppInstaller::WinRT::ILifetimeWatcher>()->SetLifetimeWatcher(CreateLifetimeWatcher()); + return result; + } + private: // Returns a lifetime watcher object that is currently *unowned*. IUnknown* CreateLifetimeWatcher() @@ -235,7 +250,7 @@ namespace ConfigurationShim return out.detach(); } - winrt::Microsoft::Management::Configuration::IConfigurationStatics2 m_statics = nullptr; + winrt::Microsoft::Management::Configuration::IConfigurationStatics3 m_statics = nullptr; AppInstaller::ThreadLocalStorage::WingetThreadGlobals m_threadGlobals; };