diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt
index e8869de97f..60627baee8 100644
--- a/.github/actions/spelling/expect.txt
+++ b/.github/actions/spelling/expect.txt
@@ -101,6 +101,7 @@ diskfull
DISMAPI
dnld
Dobbeleer
+DONOT
dsc
DUPLICATEALIAS
dustojnikhummer
@@ -470,6 +471,7 @@ uninstalls
Unk
unknwn
Unknwnbase
+UNMARSHALING
unparsable
unvirtualized
UParse
diff --git a/doc/Settings.md b/doc/Settings.md
index 04810d5c20..3f58777d44 100644
--- a/doc/Settings.md
+++ b/doc/Settings.md
@@ -312,7 +312,6 @@ You can enable the feature as shown below.
"proxy": true
},
```
-
### sideBySide
This feature enables experimental improvements for supporting multiple instances of a package being installed on a system.
@@ -323,3 +322,14 @@ You can enable the feature as shown below.
"sideBySide": true
},
```
+
+### configureSelfElevate
+
+This feature enables configure commands to request elevation as needed.
+Currently, this means that properly attributed configuration units (and only those) will be run through an elevated process while the rest are run from the current context.
+
+```json
+ "experimentalFeatures": {
+ "configureSelfElevate": true
+ },
+```
diff --git a/schemas/JSON/settings/settings.schema.0.2.json b/schemas/JSON/settings/settings.schema.0.2.json
index ce8b5d47e1..921aaa9559 100644
--- a/schemas/JSON/settings/settings.schema.0.2.json
+++ b/schemas/JSON/settings/settings.schema.0.2.json
@@ -280,6 +280,11 @@
"description": "Enable support for improved side-by-side handling",
"type": "boolean",
"default": false
+ },
+ "configureSelfElevate": {
+ "description": "Enable configure commands request elevation as needed",
+ "type": "boolean",
+ "default": false
}
}
}
diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj
index cc301b936c..26adbfa273 100644
--- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj
+++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj
@@ -443,6 +443,7 @@
+
diff --git a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters
index a54df43884..dde62a5482 100644
--- a/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters
+++ b/src/AppInstallerCLICore/AppInstallerCLICore.vcxproj.filters
@@ -248,6 +248,9 @@
Workflows
+
+ Header Files
+
@@ -456,13 +459,19 @@
Commands
-
+
Commands
-
+
Workflows
+
+ Source Files
+
+
+ Source Files
+
diff --git a/src/AppInstallerCLICore/ConfigurationDynamicRuntimeFactory.cpp b/src/AppInstallerCLICore/ConfigurationDynamicRuntimeFactory.cpp
new file mode 100644
index 0000000000..10bac58a36
--- /dev/null
+++ b/src/AppInstallerCLICore/ConfigurationDynamicRuntimeFactory.cpp
@@ -0,0 +1,245 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+#include "pch.h"
+#include "Public/ConfigurationSetProcessorFactoryRemoting.h"
+#include
+#include
+#include
+
+using namespace winrt::Windows::Foundation;
+using namespace winrt::Microsoft::Management::Configuration;
+using namespace winrt::Windows::Storage;
+
+namespace AppInstaller::CLI::ConfigurationRemoting
+{
+ namespace anonymous
+ {
+ struct DynamicProcessorInfo
+ {
+ IConfigurationSetProcessorFactory Factory;
+ IConfigurationSetProcessor Processor;
+ };
+
+ struct DynamicSetProcessor : winrt::implements
+ {
+ using ProcessorMap = std::map;
+
+ DynamicSetProcessor(IConfigurationSetProcessorFactory defaultRemoteFactory, IConfigurationSetProcessor defaultRemoteSetProcessor, const ConfigurationSet& configurationSet) : m_configurationSet(configurationSet)
+ {
+ m_currentIntegrityLevel = Security::GetEffectiveIntegrityLevel();
+ m_setProcessors.emplace(m_currentIntegrityLevel, DynamicProcessorInfo{ defaultRemoteFactory, defaultRemoteSetProcessor });
+ }
+
+ IConfigurationUnitProcessorDetails GetUnitProcessorDetails(const ConfigurationUnit& unit, ConfigurationUnitDetailFlags detailFlags)
+ {
+ // Always get processor details from the current integrity level
+ return m_setProcessors[m_currentIntegrityLevel].Processor.GetUnitProcessorDetails(unit, detailFlags);
+ }
+
+ // Creates a configuration unit processor for the given unit.
+ IConfigurationUnitProcessor CreateUnitProcessor(const ConfigurationUnit& unit)
+ {
+ // Determine and create set processors for all required integrity levels.
+ // Doing this here avoids creating them if the only call is going to be for details (ex. `configure show`)
+ std::call_once(m_createUnitSetProcessorsOnce,
+ [&]()
+ {
+ for (const auto& existingUnit : m_configurationSet.Units())
+ {
+ Security::IntegrityLevel requiredIntegrityLevel = GetIntegrityLevelForUnit(existingUnit);
+
+ if (m_setProcessors.find(requiredIntegrityLevel) == m_setProcessors.end())
+ {
+ CreateSetProcessorForIntegrityLevel(requiredIntegrityLevel);
+ }
+ }
+ });
+
+ // Create set and unit processor for current unit.
+ Security::IntegrityLevel requiredIntegrityLevel = GetIntegrityLevelForUnit(unit);
+
+ auto itr = m_setProcessors.find(requiredIntegrityLevel);
+ if (itr == m_setProcessors.end())
+ {
+ itr = CreateSetProcessorForIntegrityLevel(requiredIntegrityLevel);
+ }
+
+ return itr->second.Processor.CreateUnitProcessor(unit);
+ }
+
+ private:
+ // Converts the string representation of SecurityContext to the integrity level
+ Security::IntegrityLevel SecurityContextToIntegrityLevel(winrt::hstring securityContext)
+ {
+ std::wstring securityContextLower = Utility::ToLower(securityContext);
+
+ if (securityContextLower == L"elevated")
+ {
+ return Security::IntegrityLevel::High;
+ }
+ else if (securityContextLower == L"restricted")
+ {
+ // Not supporting elevated callers downgrading at the moment.
+ THROW_WIN32(ERROR_NOT_SUPPORTED);
+
+ // Technically this means the default level of the user token, so if UAC is disabled it would be the only integrity level (aka current).
+ //return Security::IntegrityLevel::Medium;
+ }
+ else if (securityContextLower == L"current")
+ {
+ return m_currentIntegrityLevel;
+ }
+
+ THROW_WIN32(ERROR_NOT_SUPPORTED);
+ }
+
+ // Gets the integrity level that the given unit should be run at
+ Security::IntegrityLevel GetIntegrityLevelForUnit(const ConfigurationUnit& unit)
+ {
+ // Support for 0.2 schema via metadata value
+ // TODO: Support case insensitive lookup by iteration
+ auto unitMetadata = unit.Metadata();
+ auto securityContext = unitMetadata.TryLookup(L"SecurityContext");
+ if (securityContext)
+ {
+ auto securityContextProperty = securityContext.try_as();
+ if (securityContextProperty && securityContextProperty.Type() == PropertyType::String)
+ {
+ return SecurityContextToIntegrityLevel(securityContextProperty.GetString());
+ }
+ }
+
+ // TODO: Support for 0.3 schema will require a group processor wrapper
+
+ return m_currentIntegrityLevel;
+ }
+
+ // Serializes the set properties to be sent to the remote server
+ std::string SerializeSetProperties()
+ {
+ Json::Value json{ Json::ValueType::objectValue };
+ json["path"] = winrt::to_string(m_configurationSet.Path());
+ Json::StreamWriterBuilder writerBuilder;
+ writerBuilder.settings_["indentation"] = "\t";
+ return Json::writeString(writerBuilder, json);
+ }
+
+ ///
+ /// Creates a separate configuration set containing high integrity units and returns the serialized string value.
+ ///
+ /// Serialized string value.
+ std::string SerializeHighIntegrityLevelSet()
+ {
+ ConfigurationSet highIntegritySet;
+
+ // TODO: Currently we only support schema version 0.2 for handling elevated integrity levels.
+ highIntegritySet.SchemaVersion(L"0.2");
+
+ std::vector highIntegrityUnits;
+ auto units = m_configurationSet.Units();
+
+ for (auto unit : units)
+ {
+ if (unit.IsActive() && GetIntegrityLevelForUnit(unit) == Security::IntegrityLevel::High)
+ {
+ highIntegrityUnits.emplace_back(unit);
+ }
+ }
+
+ highIntegritySet.Units(std::move(highIntegrityUnits));
+
+ // Serialize high integrity set and return output string.
+ Streams::InMemoryRandomAccessStream memoryStream;
+ highIntegritySet.Serialize(memoryStream);
+
+ Streams::DataReader reader(memoryStream.GetInputStreamAt(0));
+ reader.UnicodeEncoding(Streams::UnicodeEncoding::Utf8);
+ reader.LoadAsync((uint32_t)memoryStream.Size());
+
+ winrt::hstring result;
+ uint32_t bytesToRead = reader.UnconsumedBufferLength();
+
+ if (bytesToRead > 0)
+ {
+ result = reader.ReadString(bytesToRead);
+ }
+
+ return winrt::to_string(result);
+ }
+
+ ProcessorMap::iterator CreateSetProcessorForIntegrityLevel(Security::IntegrityLevel integrityLevel)
+ {
+ IConfigurationSetProcessorFactory factory;
+
+ // If we got here, the only option is that the current integrity level is not High.
+ if (integrityLevel == Security::IntegrityLevel::High)
+ {
+ factory = CreateOutOfProcessFactory(true, SerializeSetProperties(), SerializeHighIntegrityLevelSet());
+ }
+ else
+ {
+ THROW_WIN32(ERROR_NOT_SUPPORTED);
+ }
+
+ return m_setProcessors.emplace(integrityLevel, DynamicProcessorInfo{ factory, factory.CreateSetProcessor(m_configurationSet) }).first;
+ }
+
+ Security::IntegrityLevel m_currentIntegrityLevel;
+ ProcessorMap m_setProcessors;
+ ConfigurationSet m_configurationSet;
+ std::once_flag m_createUnitSetProcessorsOnce;
+ };
+
+ // This is implemented completely in the packaged context for now, if we want to make it more configurable, we will probably want to move it to configuration and
+ // have this implementation leverage that one with an event handler for the packaged specifics.
+ // TODO: Add SetProcessorFactory::IPwshConfigurationSetProcessorFactoryProperties and pass values along to sets on creation
+ // In turn, any properties must only be set via the command line (or eventual UI requests to the user).
+ struct DynamicFactory : winrt::implements>, WinRT::LifetimeWatcherBase
+ {
+ DynamicFactory()
+ {
+ m_defaultRemoteFactory = CreateOutOfProcessFactory();
+ }
+
+ IConfigurationSetProcessor CreateSetProcessor(const ConfigurationSet& configurationSet)
+ {
+ return winrt::make(m_defaultRemoteFactory, m_defaultRemoteFactory.CreateSetProcessor(configurationSet), configurationSet);
+ }
+
+ winrt::event_token Diagnostics(const EventHandler&)
+ {
+ // TODO: If we want diagnostics here, see ConfigurationProcessor for how to integrate nicely with the infrastructure.
+ // Best solution is probably to create a base class that both can leverage to handle it cleanly.
+ return {};
+ }
+
+ void Diagnostics(const winrt::event_token&) noexcept
+ {
+ }
+
+ DiagnosticLevel MinimumLevel()
+ {
+ return m_minimumLevel;
+ }
+
+ void MinimumLevel(DiagnosticLevel value)
+ {
+ m_minimumLevel = value;
+ }
+
+ HRESULT STDMETHODCALLTYPE SetLifetimeWatcher(IUnknown* watcher)
+ {
+ return WinRT::LifetimeWatcherBase::SetLifetimeWatcher(watcher);
+ }
+
+ private:
+ IConfigurationSetProcessorFactory m_defaultRemoteFactory;
+ DiagnosticLevel m_minimumLevel = DiagnosticLevel::Informational;
+ };
+ }
+
+ winrt::Microsoft::Management::Configuration::IConfigurationSetProcessorFactory CreateDynamicRuntimeFactory()
+ {
+ return winrt::make();
+ }
+}
diff --git a/src/AppInstallerCLICore/ConfigurationSetProcessorFactoryRemoting.cpp b/src/AppInstallerCLICore/ConfigurationSetProcessorFactoryRemoting.cpp
index 44a5dff42c..ec606883c9 100644
--- a/src/AppInstallerCLICore/ConfigurationSetProcessorFactoryRemoting.cpp
+++ b/src/AppInstallerCLICore/ConfigurationSetProcessorFactoryRemoting.cpp
@@ -5,6 +5,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -14,86 +15,64 @@ using namespace winrt::Microsoft::Management::Configuration;
namespace AppInstaller::CLI::ConfigurationRemoting
{
- namespace details
- {
- // The layout of the memory being mapped.
- struct MappedMemoryValue
- {
- // The size of the memory block itself.
- static constexpr ULONG s_MemorySize = 4 << 10;
-
- HRESULT Result;
- ULONG FactorySize;
- uint8_t FactoryObject[1];
-
- // The maximum size of the marshalled object.
- static constexpr ULONG MaxFactorySize()
- {
- static_assert(s_MemorySize > offsetof(MappedMemoryValue, FactoryObject));
- return s_MemorySize - offsetof(MappedMemoryValue, FactoryObject);
- }
- };
- }
-
namespace
{
// The executable file name for the remote server process.
constexpr std::wstring_view s_RemoteServerFileName = L"ConfigurationRemotingServer\\ConfigurationRemotingServer.exe";
- // Represents a remote factory object that was created from a specific process.
- struct RemoteFactory : winrt::implements>, WinRT::LifetimeWatcherBase
+ // The string used to divide the arguments sent to the remote server
+ constexpr std::wstring_view s_ArgumentsDivider = L"\n~~~~~~\n";
+
+ // A helper with a convenient function that we use to receive the remote factory object.
+ struct RemoteFactoryCallback : winrt::implements
{
- RemoteFactory()
+ RemoteFactoryCallback()
{
- AICLI_LOG(Config, Verbose, << "Launching process for configuration processing...");
-
- // Security attributes to set handles as inherited.
- SECURITY_ATTRIBUTES securityAttributes{};
- securityAttributes.nLength = sizeof(securityAttributes);
- securityAttributes.bInheritHandle = TRUE;
- securityAttributes.lpSecurityDescriptor = nullptr;
-
- // Create file mapping backed by page file.
- wil::unique_handle memoryHandle{ CreateFileMappingW(INVALID_HANDLE_VALUE, &securityAttributes, PAGE_READWRITE, 0, details::MappedMemoryValue::s_MemorySize, nullptr) };
- THROW_LAST_ERROR_IF_NULL(memoryHandle);
-
- // Map the memory into the process.
- wil::unique_mapview_ptr mappedMemory{ reinterpret_cast(MapViewOfFile(memoryHandle.get(), FILE_MAP_READ | FILE_MAP_WRITE , 0, 0, 0)) };
- THROW_LAST_ERROR_IF_NULL(mappedMemory);
- // Initialize the result to a failure in case the other process never comes through.
- mappedMemory->Result = E_FAIL;
-
- // Create an event that the remote process will signal to indicate it has completed creating the object.
- wil::unique_event initEvent;
- initEvent.create(wil::EventOptions::None, nullptr, &securityAttributes);
+ m_initEvent.create();
+ }
- // Create the event that the remote process will wait on to keep the object alive.
- m_completionEvent.create(wil::EventOptions::None, nullptr, &securityAttributes);
- auto completeEventIfFailureDuringConstruction = wil::scope_exit([&]() { m_completionEvent.SetEvent(); });
+ ConfigurationUnit CreateConfigurationUnit()
+ {
+ THROW_HR(E_NOTIMPL);
+ }
- wil::unique_process_handle thisProcessHandle;
- THROW_IF_WIN32_BOOL_FALSE(DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(), &thisProcessHandle, 0, TRUE, DUPLICATE_SAME_ACCESS));
+ ConfigurationSet CreateConfigurationSet()
+ {
+ THROW_HR(E_NOTIMPL);
+ }
- // Arguments are:
- // server.exe
- std::wostringstream argumentsStream;
- argumentsStream << s_RemoteServerFileName << L' ' << reinterpret_cast(memoryHandle.get()) << L' ' << reinterpret_cast(initEvent.get())
- << L' ' << reinterpret_cast(m_completionEvent.get()) << L' ' << reinterpret_cast(thisProcessHandle.get());
- std::wstring arguments = argumentsStream.str();
+ IAsyncOperation CreateConfigurationSetProcessorFactoryAsync(winrt::hstring handler)
+ {
+ // TODO: Ensure calling process has same package identity
+ std::wstringstream stringStream{ std::wstring{ static_cast(handler) } };
+ stringStream >> m_result;
+ m_initEvent.SetEvent();
+ return nullptr;
+ }
- std::filesystem::path serverPath = Runtime::GetPathTo(Runtime::PathName::SelfPackageRoot);
- serverPath /= s_RemoteServerFileName;
+ ConfigurationProcessor CreateConfigurationProcessor(IConfigurationSetProcessorFactory factory)
+ {
+ // TODO: Ensure calling process has same package identity
+ m_factory = factory;
+ m_initEvent.SetEvent();
+ return nullptr;
+ }
- STARTUPINFOW startupInfo{};
- startupInfo.cb = sizeof(startupInfo);
- wil::unique_process_information processInformation;
+ bool IsConfigurationAvailable()
+ {
+ THROW_HR(E_NOTIMPL);
+ }
- THROW_IF_WIN32_BOOL_FALSE(CreateProcessW(serverPath.c_str(), &arguments[0], nullptr, nullptr, TRUE, DETACHED_PROCESS, nullptr, nullptr, &startupInfo, &processInformation));
- AICLI_LOG(Config, Verbose, << " Configuration remote PID is " << processInformation.dwProcessId);
+ IAsyncActionWithProgress EnsureConfigurationAvailableAsync()
+ {
+ THROW_HR(E_NOTIMPL);
+ }
+ IConfigurationSetProcessorFactory Wait(HANDLE process)
+ {
HANDLE waitHandles[2];
- waitHandles[0] = initEvent.get();
- waitHandles[1] = processInformation.hProcess;
+ waitHandles[0] = m_initEvent.get();
+ waitHandles[1] = process;
for (;;)
{
@@ -116,7 +95,7 @@ namespace AppInstaller::CLI::ConfigurationRemoting
// If the process exited, then try to use the exit code.
DWORD processExitCode = 0;
- if (waitResult == (WAIT_OBJECT_0 + 1) && GetExitCodeProcess(processInformation.hProcess, &processExitCode) && FAILED(processExitCode))
+ if (waitResult == (WAIT_OBJECT_0 + 1) && GetExitCodeProcess(process, &processExitCode) && FAILED(processExitCode))
{
THROW_HR(static_cast(processExitCode));
}
@@ -127,21 +106,102 @@ namespace AppInstaller::CLI::ConfigurationRemoting
}
}
- // Report on a failure in the server.
- THROW_IF_FAILED(mappedMemory->Result);
+ THROW_IF_FAILED(m_result);
+
+ // Double-check the result
+ THROW_HR_IF(E_POINTER, !m_factory);
+ return m_factory;
+ }
- THROW_HR_IF(E_NOT_SUFFICIENT_BUFFER, mappedMemory->FactorySize == 0);
- THROW_HR_IF(E_NOT_SUFFICIENT_BUFFER, mappedMemory->FactorySize > details::MappedMemoryValue::MaxFactorySize());
+ private:
+ IConfigurationSetProcessorFactory m_factory;
+ HRESULT m_result = S_OK;
+ wil::unique_event m_initEvent;
+ };
+
+ // Represents a remote factory object that was created from a specific process.
+ struct RemoteFactory : winrt::implements>, WinRT::LifetimeWatcherBase
+ {
+ RemoteFactory(bool useRunAs, const std::string& properties, const std::string& restrictions)
+ {
+ AICLI_LOG(Config, Verbose, << "Launching process for configuration processing...");
+
+ // Create our callback and marshal it
+ auto callback = winrt::make_self();
wil::com_ptr stream;
THROW_IF_FAILED(CreateStreamOnHGlobal(nullptr, TRUE, &stream));
- THROW_IF_FAILED(stream->Write(mappedMemory->FactoryObject, mappedMemory->FactorySize, nullptr));
+
+ THROW_IF_FAILED(CoMarshalInterface(stream.get(), winrt::guid_of(), reinterpret_cast<::IUnknown*>(winrt::get_abi(callback.as())), MSHCTX_LOCAL, nullptr, MSHLFLAGS_NORMAL));
+
+ ULARGE_INTEGER streamSize{};
+ THROW_IF_FAILED(stream->Seek({}, STREAM_SEEK_CUR, &streamSize));
+
+ ULONG bufferSize = static_cast(streamSize.QuadPart);
+ std::vector buffer;
+ buffer.resize(bufferSize);
+
THROW_IF_FAILED(stream->Seek({}, STREAM_SEEK_SET, nullptr));
+ ULONG bytesRead = 0;
+ THROW_IF_FAILED(stream->Read(&buffer[0], bufferSize, &bytesRead));
+ THROW_HR_IF(E_UNEXPECTED, bytesRead != bufferSize);
+
+ std::wstring marshalledCallback = Utility::ConvertToUTF16(Utility::ConvertToHexString(buffer));
+
+ // Create the event that the remote process will wait on to keep the object alive.
+ std::wstring completionEventName = Utility::CreateNewGuidNameWString();
+ m_completionEvent.create(wil::EventOptions::None, completionEventName.c_str());
+ auto completeEventIfFailureDuringConstruction = wil::scope_exit([&]() { m_completionEvent.SetEvent(); });
+
+ // This will be presented to the user so it must be formatted nicely.
+ // Arguments are:
+ // server.exe
+ //
+ // Optionally, we may also place additional data that limits what the server may do as:
+ // ~~~~~~
+ // { "JSON properties" }
+ // ~~~~~~
+ // YAML configuration set definition
+ std::wostringstream argumentsStream;
+ argumentsStream << s_RemoteServerFileName << L' ' << marshalledCallback << L' ' << completionEventName << L' ' << GetCurrentProcessId();
+
+ if (!properties.empty() && !restrictions.empty())
+ {
+ argumentsStream << L' ' << s_ArgumentsDivider << Utility::ConvertToUTF16(properties) << s_ArgumentsDivider << Utility::ConvertToUTF16(restrictions);
+ }
- wil::com_ptr<::IUnknown> output;
- THROW_IF_FAILED(CoUnmarshalInterface(stream.get(), winrt::guid_of(), reinterpret_cast(&output)));
+ std::wstring arguments = argumentsStream.str();
+
+ std::filesystem::path serverPath = Runtime::GetPathTo(Runtime::PathName::SelfPackageRoot);
+ serverPath /= s_RemoteServerFileName;
+ std::wstring serverPathString = serverPath.wstring();
+
+ // Per documentation, the maximum length is 32767 *counting* the null.
+ THROW_WIN32_IF(ERROR_BUFFER_OVERFLOW, serverPathString.length() > 32766);
+ THROW_WIN32_IF(ERROR_BUFFER_OVERFLOW, arguments.length() > 32766);
+ // Overflow safe since we verify that each of the individual strings is also small.
+ // +1 for the space between the path and args.
+ THROW_WIN32_IF(ERROR_BUFFER_OVERFLOW, serverPathString.length() + 1 + arguments.length() > 32766);
+
+ SHELLEXECUTEINFOW execInfo = { 0 };
+ execInfo.cbSize = sizeof(execInfo);
+ execInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI | SEE_MASK_NO_CONSOLE;
+ execInfo.lpFile = serverPath.c_str();
+ execInfo.lpParameters = arguments.c_str();
+ execInfo.nShow = SW_HIDE;
+
+ if (useRunAs)
+ {
+ execInfo.lpVerb = L"runas";
+ }
+
+ THROW_LAST_ERROR_IF(!ShellExecuteExW(&execInfo) || !execInfo.hProcess);
+
+ wil::unique_process_handle process{ execInfo.hProcess };
+ AICLI_LOG(Config, Verbose, << " Configuration remote PID is " << GetProcessId(process.get()));
+
+ m_remoteFactory = callback->Wait(process.get());
AICLI_LOG(Config, Verbose, << "... configuration processing connection established.");
- m_remoteFactory = IConfigurationSetProcessorFactory{ output.detach(), winrt::take_ownership_from_abi };
completeEventIfFailureDuringConstruction.release();
}
@@ -263,52 +323,55 @@ namespace AppInstaller::CLI::ConfigurationRemoting
};
}
- IConfigurationSetProcessorFactory CreateOutOfProcessFactory()
+ IConfigurationSetProcessorFactory CreateOutOfProcessFactory(bool useRunAs, const std::string& properties, const std::string& restrictions)
{
- return winrt::make();
+ return winrt::make(useRunAs, properties, restrictions);
}
}
-HRESULT WindowsPackageManagerConfigurationCompleteOutOfProcessFactoryInitialization(HRESULT result, void* factory, uint64_t memoryHandleIntPtr, uint64_t initEventHandleIntPtr, uint64_t completionMutexHandleIntPtr, uint64_t parentProcessIntPtr) try
+HRESULT WindowsPackageManagerConfigurationCompleteOutOfProcessFactoryInitialization(HRESULT result, void* factory, LPWSTR staticsCallback, LPWSTR completionEventName, DWORD parentProcessId) try
{
- using namespace AppInstaller::CLI::ConfigurationRemoting;
-
- RETURN_HR_IF(E_POINTER, !memoryHandleIntPtr);
-
- wil::unique_handle memoryHandle{ reinterpret_cast(memoryHandleIntPtr) };
- wil::unique_mapview_ptr mappedMemory{ reinterpret_cast(MapViewOfFile(memoryHandle.get(), FILE_MAP_WRITE, 0, 0, 0)) };
- RETURN_LAST_ERROR_IF_NULL(mappedMemory);
+ {
+ wil::com_ptr globalOptions;
+ RETURN_IF_FAILED(CoCreateInstance(CLSID_GlobalOptions, nullptr, CLSCTX_INPROC, IID_PPV_ARGS(&globalOptions)));
+ RETURN_IF_FAILED(globalOptions->Set(COMGLB_RO_SETTINGS, COMGLB_FAST_RUNDOWN));
+ RETURN_IF_FAILED(globalOptions->Set(COMGLB_UNMARSHALING_POLICY, COMGLB_UNMARSHALING_POLICY_STRONG));
+ RETURN_IF_FAILED(globalOptions->Set(COMGLB_EXCEPTION_HANDLING, COMGLB_EXCEPTION_DONOT_HANDLE_ANY));
+ }
- mappedMemory->Result = result;
- mappedMemory->FactorySize = 0;
+ using namespace AppInstaller;
+ using namespace AppInstaller::CLI::ConfigurationRemoting;
- if (SUCCEEDED(result))
- {
- wil::com_ptr stream;
- RETURN_IF_FAILED(CreateStreamOnHGlobal(nullptr, TRUE, &stream));
+ RETURN_HR_IF(E_POINTER, !staticsCallback);
- RETURN_IF_FAILED(CoMarshalInterface(stream.get(), winrt::guid_of(), reinterpret_cast<::IUnknown*>(factory), MSHCTX_LOCAL, nullptr, MSHLFLAGS_NORMAL));
+ auto callbackBytes = Utility::ParseFromHexString(Utility::ConvertToUTF8(staticsCallback));
+ RETURN_HR_IF(E_INVALIDARG, callbackBytes.size() > (1 << 15));
- ULARGE_INTEGER streamSize{};
- RETURN_IF_FAILED(stream->Seek({}, STREAM_SEEK_CUR, &streamSize));
- RETURN_HR_IF(E_NOT_SUFFICIENT_BUFFER, streamSize.QuadPart > details::MappedMemoryValue::MaxFactorySize());
+ wil::com_ptr stream;
+ RETURN_IF_FAILED(CreateStreamOnHGlobal(nullptr, TRUE, &stream));
+ RETURN_IF_FAILED(stream->Write(&callbackBytes[0], static_cast(callbackBytes.size()), nullptr));
+ RETURN_IF_FAILED(stream->Seek({}, STREAM_SEEK_SET, nullptr));
- ULONG bufferSize = static_cast(streamSize.QuadPart);
+ wil::com_ptr<::IUnknown> output;
+ RETURN_IF_FAILED(CoUnmarshalInterface(stream.get(), winrt::guid_of(), reinterpret_cast(&output)));
- RETURN_IF_FAILED(stream->Seek({}, STREAM_SEEK_SET, nullptr));
- ULONG bytesRead = 0;
- RETURN_IF_FAILED(stream->Read(mappedMemory->FactoryObject, bufferSize, &bytesRead));
- RETURN_HR_IF(E_UNEXPECTED, bytesRead != bufferSize);
+ IConfigurationStatics callback{ output.detach(), winrt::take_ownership_from_abi };
- mappedMemory->FactorySize = bufferSize;
+ if (FAILED(result))
+ {
+ std::ignore = callback.CreateConfigurationSetProcessorFactoryAsync(std::to_wstring(result));
+ }
+ else
+ {
+ IConfigurationSetProcessorFactory factoryObject;
+ winrt::copy_from_abi(factoryObject, factory);
+ std::ignore = callback.CreateConfigurationProcessor(factoryObject);
}
-
- wil::unique_event initEvent{ reinterpret_cast(initEventHandleIntPtr) };
- initEvent.SetEvent();
// Wait until the caller releases the object (signalling the event) or the parent process exits
- wil::unique_event completionEvent{ reinterpret_cast(completionMutexHandleIntPtr) };
- wil::unique_process_handle parentProcess{ reinterpret_cast(parentProcessIntPtr) };
+ wil::unique_event completionEvent;
+ completionEvent.open(completionEventName);
+ wil::unique_process_handle parentProcess{ OpenProcess(SYNCHRONIZE, FALSE, parentProcessId) };
HANDLE waitHandles[2];
waitHandles[0] = completionEvent.get();
diff --git a/src/AppInstallerCLICore/Public/ConfigurationSetProcessorFactoryRemoting.h b/src/AppInstallerCLICore/Public/ConfigurationSetProcessorFactoryRemoting.h
index 78fe43bfb7..9c44521f13 100644
--- a/src/AppInstallerCLICore/Public/ConfigurationSetProcessorFactoryRemoting.h
+++ b/src/AppInstallerCLICore/Public/ConfigurationSetProcessorFactoryRemoting.h
@@ -7,8 +7,11 @@
namespace AppInstaller::CLI::ConfigurationRemoting
{
// Creates a factory in another process
- winrt::Microsoft::Management::Configuration::IConfigurationSetProcessorFactory CreateOutOfProcessFactory();
+ winrt::Microsoft::Management::Configuration::IConfigurationSetProcessorFactory CreateOutOfProcessFactory(bool useRunAs = false, const std::string& properties = {}, const std::string& restrictions = {});
+
+ // Creates a factory that can route configurations to the appropriate internal factory.
+ winrt::Microsoft::Management::Configuration::IConfigurationSetProcessorFactory CreateDynamicRuntimeFactory();
}
// Export for use by the out of process factory server to report its initialization.
-HRESULT WindowsPackageManagerConfigurationCompleteOutOfProcessFactoryInitialization(HRESULT result, void* factory, uint64_t memoryHandle, uint64_t initEventHandle, uint64_t completionMutexHandle);
+HRESULT WindowsPackageManagerConfigurationCompleteOutOfProcessFactoryInitialization(HRESULT result, void* factory, LPWSTR staticsCallback, LPWSTR completionEventName, DWORD parentProcessId);
diff --git a/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp b/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp
index 279be1662a..58736ea4fb 100644
--- a/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp
+++ b/src/AppInstallerCLICore/Workflows/ConfigurationFlow.cpp
@@ -4,13 +4,15 @@
#include "ConfigurationFlow.h"
#include "PromptFlow.h"
#include "Public/ConfigurationSetProcessorFactoryRemoting.h"
+#include "ConfigurationCommon.h"
+#include "ConfigurationWingetDscModuleUnitValidation.h"
#include
#include
+#include
#include
-#include
+#include
#include
-#include "ConfigurationCommon.h"
-#include "ConfigurationWingetDscModuleUnitValidation.h"
+#include
using namespace AppInstaller::CLI::Execution;
using namespace winrt::Microsoft::Management::Configuration;
@@ -86,8 +88,21 @@ namespace AppInstaller::CLI::Workflow
}
#endif
- auto factory = ConfigurationRemoting::CreateOutOfProcessFactory();
- Configuration::SetModulePath(context, factory);
+ IConfigurationSetProcessorFactory factory;
+
+ // Since downgrading is not currently supported, only use dynamic if not running as admin.
+ if (Settings::ExperimentalFeature::IsEnabled(Settings::ExperimentalFeature::Feature::ConfigureSelfElevation) &&
+ !Runtime::IsRunningAsAdmin())
+ {
+ factory = ConfigurationRemoting::CreateDynamicRuntimeFactory();
+ // TODO: Implement SetProcessorFactory::IPwshConfigurationSetProcessorFactoryProperties on dynamic factory
+ }
+ else
+ {
+ factory = ConfigurationRemoting::CreateOutOfProcessFactory();
+ Configuration::SetModulePath(context, factory);
+ }
+
return factory;
}
diff --git a/src/AppInstallerCommonCore/ExperimentalFeature.cpp b/src/AppInstallerCommonCore/ExperimentalFeature.cpp
index 5b7845a042..9475a32c5d 100644
--- a/src/AppInstallerCommonCore/ExperimentalFeature.cpp
+++ b/src/AppInstallerCommonCore/ExperimentalFeature.cpp
@@ -48,6 +48,8 @@ namespace AppInstaller::Settings
return userSettings.Get();
case ExperimentalFeature::Feature::Proxy:
return userSettings.Get();
+ case ExperimentalFeature::Feature::ConfigureSelfElevation:
+ return userSettings.Get();
default:
THROW_HR(E_UNEXPECTED);
}
@@ -85,6 +87,8 @@ namespace AppInstaller::Settings
return ExperimentalFeature{ "Side-by-side improvements", "sideBySide", "https://aka.ms/winget-settings", Feature::SideBySide };
case Feature::Proxy:
return ExperimentalFeature{ "Proxy", "proxy", "https://aka.ms/winget-settings", Feature::Proxy };
+ case Feature::ConfigureSelfElevation:
+ return ExperimentalFeature{ "Configure Self Elevation", "configureSelfElevate", "https://aka.ms/winget-settings", Feature::ConfigureSelfElevation };
default:
THROW_HR(E_UNEXPECTED);
}
diff --git a/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h b/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h
index 00e47f419c..9c5684af2c 100644
--- a/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h
+++ b/src/AppInstallerCommonCore/Public/winget/ExperimentalFeature.h
@@ -27,6 +27,7 @@ namespace AppInstaller::Settings
Configuration03 = 0x4,
Proxy = 0x8,
SideBySide = 0x10,
+ ConfigureSelfElevation = 0x20,
Max, // This MUST always be after all experimental features
// Features listed after Max will not be shown with the features command
diff --git a/src/AppInstallerCommonCore/Public/winget/UserSettings.h b/src/AppInstallerCommonCore/Public/winget/UserSettings.h
index fa8f1b412d..94e7f67500 100644
--- a/src/AppInstallerCommonCore/Public/winget/UserSettings.h
+++ b/src/AppInstallerCommonCore/Public/winget/UserSettings.h
@@ -74,6 +74,7 @@ namespace AppInstaller::Settings
EFConfiguration03,
EFSideBySide,
EFProxy,
+ EFConfigureSelfElevation,
// Telemetry
TelemetryDisable,
// Install behavior
@@ -155,6 +156,7 @@ namespace AppInstaller::Settings
SETTINGMAPPING_SPECIALIZATION(Setting::EFConfiguration03, bool, bool, false, ".experimentalFeatures.configuration03"sv);
SETTINGMAPPING_SPECIALIZATION(Setting::EFSideBySide, bool, bool, false, ".experimentalFeatures.sideBySide"sv);
SETTINGMAPPING_SPECIALIZATION(Setting::EFProxy, bool, bool, false, ".experimentalFeatures.proxy"sv);
+ SETTINGMAPPING_SPECIALIZATION(Setting::EFConfigureSelfElevation, bool, bool, false, ".experimentalFeatures.configureSelfElevate"sv);
// Telemetry
SETTINGMAPPING_SPECIALIZATION(Setting::TelemetryDisable, bool, bool, false, ".telemetry.disable"sv);
// Install behavior
diff --git a/src/AppInstallerCommonCore/UserSettings.cpp b/src/AppInstallerCommonCore/UserSettings.cpp
index 091ba83423..926c45a110 100644
--- a/src/AppInstallerCommonCore/UserSettings.cpp
+++ b/src/AppInstallerCommonCore/UserSettings.cpp
@@ -263,6 +263,7 @@ namespace AppInstaller::Settings
WINGET_VALIDATE_PASS_THROUGH(EFConfiguration03)
WINGET_VALIDATE_PASS_THROUGH(EFSideBySide)
WINGET_VALIDATE_PASS_THROUGH(EFProxy)
+ WINGET_VALIDATE_PASS_THROUGH(EFConfigureSelfElevation)
WINGET_VALIDATE_PASS_THROUGH(AnonymizePathForDisplay)
WINGET_VALIDATE_PASS_THROUGH(TelemetryDisable)
WINGET_VALIDATE_PASS_THROUGH(InteractivityDisable)
diff --git a/src/AppInstallerSharedLib/AppInstallerStrings.cpp b/src/AppInstallerSharedLib/AppInstallerStrings.cpp
index c763f8453b..17bb54a720 100644
--- a/src/AppInstallerSharedLib/AppInstallerStrings.cpp
+++ b/src/AppInstallerSharedLib/AppInstallerStrings.cpp
@@ -869,11 +869,22 @@ namespace AppInstaller::Utility
std::string ConvertGuidToString(const GUID& value)
{
- wchar_t buffer[256];
+ wchar_t buffer[40];
THROW_HR_IF(E_UNEXPECTED, !StringFromGUID2(value, buffer, ARRAYSIZE(buffer)));
return ConvertToUTF8(buffer);
}
+ std::wstring CreateNewGuidNameWString()
+ {
+ GUID guid;
+ THROW_IF_FAILED(CoCreateGuid(&guid));
+
+ wchar_t buffer[40];
+ THROW_HR_IF(E_UNEXPECTED, StringFromGUID2(guid, buffer, ARRAYSIZE(buffer)) != 39);
+
+ return std::wstring{ &buffer[1], 36 };
+ }
+
bool IsDwordFlagSet(const std::string& value)
{
if (std::empty(value))
diff --git a/src/AppInstallerSharedLib/Public/AppInstallerStrings.h b/src/AppInstallerSharedLib/Public/AppInstallerStrings.h
index 51ca0373c6..e04e4912ad 100644
--- a/src/AppInstallerSharedLib/Public/AppInstallerStrings.h
+++ b/src/AppInstallerSharedLib/Public/AppInstallerStrings.h
@@ -269,6 +269,9 @@ namespace AppInstaller::Utility
// Converts the given GUID value to a string.
std::string ConvertGuidToString(const GUID& value);
+ // Creates a new GUID and returns the string value.
+ std::wstring CreateNewGuidNameWString();
+
// Converts the input string to a DWORD value using std::stoul and returns a boolean value based on the resulting DWORD value.
bool IsDwordFlagSet(const std::string& value);
}
diff --git a/src/AppInstallerSharedLib/Public/winget/ConfigurationSetProcessorHandlers.h b/src/AppInstallerSharedLib/Public/winget/ConfigurationSetProcessorHandlers.h
index 3c6d783b61..3d675211e8 100644
--- a/src/AppInstallerSharedLib/Public/winget/ConfigurationSetProcessorHandlers.h
+++ b/src/AppInstallerSharedLib/Public/winget/ConfigurationSetProcessorHandlers.h
@@ -8,4 +8,5 @@
namespace AppInstaller::Configuration
{
constexpr std::wstring_view PowerShellHandlerIdentifier = L"pwsh";
+ constexpr std::wstring_view DynamicRuntimeHandlerIdentifier = L"{73fea39f-6f4a-41c9-ba94-6fd14d633e40}";
}
diff --git a/src/ConfigurationRemotingServer/Program.cs b/src/ConfigurationRemotingServer/Program.cs
index 1a7d11890e..c875c04062 100644
--- a/src/ConfigurationRemotingServer/Program.cs
+++ b/src/ConfigurationRemotingServer/Program.cs
@@ -1,8 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
-using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
+using System.Runtime.Loader;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
@@ -13,6 +13,57 @@
namespace ConfigurationRemotingServer
{
+ ///
+ /// Custom assembly load context.
+ ///
+ internal class NativeAssemblyLoadContext : AssemblyLoadContext
+ {
+ private static readonly string PackageRootPath;
+
+ private static readonly NativeAssemblyLoadContext NativeALC = new();
+
+ static NativeAssemblyLoadContext()
+ {
+ var self = typeof(NativeAssemblyLoadContext).Assembly;
+ PackageRootPath = Path.Combine(
+ Path.GetDirectoryName(self.Location)!,
+ "..");
+ }
+
+ private NativeAssemblyLoadContext()
+ : base("NativeAssemblyLoadContext", isCollectible: false)
+ {
+ }
+
+ ///
+ /// Handler to resolve unmanaged assemblies.
+ ///
+ /// Assembly load context.
+ /// Assembly name.
+ /// The assembly, null if not in our assembly location.
+ internal static IntPtr ResolvingUnmanagedHandler(Assembly context, string name)
+ {
+ if (name.Equals("WindowsPackageManager.dll", StringComparison.OrdinalIgnoreCase))
+ {
+ return NativeALC.LoadUnmanagedDll(name);
+ }
+
+ return IntPtr.Zero;
+ }
+
+ ///
+ protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
+ {
+ string path = Path.Combine(PackageRootPath, unmanagedDllName);
+ if (File.Exists(path))
+ {
+ return this.LoadUnmanagedDllFromPath(path);
+ }
+
+ return IntPtr.Zero;
+ }
+ }
+
internal class Program
{
private const string CommandLineSectionSeparator = "~~~~~~";
@@ -20,13 +71,15 @@ internal class Program
static int Main(string[] args)
{
- ulong memoryHandle = ulong.Parse(args[0]);
+ // Help find WindowsPackageManager.dll
+ AssemblyLoadContext.Default.ResolvingUnmanagedDll += NativeAssemblyLoadContext.ResolvingUnmanagedHandler;
+
+ string staticsCallback = args[1];
try
{
- ulong initEventHandle = ulong.Parse(args[1]);
- ulong completionEventHandle = ulong.Parse(args[2]);
- ulong parentProcessHandle = ulong.Parse(args[3]);
+ string completionEventName = args[2];
+ uint parentProcessId = uint.Parse(args[3]);
PowerShellConfigurationSetProcessorFactory factory = new PowerShellConfigurationSetProcessorFactory();
@@ -100,11 +153,11 @@ static int Main(string[] args)
IObjectReference factoryInterface = MarshalInterface.CreateMarshaler(factory);
- return WindowsPackageManagerConfigurationCompleteOutOfProcessFactoryInitialization(0, factoryInterface.ThisPtr, memoryHandle, initEventHandle, completionEventHandle, parentProcessHandle);
+ return WindowsPackageManagerConfigurationCompleteOutOfProcessFactoryInitialization(0, factoryInterface.ThisPtr, staticsCallback, completionEventName, parentProcessId);
}
catch(Exception ex)
{
- WindowsPackageManagerConfigurationCompleteOutOfProcessFactoryInitialization(ex.HResult, IntPtr.Zero, memoryHandle, 0, 0, 0);
+ WindowsPackageManagerConfigurationCompleteOutOfProcessFactoryInitialization(ex.HResult, IntPtr.Zero, staticsCallback, null, 0);
return ex.HResult;
}
}
@@ -135,7 +188,12 @@ private static string GetExternalModulesPath()
}
[DllImport("WindowsPackageManager.dll")]
- private static extern int WindowsPackageManagerConfigurationCompleteOutOfProcessFactoryInitialization(int result, IntPtr factory, ulong memoryHandle, ulong initEventHandle, ulong completionMutexHandle, ulong parentProcessHandle);
+ private static extern int WindowsPackageManagerConfigurationCompleteOutOfProcessFactoryInitialization(
+ int result,
+ IntPtr factory,
+ [MarshalAs(UnmanagedType.LPWStr)]string staticsCallback,
+ [MarshalAs(UnmanagedType.LPWStr)]string? completionEventName,
+ uint parentProcessId);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr GetCommandLineW();
diff --git a/src/Microsoft.Management.Configuration.UnitTests/Helpers/ConfigurationProcessorTestBase.cs b/src/Microsoft.Management.Configuration.UnitTests/Helpers/ConfigurationProcessorTestBase.cs
index aeb44ca504..c2e9375640 100644
--- a/src/Microsoft.Management.Configuration.UnitTests/Helpers/ConfigurationProcessorTestBase.cs
+++ b/src/Microsoft.Management.Configuration.UnitTests/Helpers/ConfigurationProcessorTestBase.cs
@@ -1,4 +1,4 @@
-// -----------------------------------------------------------------------------
+// -----------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. Licensed under the MIT License.
//
@@ -78,6 +78,29 @@ protected IInputStream CreateStream(string contents)
return result;
}
+ ///
+ /// Creates an string from the given output stream.
+ ///
+ /// The output stream.
+ /// The created string.
+ protected string ReadStream(InMemoryRandomAccessStream stream)
+ {
+ string result = string.Empty;
+ using (DataReader reader = new DataReader(stream.GetInputStreamAt(0)))
+ {
+ reader.UnicodeEncoding = UnicodeEncoding.Utf8;
+ reader.LoadAsync((uint)stream.Size).AsTask().Wait();
+ uint bytesToRead = reader.UnconsumedBufferLength;
+
+ if (bytesToRead > 0)
+ {
+ result = reader.ReadString(bytesToRead);
+ }
+ }
+
+ return result;
+ }
+
///
/// Creates a configuration unit via the configuration statics object.
///
diff --git a/src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationProcessorGroupTests.cs b/src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationProcessorGroupTests.cs
index 1dc857e39f..54343194df 100644
--- a/src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationProcessorGroupTests.cs
+++ b/src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationProcessorGroupTests.cs
@@ -1,4 +1,4 @@
-// -----------------------------------------------------------------------------
+// -----------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. Licensed under the MIT License.
//
diff --git a/src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationSetAuthoringTests.cs b/src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationSetAuthoringTests.cs
index 0a5d6019c6..5d56565aa5 100644
--- a/src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationSetAuthoringTests.cs
+++ b/src/Microsoft.Management.Configuration.UnitTests/Tests/ConfigurationSetAuthoringTests.cs
@@ -10,6 +10,8 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests
using Microsoft.Management.Configuration.UnitTests.Fixtures;
using Microsoft.Management.Configuration.UnitTests.Helpers;
using Microsoft.VisualBasic;
+ using Windows.Foundation.Collections;
+ using Windows.Storage.Streams;
using Xunit;
using Xunit.Abstractions;
@@ -70,7 +72,6 @@ public void ConfigurationUnitAndProperties()
ConfigurationUnitIntent testIntent = ConfigurationUnitIntent.Assert;
ConfigurationUnit testUnit = this.ConfigurationUnit();
-
testUnit.Type = testName;
Assert.Equal(testName, testUnit.Type);
testUnit.Identifier = testIdentifier;
@@ -100,12 +101,33 @@ public void ConfigurationUnitAndProperties()
}
///
- /// This test is to ensure that real tests are added when Serialize is implemented.
+ /// Basic sanity check to verify that nested value sets can be serialized successfully.
///
[Fact]
- public void ConfigurationSetSerializeNotImplemented()
+ public void ConfigurationSetSerializeNestedValueSets()
{
- Assert.Throws(() => this.ConfigurationSet().Serialize(null));
+ ConfigurationSet testSet = this.ConfigurationSet();
+
+ testSet.SchemaVersion = "0.2";
+ ConfigurationUnit testUnit = this.ConfigurationUnit();
+ string testName = "Test Name";
+ string testIdentifier = "Test Identifier";
+ testUnit.Type = testName;
+ testUnit.Identifier = testIdentifier;
+
+ ValueSet innerValueSet = new ValueSet();
+ innerValueSet.Add("innerKey", "innerValue");
+
+ ValueSet outerValueSet = new ValueSet();
+ outerValueSet.Add("outerKey", innerValueSet);
+ testUnit.Metadata = outerValueSet;
+ testSet.Units.Add(testUnit);
+
+ InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream();
+ testSet.Serialize(stream);
+
+ string yamlOutput = this.ReadStream(stream);
+ Assert.NotNull(yamlOutput);
}
}
}
diff --git a/src/Microsoft.Management.Configuration.UnitTests/Tests/OpenConfigurationSetTests.cs b/src/Microsoft.Management.Configuration.UnitTests/Tests/OpenConfigurationSetTests.cs
index 2e31263bf4..db34cb2f05 100644
--- a/src/Microsoft.Management.Configuration.UnitTests/Tests/OpenConfigurationSetTests.cs
+++ b/src/Microsoft.Management.Configuration.UnitTests/Tests/OpenConfigurationSetTests.cs
@@ -1,4 +1,4 @@
-// -----------------------------------------------------------------------------
+// -----------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. Licensed under the MIT License.
//
@@ -10,11 +10,14 @@ namespace Microsoft.Management.Configuration.UnitTests.Tests
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
+ using Microsoft.Management.Configuration.Processor.Extensions;
using Microsoft.Management.Configuration.UnitTests.Fixtures;
using Microsoft.Management.Configuration.UnitTests.Helpers;
using Microsoft.VisualBasic;
using Newtonsoft.Json.Linq;
using Windows.Foundation.Collections;
+ using Windows.Storage.Streams;
+ using WinRT;
using Xunit;
using Xunit.Abstractions;
@@ -467,6 +470,79 @@ public void EmptyResourceWithModule()
Assert.NotEqual(0U, result.Column);
}
+ ///
+ /// Verifies that the configuration set (0.2) can be serialized and reopened correctly.
+ ///
+ [Fact]
+ public void TestSet_Serialize_0_2()
+ {
+ ConfigurationProcessor processor = this.CreateConfigurationProcessorWithDiagnostics();
+
+ OpenConfigurationSetResult openResult = processor.OpenConfigurationSet(this.CreateStream(@"
+properties:
+ configurationVersion: 0.2
+ assertions:
+ - resource: FakeModule
+ id: TestId
+ directives:
+ description: FakeDescription
+ allowPrerelease: true
+ SecurityContext: elevated
+ settings:
+ TestString: Hello
+ TestBool: false
+ TestInt: 1234
+ resources:
+ - resource: FakeModule2
+ id: TestId2
+ dependsOn:
+ - TestId
+ - dependency2
+ - dependency3
+ directives:
+ description: FakeDescription2
+ SecurityContext: elevated
+ settings:
+ TestString: Bye
+ TestBool: true
+ TestInt: 4321
+ Mapping:
+ Key: TestValue
+"));
+
+ // Serialize set.
+ ConfigurationSet configurationSet = openResult.Set;
+ InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream();
+ configurationSet.Serialize(stream);
+
+ string yamlOutput = this.ReadStream(stream);
+
+ // Reopen configuration set from serialized string and verify values.
+ OpenConfigurationSetResult serializedSetResult = processor.OpenConfigurationSet(this.CreateStream(yamlOutput));
+ Assert.Null(serializedSetResult.ResultCode);
+ ConfigurationSet set = serializedSetResult.Set;
+ Assert.NotNull(set);
+
+ Assert.Equal("0.2", set.SchemaVersion);
+ Assert.Equal(2, set.Units.Count);
+
+ Assert.Equal("FakeModule", set.Units[0].Type);
+ Assert.Equal(ConfigurationUnitIntent.Assert, set.Units[0].Intent);
+ Assert.Equal("TestId", set.Units[0].Identifier);
+ this.VerifyValueSet(set.Units[0].Metadata, new ("description", "FakeDescription"), new ("allowPrerelease", true), new ("SecurityContext", "elevated"));
+ this.VerifyValueSet(set.Units[0].Settings, new ("TestString", "Hello"), new ("TestBool", false), new ("TestInt", 1234));
+
+ Assert.Equal("FakeModule2", set.Units[1].Type);
+ Assert.Equal(ConfigurationUnitIntent.Apply, set.Units[1].Intent);
+ Assert.Equal("TestId2", set.Units[1].Identifier);
+ this.VerifyStringArray(set.Units[1].Dependencies, "TestId", "dependency2", "dependency3");
+ this.VerifyValueSet(set.Units[1].Metadata, new ("description", "FakeDescription2"), new ("SecurityContext", "elevated"));
+
+ ValueSet mapping = new ValueSet();
+ mapping.Add("Key", "TestValue");
+ this.VerifyValueSet(set.Units[1].Settings, new ("TestString", "Bye"), new ("TestBool", true), new ("TestInt", 4321), new ("Mapping", mapping));
+ }
+
///
/// Test for using version 0.3 schema.
///
@@ -692,6 +768,12 @@ private void VerifyValueSet(ValueSet values, params KeyValuePair
case string s:
Assert.Equal(s, (string)value);
break;
+ case bool b:
+ Assert.Equal(b, (bool)value);
+ break;
+ case ValueSet v:
+ Assert.True(v.ContentEquals(value.As()));
+ break;
default:
Assert.Fail($"Add expected type `{expectation.Value.GetType().Name}` to switch statement.");
break;
diff --git a/src/Microsoft.Management.Configuration/ConfigurationSet.cpp b/src/Microsoft.Management.Configuration/ConfigurationSet.cpp
index 8f7aa3f233..c120066fb2 100644
--- a/src/Microsoft.Management.Configuration/ConfigurationSet.cpp
+++ b/src/Microsoft.Management.Configuration/ConfigurationSet.cpp
@@ -4,6 +4,7 @@
#include "ConfigurationSet.h"
#include "ConfigurationSet.g.cpp"
#include "ConfigurationSetParser.h"
+#include "ConfigurationSetSerializer.h"
namespace winrt::Microsoft::Management::Configuration::implementation
{
@@ -125,8 +126,14 @@ namespace winrt::Microsoft::Management::Configuration::implementation
void ConfigurationSet::Serialize(const Windows::Storage::Streams::IOutputStream& stream)
{
- UNREFERENCED_PARAMETER(stream);
- THROW_HR(E_NOTIMPL);
+ std::unique_ptr serializer = ConfigurationSetSerializer::CreateSerializer(m_schemaVersion);
+ hstring result = serializer->Serialize(this);
+
+ Windows::Storage::Streams::DataWriter dataWriter{ stream };
+ dataWriter.UnicodeEncoding(Windows::Storage::Streams::UnicodeEncoding::Utf8);
+ dataWriter.WriteString(result);
+ dataWriter.StoreAsync().get();
+ dataWriter.DetachStream();
}
void ConfigurationSet::Remove()
diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetParser.cpp b/src/Microsoft.Management.Configuration/ConfigurationSetParser.cpp
index c172902a38..2397ff515d 100644
--- a/src/Microsoft.Management.Configuration/ConfigurationSetParser.cpp
+++ b/src/Microsoft.Management.Configuration/ConfigurationSetParser.cpp
@@ -10,6 +10,7 @@
#include
#include
+#include "ConfigurationSetUtilities.h"
#include "ConfigurationSetParserError.h"
#include "ConfigurationSetParser_0_1.h"
#include "ConfigurationSetParser_0_2.h"
@@ -178,7 +179,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation
std::string schemaUriString;
std::string schemaVersionString;
- Node& schemaNode = document[GetFieldName(FieldName::Schema)];
+ Node& schemaNode = document[GetConfigurationFieldName(ConfigurationField::Schema)];
if (schemaNode.IsScalar())
{
schemaUriString = schemaNode.as();
@@ -199,7 +200,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation
if (schemaNode.IsScalar())
{
AICLI_LOG(Config, Error, << "Unknown configuration schema: " << schemaUriString);
- return std::make_unique(WINGET_CONFIG_ERROR_UNKNOWN_CONFIGURATION_FILE_VERSION, GetFieldName(FieldName::Schema), schemaUriString);
+ return std::make_unique(WINGET_CONFIG_ERROR_UNKNOWN_CONFIGURATION_FILE_VERSION, GetConfigurationFieldName(ConfigurationField::Schema), schemaUriString);
}
else
{
@@ -227,7 +228,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation
}
AICLI_LOG(Config, Error, << "Unknown configuration version: " << schemaVersion.ToString());
- return std::make_unique(WINGET_CONFIG_ERROR_UNKNOWN_CONFIGURATION_FILE_VERSION, GetFieldName(FieldName::ConfigurationVersion), schemaVersion.ToString());
+ return std::make_unique(WINGET_CONFIG_ERROR_UNKNOWN_CONFIGURATION_FILE_VERSION, GetConfigurationFieldName(ConfigurationField::ConfigurationVersion), schemaVersion.ToString());
}
bool ConfigurationSetParser::IsRecognizedSchemaVersion(hstring value) try
@@ -325,76 +326,36 @@ namespace winrt::Microsoft::Management::Configuration::implementation
SetError(result, field, value, static_cast(mark.line), static_cast(mark.column));
}
- std::string_view ConfigurationSetParser::GetFieldName(FieldName fieldName)
+ const Node& ConfigurationSetParser::GetAndEnsureField(const Node& parent, ConfigurationField field, bool required, std::optional type)
{
- switch (fieldName)
- {
- case FieldName::ConfigurationVersion: return "configurationVersion"sv;
- case FieldName::Properties: return "properties"sv;
- case FieldName::Resource: return "resource"sv;
- case FieldName::Directives: return "directives"sv;
- case FieldName::Settings: return "settings"sv;
- case FieldName::Assertions: return "assertions"sv;
- case FieldName::Id: return "id"sv;
- case FieldName::DependsOn: return "dependsOn"sv;
-
- case FieldName::Resources: return "resources"sv;
- case FieldName::ModuleDirective: return "module"sv;
-
- case FieldName::Schema: return "$schema"sv;
- case FieldName::Metadata: return "metadata"sv;
- case FieldName::Parameters: return "parameters"sv;
- case FieldName::Variables: return "variables"sv;
- case FieldName::Type: return "type"sv;
- case FieldName::Description: return "description"sv;
- case FieldName::Name: return "name"sv;
- case FieldName::IsGroupMetadata: return "isGroup"sv;
- case FieldName::DefaultValue: return "defaultValue"sv;
- case FieldName::AllowedValues: return "allowedValues"sv;
- case FieldName::MinimumLength: return "minLength"sv;
- case FieldName::MaximumLength: return "maxLength"sv;
- case FieldName::MinimumValue: return "minValue"sv;
- case FieldName::MaximumValue: return "maxValue"sv;
- }
-
- THROW_HR(E_UNEXPECTED);
- }
-
- hstring ConfigurationSetParser::GetFieldNameHString(FieldName fieldName)
- {
- return hstring{ ConvertToUTF16(GetFieldName(fieldName)) };
- }
-
- const Node& ConfigurationSetParser::GetAndEnsureField(const Node& parent, FieldName field, bool required, std::optional type)
- {
- const Node& fieldNode = parent[GetFieldName(field)];
+ const Node& fieldNode = parent[GetConfigurationFieldName(field)];
if (fieldNode)
{
if (type && fieldNode.GetType() != type.value())
{
- SetError(WINGET_CONFIG_ERROR_INVALID_FIELD_TYPE, GetFieldName(field), fieldNode.Mark());
+ SetError(WINGET_CONFIG_ERROR_INVALID_FIELD_TYPE, GetConfigurationFieldName(field), fieldNode.Mark());
}
}
else if (required)
{
- SetError(WINGET_CONFIG_ERROR_MISSING_FIELD, GetFieldName(field));
+ SetError(WINGET_CONFIG_ERROR_MISSING_FIELD, GetConfigurationFieldName(field));
}
return fieldNode;
}
- void ConfigurationSetParser::EnsureFieldAbsent(const Node& parent, FieldName field)
+ void ConfigurationSetParser::EnsureFieldAbsent(const Node& parent, ConfigurationField field)
{
- const Node& fieldNode = parent[GetFieldName(field)];
+ const Node& fieldNode = parent[GetConfigurationFieldName(field)];
if (fieldNode)
{
- SetError(WINGET_CONFIG_ERROR_INVALID_FIELD_VALUE, GetFieldName(field), fieldNode.Mark(), fieldNode.as());
+ SetError(WINGET_CONFIG_ERROR_INVALID_FIELD_VALUE, GetConfigurationFieldName(field), fieldNode.Mark(), fieldNode.as());
}
}
- void ConfigurationSetParser::ParseValueSet(const Node& node, FieldName field, bool required, const Windows::Foundation::Collections::ValueSet& valueSet)
+ void ConfigurationSetParser::ParseValueSet(const Node& node, ConfigurationField field, bool required, const Windows::Foundation::Collections::ValueSet& valueSet)
{
const Node& mapNode = CHECK_ERROR(GetAndEnsureField(node, field, required, Node::Type::Mapping));
@@ -404,7 +365,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation
}
}
- void ConfigurationSetParser::ParseMapping(const AppInstaller::YAML::Node& node, FieldName field, bool required, AppInstaller::YAML::Node::Type elementType, std::function operation)
+ void ConfigurationSetParser::ParseMapping(const AppInstaller::YAML::Node& node, ConfigurationField field, bool required, AppInstaller::YAML::Node::Type elementType, std::function operation)
{
const Node& mapNode = CHECK_ERROR(GetAndEnsureField(node, field, required, Node::Type::Mapping));
if (!mapNode)
@@ -413,7 +374,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation
}
std::ostringstream strstr;
- strstr << GetFieldName(field);
+ strstr << GetConfigurationFieldName(field);
size_t index = 0;
for (const auto& mapItem : mapNode.Mapping())
@@ -436,7 +397,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation
}
}
- void ConfigurationSetParser::ParseSequence(const AppInstaller::YAML::Node& node, FieldName field, bool required, std::optional elementType, std::function operation)
+ void ConfigurationSetParser::ParseSequence(const AppInstaller::YAML::Node& node, ConfigurationField field, bool required, std::optional elementType, std::function operation)
{
const Node& sequenceNode = CHECK_ERROR(GetAndEnsureField(node, field, required, Node::Type::Sequence));
if (!sequenceNode)
@@ -445,7 +406,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation
}
std::ostringstream strstr;
- strstr << GetFieldName(field);
+ strstr << GetConfigurationFieldName(field);
size_t index = 0;
for (const Node& item : sequenceNode.Sequence())
@@ -463,49 +424,49 @@ namespace winrt::Microsoft::Management::Configuration::implementation
std::unique_ptr ConfigurationSetParser::GetSchemaVersionFromOldFormat(AppInstaller::YAML::Node& document, std::string& schemaVersionString)
{
- Node& propertiesNode = document[GetFieldName(FieldName::Properties)];
+ Node& propertiesNode = document[GetConfigurationFieldName(ConfigurationField::Properties)];
if (!propertiesNode)
{
AICLI_LOG(Config, Error, << "No properties");
// Even though this is for the "older" format, if there is no properties entry then give an error for the newer format since this is probably neither.
- return std::make_unique(WINGET_CONFIG_ERROR_MISSING_FIELD, GetFieldName(FieldName::Schema));
+ return std::make_unique(WINGET_CONFIG_ERROR_MISSING_FIELD, GetConfigurationFieldName(ConfigurationField::Schema));
}
else if (!propertiesNode.IsMap())
{
AICLI_LOG(Config, Error, << "Invalid properties type");
- return std::make_unique(WINGET_CONFIG_ERROR_INVALID_FIELD_TYPE, GetFieldName(FieldName::Properties), propertiesNode.Mark());
+ return std::make_unique(WINGET_CONFIG_ERROR_INVALID_FIELD_TYPE, GetConfigurationFieldName(ConfigurationField::Properties), propertiesNode.Mark());
}
- Node& versionNode = propertiesNode[GetFieldName(FieldName::ConfigurationVersion)];
+ Node& versionNode = propertiesNode[GetConfigurationFieldName(ConfigurationField::ConfigurationVersion)];
if (!versionNode)
{
AICLI_LOG(Config, Error, << "No configuration version");
- return std::make_unique(WINGET_CONFIG_ERROR_MISSING_FIELD, GetFieldName(FieldName::ConfigurationVersion));
+ return std::make_unique(WINGET_CONFIG_ERROR_MISSING_FIELD, GetConfigurationFieldName(ConfigurationField::ConfigurationVersion));
}
else if (!versionNode.IsScalar())
{
AICLI_LOG(Config, Error, << "Invalid configuration version type");
- return std::make_unique(WINGET_CONFIG_ERROR_INVALID_FIELD_TYPE, GetFieldName(FieldName::ConfigurationVersion), versionNode.Mark());
+ return std::make_unique(WINGET_CONFIG_ERROR_INVALID_FIELD_TYPE, GetConfigurationFieldName(ConfigurationField::ConfigurationVersion), versionNode.Mark());
}
schemaVersionString = versionNode.as();
return {};
}
- void ConfigurationSetParser::GetStringValueForUnit(const Node& node, FieldName field, bool required, ConfigurationUnit* unit, void(ConfigurationUnit::* propertyFunction)(const hstring& value))
+ void ConfigurationSetParser::GetStringValueForUnit(const Node& node, ConfigurationField field, bool required, ConfigurationUnit* unit, void(ConfigurationUnit::* propertyFunction)(const hstring& value))
{
const Node& valueNode = CHECK_ERROR(GetAndEnsureField(node, field, required, Node::Type::Scalar));
if (valueNode)
{
hstring value{ valueNode.as() };
- FIELD_MISSING_ERROR_IF(value.empty() && required, GetFieldName(field));
+ FIELD_MISSING_ERROR_IF(value.empty() && required, GetConfigurationFieldName(field));
(unit->*propertyFunction)(std::move(value));
}
}
- void ConfigurationSetParser::GetStringArrayForUnit(const Node& node, FieldName field, bool required, ConfigurationUnit* unit, void(ConfigurationUnit::* propertyFunction)(std::vector&& value))
+ void ConfigurationSetParser::GetStringArrayForUnit(const Node& node, ConfigurationField field, bool required, ConfigurationUnit* unit, void(ConfigurationUnit::* propertyFunction)(std::vector&& value))
{
std::vector arrayValue;
CHECK_ERROR(ParseSequence(node, field, required, Node::Type::Scalar, [&](const AppInstaller::YAML::Node& item)
@@ -519,25 +480,25 @@ namespace winrt::Microsoft::Management::Configuration::implementation
}
}
- void ConfigurationSetParser::ValidateType(ConfigurationUnit* unit, const Node& unitNode, FieldName typeField, bool moveModuleNameToMetadata, bool moduleNameRequiredInType)
+ void ConfigurationSetParser::ValidateType(ConfigurationUnit* unit, const Node& unitNode, ConfigurationField typeField, bool moveModuleNameToMetadata, bool moduleNameRequiredInType)
{
QualifiedResourceName qualifiedName{ unit->Type() };
const Node& typeNode = CHECK_ERROR(GetAndEnsureField(unitNode, typeField, true, Node::Type::Scalar));
- FIELD_VALUE_ERROR_IF(qualifiedName.Resource.empty(), GetFieldName(typeField), ConvertToUTF8(unit->Type()), typeNode.Mark());
+ FIELD_VALUE_ERROR_IF(qualifiedName.Resource.empty(), GetConfigurationFieldName(typeField), ConvertToUTF8(unit->Type()), typeNode.Mark());
if (!qualifiedName.Module.empty())
{
// If the module is provided in both the resource name and the directives, ensure that it matches
- hstring moduleDirectiveFieldName = GetFieldNameHString(FieldName::ModuleDirective);
+ hstring moduleDirectiveFieldName = GetConfigurationFieldNameHString(ConfigurationField::ModuleDirective);
auto moduleDirective = unit->Metadata().TryLookup(moduleDirectiveFieldName);
if (moduleDirective)
{
auto moduleProperty = moduleDirective.try_as();
- FIELD_TYPE_ERROR_IF(!moduleProperty, GetFieldName(FieldName::ModuleDirective), unitNode.Mark());
- FIELD_TYPE_ERROR_IF(moduleProperty.Type() != Windows::Foundation::PropertyType::String, GetFieldName(FieldName::ModuleDirective), unitNode.Mark());
+ FIELD_TYPE_ERROR_IF(!moduleProperty, GetConfigurationFieldName(ConfigurationField::ModuleDirective), unitNode.Mark());
+ FIELD_TYPE_ERROR_IF(moduleProperty.Type() != Windows::Foundation::PropertyType::String, GetConfigurationFieldName(ConfigurationField::ModuleDirective), unitNode.Mark());
hstring moduleValue = moduleProperty.GetString();
- FIELD_VALUE_ERROR_IF(qualifiedName.Module != moduleValue, GetFieldName(FieldName::ModuleDirective), ConvertToUTF8(moduleValue), unitNode.Mark());
+ FIELD_VALUE_ERROR_IF(qualifiedName.Module != moduleValue, GetConfigurationFieldName(ConfigurationField::ModuleDirective), ConvertToUTF8(moduleValue), unitNode.Mark());
}
else if (moveModuleNameToMetadata)
{
@@ -552,22 +513,22 @@ namespace winrt::Microsoft::Management::Configuration::implementation
}
else if (moduleNameRequiredInType)
{
- FIELD_VALUE_ERROR(GetFieldName(typeField), ConvertToUTF8(unit->Type()), typeNode.Mark());
+ FIELD_VALUE_ERROR(GetConfigurationFieldName(typeField), ConvertToUTF8(unit->Type()), typeNode.Mark());
}
}
- void ConfigurationSetParser::ParseObject(const Node& node, FieldName fieldForErrors, Windows::Foundation::PropertyType type, Windows::Foundation::IInspectable& result)
+ void ConfigurationSetParser::ParseObject(const Node& node, ConfigurationField fieldForErrors, Windows::Foundation::PropertyType type, Windows::Foundation::IInspectable& result)
{
try
{
Windows::Foundation::IInspectable object = GetIInspectableFromNode(node);
- FIELD_VALUE_ERROR_IF(!IsValidObjectType(object, type), GetFieldName(fieldForErrors), node.as(), node.Mark());
+ FIELD_VALUE_ERROR_IF(!IsValidObjectType(object, type), GetConfigurationFieldName(fieldForErrors), node.as(), node.Mark());
result = std::move(object);
}
catch (...)
{
LOG_CAUGHT_EXCEPTION();
- FIELD_VALUE_ERROR(GetFieldName(fieldForErrors), node.as(), node.Mark());
+ FIELD_VALUE_ERROR(GetConfigurationFieldName(fieldForErrors), node.as(), node.Mark());
}
}
}
diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetParser.h b/src/Microsoft.Management.Configuration/ConfigurationSetParser.h
index d12c286f06..c0fa73cee8 100644
--- a/src/Microsoft.Management.Configuration/ConfigurationSetParser.h
+++ b/src/Microsoft.Management.Configuration/ConfigurationSetParser.h
@@ -3,14 +3,13 @@
#pragma once
#include
#include
+#include
#include
#include
#include
#include
#include
-using namespace std::string_view_literals;
-
namespace winrt::Microsoft::Management::Configuration::implementation
{
// Interface for parsing a configuration set stream.
@@ -79,44 +78,6 @@ namespace winrt::Microsoft::Management::Configuration::implementation
void SetError(hresult result, std::string_view field = {}, std::string_view value = {}, uint32_t line = 0, uint32_t column = 0);
void SetError(hresult result, std::string_view field, const AppInstaller::YAML::Mark& mark, std::string_view value = {});
- // The various field names that are used in parsing.
- enum class FieldName
- {
- // v0.1 and v0.2
- ConfigurationVersion,
- Properties,
- Resource,
- Directives,
- Settings,
- Assertions,
- Id,
- DependsOn,
-
- // Universal
- Resources,
- ModuleDirective,
-
- // v0.3
- Schema,
- Metadata,
- Parameters,
- Variables,
- Type,
- Description,
- Name,
- IsGroupMetadata,
- DefaultValue,
- AllowedValues,
- MinimumLength,
- MaximumLength,
- MinimumValue,
- MaximumValue,
- };
-
- // Gets the value of the field name.
- static std::string_view GetFieldName(FieldName fieldName);
- static hstring GetFieldNameHString(FieldName fieldName);
-
ConfigurationSetPtr m_configurationSet;
hresult m_result;
hstring m_field;
@@ -125,31 +86,31 @@ namespace winrt::Microsoft::Management::Configuration::implementation
uint32_t m_column = 0;
// Gets the given `field` from the `parent` node, checking against the requirement and type.
- const AppInstaller::YAML::Node& GetAndEnsureField(const AppInstaller::YAML::Node& parent, FieldName field, bool required, std::optional type);
+ const AppInstaller::YAML::Node& GetAndEnsureField(const AppInstaller::YAML::Node& parent, ConfigurationField field, bool required, std::optional type);
// Errors if the given `field` is present.
- void EnsureFieldAbsent(const AppInstaller::YAML::Node& parent, FieldName field);
+ void EnsureFieldAbsent(const AppInstaller::YAML::Node& parent, ConfigurationField field);
// Parse the ValueSet named `field` from the given `node`.
- void ParseValueSet(const AppInstaller::YAML::Node& node, FieldName field, bool required, const Windows::Foundation::Collections::ValueSet& valueSet);
+ void ParseValueSet(const AppInstaller::YAML::Node& node, ConfigurationField field, bool required, const Windows::Foundation::Collections::ValueSet& valueSet);
// Parse the mapping named `field` from the given `node`.
- void ParseMapping(const AppInstaller::YAML::Node& node, FieldName field, bool required, AppInstaller::YAML::Node::Type elementType, std::function operation);
+ void ParseMapping(const AppInstaller::YAML::Node& node, ConfigurationField field, bool required, AppInstaller::YAML::Node::Type elementType, std::function operation);
// Parse the sequence named `field` from the given `node`.
- void ParseSequence(const AppInstaller::YAML::Node& node, FieldName field, bool required, std::optional elementType, std::function operation);
+ void ParseSequence(const AppInstaller::YAML::Node& node, ConfigurationField field, bool required, std::optional elementType, std::function operation);
// Gets the string value in `field` from the given `node`, setting this value on `unit` using the `propertyFunction`.
- void GetStringValueForUnit(const AppInstaller::YAML::Node& node, FieldName field, bool required, ConfigurationUnit* unit, void(ConfigurationUnit::* propertyFunction)(const hstring& value));
+ void GetStringValueForUnit(const AppInstaller::YAML::Node& node, ConfigurationField field, bool required, ConfigurationUnit* unit, void(ConfigurationUnit::* propertyFunction)(const hstring& value));
// Gets the string array in `field` from the given `node`, setting this value on `unit` using the `propertyFunction`.
- void GetStringArrayForUnit(const AppInstaller::YAML::Node& node, FieldName field, bool required, ConfigurationUnit* unit, void(ConfigurationUnit::* propertyFunction)(std::vector&& value));
+ void GetStringArrayForUnit(const AppInstaller::YAML::Node& node, ConfigurationField field, bool required, ConfigurationUnit* unit, void(ConfigurationUnit::* propertyFunction)(std::vector&& value));
// Validates the unit's Type property for correctness and consistency with the metadata. Should be called after parsing the Metadata value.
- void ValidateType(ConfigurationUnit* unit, const AppInstaller::YAML::Node& unitNode, FieldName typeField, bool moveModuleNameToMetadata, bool moduleNameRequiredInType);
+ void ValidateType(ConfigurationUnit* unit, const AppInstaller::YAML::Node& unitNode, ConfigurationField typeField, bool moveModuleNameToMetadata, bool moduleNameRequiredInType);
// Parses an object from the given node, attempting to treat it as the requested type if possible.
- void ParseObject(const AppInstaller::YAML::Node& node, FieldName fieldForErrors, Windows::Foundation::PropertyType type, Windows::Foundation::IInspectable& result);
+ void ParseObject(const AppInstaller::YAML::Node& node, ConfigurationField fieldForErrors, Windows::Foundation::PropertyType type, Windows::Foundation::IInspectable& result);
private:
// Support older schema parsing.
diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_1.cpp b/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_1.cpp
index cffbd52ae8..e679677f9e 100644
--- a/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_1.cpp
+++ b/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_1.cpp
@@ -16,10 +16,10 @@ namespace winrt::Microsoft::Management::Configuration::implementation
void ConfigurationSetParser_0_1::Parse()
{
std::vector units;
- const Node& properties = m_document[GetFieldName(FieldName::Properties)];
- ParseConfigurationUnitsFromField(properties, FieldName::Assertions, ConfigurationUnitIntent::Assert, units);
- ParseConfigurationUnitsFromField(properties, FieldName::Parameters, ConfigurationUnitIntent::Inform, units);
- ParseConfigurationUnitsFromField(properties, FieldName::Resources, ConfigurationUnitIntent::Apply, units);
+ const Node& properties = m_document[GetConfigurationFieldName(ConfigurationField::Properties)];
+ ParseConfigurationUnitsFromField(properties, ConfigurationField::Assertions, ConfigurationUnitIntent::Assert, units);
+ ParseConfigurationUnitsFromField(properties, ConfigurationField::Parameters, ConfigurationUnitIntent::Inform, units);
+ ParseConfigurationUnitsFromField(properties, ConfigurationField::Resources, ConfigurationUnitIntent::Apply, units);
m_configurationSet = make_self>();
m_configurationSet->Units(std::move(units));
@@ -32,7 +32,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation
return s_schemaVersion;
}
- void ConfigurationSetParser_0_1::ParseConfigurationUnitsFromField(const Node& document, FieldName field, ConfigurationUnitIntent intent, std::vector& result)
+ void ConfigurationSetParser_0_1::ParseConfigurationUnitsFromField(const Node& document, ConfigurationField field, ConfigurationUnitIntent intent, std::vector& result)
{
ParseSequence(document, field, false, Node::Type::Mapping, [&](const Node& item)
{
@@ -44,11 +44,11 @@ namespace winrt::Microsoft::Management::Configuration::implementation
void ConfigurationSetParser_0_1::ParseConfigurationUnit(ConfigurationUnit* unit, const Node& unitNode, ConfigurationUnitIntent intent)
{
- CHECK_ERROR(GetStringValueForUnit(unitNode, FieldName::Resource, true, unit, &ConfigurationUnit::Type));
- CHECK_ERROR(GetStringValueForUnit(unitNode, FieldName::Id, false, unit, &ConfigurationUnit::Identifier));
+ CHECK_ERROR(GetStringValueForUnit(unitNode, ConfigurationField::Resource, true, unit, &ConfigurationUnit::Type));
+ CHECK_ERROR(GetStringValueForUnit(unitNode, ConfigurationField::Id, false, unit, &ConfigurationUnit::Identifier));
unit->Intent(intent);
- CHECK_ERROR(GetStringArrayForUnit(unitNode, FieldName::DependsOn, false, unit, &ConfigurationUnit::Dependencies));
- CHECK_ERROR(ParseValueSet(unitNode, FieldName::Directives, false, unit->Metadata()));
- CHECK_ERROR(ParseValueSet(unitNode, FieldName::Settings, false, unit->Settings()));
+ CHECK_ERROR(GetStringArrayForUnit(unitNode, ConfigurationField::DependsOn, false, unit, &ConfigurationUnit::Dependencies));
+ CHECK_ERROR(ParseValueSet(unitNode, ConfigurationField::Directives, false, unit->Metadata()));
+ CHECK_ERROR(ParseValueSet(unitNode, ConfigurationField::Settings, false, unit->Settings()));
}
}
diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_1.h b/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_1.h
index 10383612f7..0762fef505 100644
--- a/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_1.h
+++ b/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_1.h
@@ -25,7 +25,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation
hstring GetSchemaVersion() override;
protected:
- void ParseConfigurationUnitsFromField(const AppInstaller::YAML::Node& document, FieldName field, ConfigurationUnitIntent intent, std::vector& result);
+ void ParseConfigurationUnitsFromField(const AppInstaller::YAML::Node& document, ConfigurationField field, ConfigurationUnitIntent intent, std::vector& result);
virtual void ParseConfigurationUnit(ConfigurationUnit* unit, const AppInstaller::YAML::Node& unitNode, ConfigurationUnitIntent intent);
AppInstaller::YAML::Node m_document;
diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_2.cpp b/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_2.cpp
index b52fa9e779..8b2234fae7 100644
--- a/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_2.cpp
+++ b/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_2.cpp
@@ -22,6 +22,6 @@ namespace winrt::Microsoft::Management::Configuration::implementation
void ConfigurationSetParser_0_2::ParseConfigurationUnit(ConfigurationUnit* unit, const Node& unitNode, ConfigurationUnitIntent intent)
{
CHECK_ERROR(ConfigurationSetParser_0_1::ParseConfigurationUnit(unit, unitNode, intent));
- ValidateType(unit, unitNode, FieldName::Resource, true, false);
+ ValidateType(unit, unitNode, ConfigurationField::Resource, true, false);
}
}
diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_3.cpp b/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_3.cpp
index 394504c884..7ef8162286 100644
--- a/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_3.cpp
+++ b/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_3.cpp
@@ -18,12 +18,12 @@ namespace winrt::Microsoft::Management::Configuration::implementation
{
auto result = make_self>();
- CHECK_ERROR(ParseValueSet(m_document, FieldName::Metadata, false, result->Metadata()));
+ CHECK_ERROR(ParseValueSet(m_document, ConfigurationField::Metadata, false, result->Metadata()));
CHECK_ERROR(ParseParameters(result));
- CHECK_ERROR(ParseValueSet(m_document, FieldName::Variables, false, result->Variables()));
+ CHECK_ERROR(ParseValueSet(m_document, ConfigurationField::Variables, false, result->Variables()));
std::vector units;
- CHECK_ERROR(ParseConfigurationUnitsFromField(m_document, FieldName::Resources, units));
+ CHECK_ERROR(ParseConfigurationUnitsFromField(m_document, ConfigurationField::Resources, units));
result->Units(std::move(units));
result->SchemaVersion(GetSchemaVersion());
@@ -40,7 +40,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation
{
std::vector parameters;
- ParseMapping(m_document, FieldName::Parameters, false, Node::Type::Mapping, [&](std::string name, const Node& item)
+ ParseMapping(m_document, ConfigurationField::Parameters, false, Node::Type::Mapping, [&](std::string name, const Node& item)
{
auto parameter = make_self>();
CHECK_ERROR(ParseParameter(parameter.get(), item));
@@ -54,18 +54,18 @@ namespace winrt::Microsoft::Management::Configuration::implementation
void ConfigurationSetParser_0_3::ParseParameter(ConfigurationParameter* parameter, const AppInstaller::YAML::Node& node)
{
CHECK_ERROR(ParseParameterType(parameter, node));
- CHECK_ERROR(ParseValueSet(node, FieldName::Metadata, false, parameter->Metadata()));
- CHECK_ERROR(GetStringValueForParameter(node, FieldName::Description, parameter, &ConfigurationParameter::Description));
+ CHECK_ERROR(ParseValueSet(node, ConfigurationField::Metadata, false, parameter->Metadata()));
+ CHECK_ERROR(GetStringValueForParameter(node, ConfigurationField::Description, parameter, &ConfigurationParameter::Description));
Windows::Foundation::PropertyType parameterType = parameter->Type();
- CHECK_ERROR(ParseObjectValueForParameter(node, FieldName::DefaultValue, parameterType, parameter, &ConfigurationParameter::DefaultValue));
+ CHECK_ERROR(ParseObjectValueForParameter(node, ConfigurationField::DefaultValue, parameterType, parameter, &ConfigurationParameter::DefaultValue));
std::vector allowedValues;
- CHECK_ERROR(ParseSequence(node, FieldName::AllowedValues, false, std::nullopt, [&](const Node& item)
+ CHECK_ERROR(ParseSequence(node, ConfigurationField::AllowedValues, false, std::nullopt, [&](const Node& item)
{
Windows::Foundation::IInspectable object;
- CHECK_ERROR(ParseObject(item, FieldName::AllowedValues, parameterType, object));
+ CHECK_ERROR(ParseObject(item, ConfigurationField::AllowedValues, parameterType, object));
allowedValues.emplace_back(std::move(object));
}));
@@ -76,30 +76,30 @@ namespace winrt::Microsoft::Management::Configuration::implementation
if (IsLengthType(parameterType))
{
- CHECK_ERROR(GetUInt32ValueForParameter(node, FieldName::MinimumLength, parameter, &ConfigurationParameter::MinimumLength));
- CHECK_ERROR(GetUInt32ValueForParameter(node, FieldName::MaximumLength, parameter, &ConfigurationParameter::MaximumLength));
+ CHECK_ERROR(GetUInt32ValueForParameter(node, ConfigurationField::MinimumLength, parameter, &ConfigurationParameter::MinimumLength));
+ CHECK_ERROR(GetUInt32ValueForParameter(node, ConfigurationField::MaximumLength, parameter, &ConfigurationParameter::MaximumLength));
}
else
{
- CHECK_ERROR(EnsureFieldAbsent(node, FieldName::MinimumLength));
- CHECK_ERROR(EnsureFieldAbsent(node, FieldName::MaximumLength));
+ CHECK_ERROR(EnsureFieldAbsent(node, ConfigurationField::MinimumLength));
+ CHECK_ERROR(EnsureFieldAbsent(node, ConfigurationField::MaximumLength));
}
if (IsComparableType(parameterType))
{
- CHECK_ERROR(ParseObjectValueForParameter(node, FieldName::MinimumValue, parameterType, parameter, &ConfigurationParameter::MinimumValue));
- CHECK_ERROR(ParseObjectValueForParameter(node, FieldName::MaximumValue, parameterType, parameter, &ConfigurationParameter::MaximumValue));
+ CHECK_ERROR(ParseObjectValueForParameter(node, ConfigurationField::MinimumValue, parameterType, parameter, &ConfigurationParameter::MinimumValue));
+ CHECK_ERROR(ParseObjectValueForParameter(node, ConfigurationField::MaximumValue, parameterType, parameter, &ConfigurationParameter::MaximumValue));
}
else
{
- CHECK_ERROR(EnsureFieldAbsent(node, FieldName::MinimumValue));
- CHECK_ERROR(EnsureFieldAbsent(node, FieldName::MaximumValue));
+ CHECK_ERROR(EnsureFieldAbsent(node, ConfigurationField::MinimumValue));
+ CHECK_ERROR(EnsureFieldAbsent(node, ConfigurationField::MaximumValue));
}
}
void ConfigurationSetParser_0_3::ParseParameterType(ConfigurationParameter* parameter, const AppInstaller::YAML::Node& node)
{
- const Node& typeNode = CHECK_ERROR(GetAndEnsureField(node, FieldName::Type, true, Node::Type::Scalar));
+ const Node& typeNode = CHECK_ERROR(GetAndEnsureField(node, ConfigurationField::Type, true, Node::Type::Scalar));
std::string typeValue = typeNode.as();
if (typeValue == "string")
@@ -134,7 +134,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation
}
else
{
- FIELD_VALUE_ERROR(GetFieldName(FieldName::Type), typeValue, typeNode.Mark());
+ FIELD_VALUE_ERROR(GetConfigurationFieldName(ConfigurationField::Type), typeValue, typeNode.Mark());
}
// TODO: Consider supporting an expanded set of type strings
@@ -142,7 +142,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation
void ConfigurationSetParser_0_3::GetStringValueForParameter(
const Node& node,
- FieldName field,
+ ConfigurationField field,
ConfigurationParameter* parameter,
void(ConfigurationParameter::* propertyFunction)(const hstring& value))
{
@@ -156,7 +156,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation
void ConfigurationSetParser_0_3::GetUInt32ValueForParameter(
const AppInstaller::YAML::Node& node,
- FieldName field,
+ ConfigurationField field,
ConfigurationParameter* parameter,
void(ConfigurationParameter::* propertyFunction)(uint32_t value))
{
@@ -167,7 +167,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation
int64_t value = valueNode.as();
if (value < 0 || value > static_cast(std::numeric_limits::max()))
{
- FIELD_VALUE_ERROR(GetFieldName(field), valueNode.as(), valueNode.Mark());
+ FIELD_VALUE_ERROR(GetConfigurationFieldName(field), valueNode.as(), valueNode.Mark());
}
(parameter->*propertyFunction)(static_cast(value));
}
@@ -175,7 +175,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation
void ConfigurationSetParser_0_3::ParseObjectValueForParameter(
const AppInstaller::YAML::Node& node,
- FieldName field,
+ ConfigurationField field,
Windows::Foundation::PropertyType type,
ConfigurationParameter* parameter,
void(ConfigurationParameter::* propertyFunction)(const Windows::Foundation::IInspectable& value))
@@ -191,7 +191,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation
}
}
- void ConfigurationSetParser_0_3::ParseConfigurationUnitsFromField(const Node& document, FieldName field, std::vector& result)
+ void ConfigurationSetParser_0_3::ParseConfigurationUnitsFromField(const Node& document, ConfigurationField field, std::vector& result)
{
ParseSequence(document, field, false, Node::Type::Mapping, [&](const Node& item)
{
@@ -206,14 +206,14 @@ namespace winrt::Microsoft::Management::Configuration::implementation
// Set unknown intent as the new schema doesn't express it directly
unit->Intent(ConfigurationUnitIntent::Unknown);
- CHECK_ERROR(GetStringValueForUnit(unitNode, FieldName::Name, true, unit, &ConfigurationUnit::Identifier));
- CHECK_ERROR(GetStringValueForUnit(unitNode, FieldName::Type, true, unit, &ConfigurationUnit::Type));
- CHECK_ERROR(ParseValueSet(unitNode, FieldName::Metadata, false, unit->Metadata()));
- CHECK_ERROR(ValidateType(unit, unitNode, FieldName::Type, false, true));
- CHECK_ERROR(GetStringArrayForUnit(unitNode, FieldName::DependsOn, false, unit, &ConfigurationUnit::Dependencies));
+ CHECK_ERROR(GetStringValueForUnit(unitNode, ConfigurationField::Name, true, unit, &ConfigurationUnit::Identifier));
+ CHECK_ERROR(GetStringValueForUnit(unitNode, ConfigurationField::Type, true, unit, &ConfigurationUnit::Type));
+ CHECK_ERROR(ParseValueSet(unitNode, ConfigurationField::Metadata, false, unit->Metadata()));
+ CHECK_ERROR(ValidateType(unit, unitNode, ConfigurationField::Type, false, true));
+ CHECK_ERROR(GetStringArrayForUnit(unitNode, ConfigurationField::DependsOn, false, unit, &ConfigurationUnit::Dependencies));
// Regardless of being a group or not, parse the settings.
- CHECK_ERROR(ParseValueSet(unitNode, FieldName::Properties, false, unit->Settings()));
+ CHECK_ERROR(ParseValueSet(unitNode, ConfigurationField::Properties, false, unit->Settings()));
if (ShouldConvertToGroup(unit))
{
@@ -221,11 +221,11 @@ namespace winrt::Microsoft::Management::Configuration::implementation
// TODO: The PS DSC v3 POR looks like it supports each group defining a new schema to be used for its group items.
// Consider supporting that in the future; but for now just use the same schema for everything.
- const Node& propertiesNode = GetAndEnsureField(unitNode, FieldName::Properties, false, Node::Type::Mapping);
+ const Node& propertiesNode = GetAndEnsureField(unitNode, ConfigurationField::Properties, false, Node::Type::Mapping);
if (propertiesNode)
{
std::vector units;
- CHECK_ERROR(ParseConfigurationUnitsFromField(propertiesNode, FieldName::Resources, units));
+ CHECK_ERROR(ParseConfigurationUnitsFromField(propertiesNode, ConfigurationField::Resources, units));
unit->Units(std::move(units));
}
}
@@ -234,7 +234,7 @@ namespace winrt::Microsoft::Management::Configuration::implementation
bool ConfigurationSetParser_0_3::ShouldConvertToGroup(ConfigurationUnit* unit)
{
// Allow the metadata to inform us that we should treat it as a group, including preventing a known type from being treated as one.
- auto isGroupObject = unit->Metadata().TryLookup(GetFieldNameHString(FieldName::IsGroupMetadata));
+ auto isGroupObject = unit->Metadata().TryLookup(GetConfigurationFieldNameHString(ConfigurationField::IsGroupMetadata));
if (isGroupObject)
{
auto isGroupProperty = isGroupObject.try_as();
diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_3.h b/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_3.h
index 8f29ebc47d..60b8430b05 100644
--- a/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_3.h
+++ b/src/Microsoft.Management.Configuration/ConfigurationSetParser_0_3.h
@@ -32,22 +32,22 @@ namespace winrt::Microsoft::Management::Configuration::implementation
void ParseParameterType(ConfigurationParameter* parameter, const AppInstaller::YAML::Node& node);
void GetStringValueForParameter(
const AppInstaller::YAML::Node& node,
- FieldName field,
+ ConfigurationField field,
ConfigurationParameter* parameter,
void(ConfigurationParameter::* propertyFunction)(const hstring& value));
void GetUInt32ValueForParameter(
const AppInstaller::YAML::Node& node,
- FieldName field,
+ ConfigurationField field,
ConfigurationParameter* parameter,
void(ConfigurationParameter::* propertyFunction)(uint32_t value));
void ParseObjectValueForParameter(
const AppInstaller::YAML::Node& node,
- FieldName field,
+ ConfigurationField field,
Windows::Foundation::PropertyType type,
ConfigurationParameter* parameter,
void(ConfigurationParameter::* propertyFunction)(const Windows::Foundation::IInspectable& value));
- void ParseConfigurationUnitsFromField(const AppInstaller::YAML::Node& document, FieldName field, std::vector& result);
+ void ParseConfigurationUnitsFromField(const AppInstaller::YAML::Node& document, ConfigurationField field, std::vector& result);
virtual void ParseConfigurationUnit(ConfigurationUnit* unit, const AppInstaller::YAML::Node& unitNode);
// Determines if the given unit should be converted to a group.
bool ShouldConvertToGroup(ConfigurationUnit* unit);
diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetSerializer.cpp b/src/Microsoft.Management.Configuration/ConfigurationSetSerializer.cpp
new file mode 100644
index 0000000000..2107fdef17
--- /dev/null
+++ b/src/Microsoft.Management.Configuration/ConfigurationSetSerializer.cpp
@@ -0,0 +1,129 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+#include "pch.h"
+
+#include
+#include
+#include
+
+#include "ConfigurationSetSerializer.h"
+#include "ConfigurationSetSerializer_0_2.h"
+#include "ConfigurationSetUtilities.h"
+
+using namespace AppInstaller::YAML;
+using namespace winrt::Windows::Foundation;
+
+namespace winrt::Microsoft::Management::Configuration::implementation
+{
+ std::unique_ptr ConfigurationSetSerializer::CreateSerializer(hstring version)
+ {
+ // Create the parser based on the version selected
+ AppInstaller::Utility::SemanticVersion schemaVersion(std::move(winrt::to_string(version)));
+
+ // TODO: Consider having the version/uri/type information all together in the future
+ if (schemaVersion.PartAt(0).Integer == 0 && schemaVersion.PartAt(1).Integer == 1)
+ {
+ throw E_NOTIMPL;
+ }
+ else if (schemaVersion.PartAt(0).Integer == 0 && schemaVersion.PartAt(1).Integer == 2)
+ {
+ return std::make_unique();
+ }
+ else if (schemaVersion.PartAt(0).Integer == 0 && schemaVersion.PartAt(1).Integer == 3)
+ {
+ throw E_NOTIMPL;
+ }
+ else
+ {
+ AICLI_LOG(Config, Error, << "Unknown configuration version: " << schemaVersion.ToString());
+ throw E_UNEXPECTED;
+ }
+ }
+
+ void ConfigurationSetSerializer::WriteYamlValueSet(AppInstaller::YAML::Emitter& emitter, const Windows::Foundation::Collections::ValueSet& valueSet)
+ {
+ emitter << BeginMap;
+
+ for (const auto& [key, value] : valueSet)
+ {
+ std::string keyName = winrt::to_string(key);
+ const auto& currentValueSet = value.try_as();
+
+ if (currentValueSet)
+ {
+ emitter << AppInstaller::YAML::Key << keyName;
+ WriteYamlValueSet(emitter, currentValueSet);
+ }
+ else
+ {
+ IPropertyValue property = value.as();
+ auto type = property.Type();
+
+ if (type == PropertyType::Boolean)
+ {
+ emitter << AppInstaller::YAML::Key << keyName << AppInstaller::YAML::Value << property.GetBoolean();
+ }
+ else if (type == PropertyType::String)
+ {
+ emitter << AppInstaller::YAML::Key << keyName << AppInstaller::YAML::Value << AppInstaller::Utility::ConvertToUTF8(property.GetString());
+ }
+ else if (type == PropertyType::Int64)
+ {
+ emitter << AppInstaller::YAML::Key << keyName << AppInstaller::YAML::Value << property.GetInt64();
+ }
+ else
+ {
+ THROW_HR(E_NOTIMPL);;
+ }
+ }
+ }
+
+ emitter << EndMap;
+ }
+
+ void ConfigurationSetSerializer::WriteYamlConfigurationUnits(AppInstaller::YAML::Emitter& emitter, const std::vector& units)
+ {
+ emitter << BeginSeq;
+
+ for (const auto& unit : units)
+ {
+ // Resource
+ emitter << BeginMap;
+ emitter << Key << GetConfigurationFieldName(ConfigurationField::Resource) << Value << AppInstaller::Utility::ConvertToUTF8(unit.Type());
+
+ // Id
+ if (!unit.Identifier().empty())
+ {
+ emitter << Key << GetConfigurationFieldName(ConfigurationField::Id) << Value << AppInstaller::Utility::ConvertToUTF8(unit.Identifier());
+ }
+
+ // Dependencies
+ if (unit.Dependencies().Size() > 0)
+ {
+ emitter << Key << GetConfigurationFieldName(ConfigurationField::DependsOn);
+ emitter << BeginSeq;
+
+ for (const auto& dependency : unit.Dependencies())
+ {
+ emitter << AppInstaller::Utility::ConvertToUTF8(dependency);
+ }
+
+ emitter << EndSeq;
+ }
+
+ // Directives
+ const auto& metadata = unit.Metadata();
+ emitter << Key << GetConfigurationFieldName(ConfigurationField::Directives);
+ WriteYamlValueSet(emitter, metadata);
+
+ // Settings
+ const auto& settings = unit.Settings();
+ emitter << Key << GetConfigurationFieldName(ConfigurationField::Settings);
+ WriteYamlValueSet(emitter, settings);
+
+ emitter << EndMap;
+ }
+
+ emitter << EndSeq;
+ }
+}
diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetSerializer.h b/src/Microsoft.Management.Configuration/ConfigurationSetSerializer.h
new file mode 100644
index 0000000000..bf522172c4
--- /dev/null
+++ b/src/Microsoft.Management.Configuration/ConfigurationSetSerializer.h
@@ -0,0 +1,32 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+#pragma once
+#include "ConfigurationSetSerializer.h"
+#include "ConfigurationSet.h"
+
+#include
+
+namespace winrt::Microsoft::Management::Configuration::implementation
+{
+ struct ConfigurationSetSerializer
+ {
+ static std::unique_ptr CreateSerializer(hstring version);
+
+ virtual ~ConfigurationSetSerializer() noexcept = default;
+
+ ConfigurationSetSerializer(const ConfigurationSetSerializer&) = delete;
+ ConfigurationSetSerializer& operator=(const ConfigurationSetSerializer&) = delete;
+ ConfigurationSetSerializer(ConfigurationSetSerializer&&) = default;
+ ConfigurationSetSerializer& operator=(ConfigurationSetSerializer&&) = default;
+
+ // Serializes a configuration set to the original yaml string.
+ virtual hstring Serialize(ConfigurationSet*) = 0;
+
+ protected:
+ ConfigurationSetSerializer() = default;
+
+ void WriteYamlValueSet(AppInstaller::YAML::Emitter& emitter, const Windows::Foundation::Collections::ValueSet& valueSet);
+
+ void WriteYamlConfigurationUnits(AppInstaller::YAML::Emitter& emitter, const std::vector& units);
+ };
+}
diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetSerializer_0_2.cpp b/src/Microsoft.Management.Configuration/ConfigurationSetSerializer_0_2.cpp
new file mode 100644
index 0000000000..5631e1d2e3
--- /dev/null
+++ b/src/Microsoft.Management.Configuration/ConfigurationSetSerializer_0_2.cpp
@@ -0,0 +1,55 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+#include "pch.h"
+#include "ConfigurationSetSerializer_0_2.h"
+#include "ConfigurationSetUtilities.h"
+
+#include
+
+namespace winrt::Microsoft::Management::Configuration::implementation
+{
+ using namespace AppInstaller::YAML;
+
+ hstring ConfigurationSetSerializer_0_2::Serialize(ConfigurationSet* configurationSet)
+ {
+ std::vector assertions;
+ std::vector resources;
+
+ for (auto unit : configurationSet->Units())
+ {
+ if (unit.Intent() == ConfigurationUnitIntent::Assert)
+ {
+ assertions.emplace_back(unit);
+ }
+ else if (unit.Intent() == ConfigurationUnitIntent::Apply)
+ {
+ resources.emplace_back(unit);
+ }
+ }
+
+ Emitter emitter;
+
+ emitter << BeginMap;
+ emitter << Key << GetConfigurationFieldName(ConfigurationField::Properties);
+
+ emitter << BeginMap;
+ emitter << Key << GetConfigurationFieldName(ConfigurationField::ConfigurationVersion) << Value << AppInstaller::Utility::ConvertToUTF8(configurationSet->SchemaVersion());
+
+ if (!assertions.empty())
+ {
+ emitter << Key << GetConfigurationFieldName(ConfigurationField::Assertions);
+ WriteYamlConfigurationUnits(emitter, assertions);
+ }
+
+ if (!resources.empty())
+ {
+ emitter << Key << GetConfigurationFieldName(ConfigurationField::Resources);
+ WriteYamlConfigurationUnits(emitter, resources);
+ }
+
+ emitter << EndMap;
+ emitter << EndMap;
+
+ return winrt::to_hstring(emitter.str());
+ }
+}
diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetSerializer_0_2.h b/src/Microsoft.Management.Configuration/ConfigurationSetSerializer_0_2.h
new file mode 100644
index 0000000000..c7034215d9
--- /dev/null
+++ b/src/Microsoft.Management.Configuration/ConfigurationSetSerializer_0_2.h
@@ -0,0 +1,22 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+#pragma once
+#include "ConfigurationSetSerializer.h"
+
+namespace winrt::Microsoft::Management::Configuration::implementation
+{
+ // Serializer for schema version 0.2
+ struct ConfigurationSetSerializer_0_2 : public ConfigurationSetSerializer
+ {
+ ConfigurationSetSerializer_0_2() {}
+
+ virtual ~ConfigurationSetSerializer_0_2() noexcept = default;
+
+ ConfigurationSetSerializer_0_2(const ConfigurationSetSerializer_0_2&) = delete;
+ ConfigurationSetSerializer_0_2& operator=(const ConfigurationSetSerializer_0_2&) = delete;
+ ConfigurationSetSerializer_0_2(ConfigurationSetSerializer_0_2&&) = default;
+ ConfigurationSetSerializer_0_2& operator=(ConfigurationSetSerializer_0_2&&) = default;
+
+ hstring Serialize(ConfigurationSet* configurationSet) override;
+ };
+}
diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetUtilities.cpp b/src/Microsoft.Management.Configuration/ConfigurationSetUtilities.cpp
new file mode 100644
index 0000000000..80c64a732f
--- /dev/null
+++ b/src/Microsoft.Management.Configuration/ConfigurationSetUtilities.cpp
@@ -0,0 +1,50 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+#include "pch.h"
+#include "ConfigurationSetUtilities.h"
+#include
+
+using namespace std::string_view_literals;
+
+namespace winrt::Microsoft::Management::Configuration::implementation
+{
+ std::string_view GetConfigurationFieldName(ConfigurationField fieldName)
+ {
+ switch (fieldName)
+ {
+ case ConfigurationField::ConfigurationVersion: return "configurationVersion"sv;
+ case ConfigurationField::Properties: return "properties"sv;
+ case ConfigurationField::Resource: return "resource"sv;
+ case ConfigurationField::Directives: return "directives"sv;
+ case ConfigurationField::Settings: return "settings"sv;
+ case ConfigurationField::Assertions: return "assertions"sv;
+ case ConfigurationField::Id: return "id"sv;
+ case ConfigurationField::DependsOn: return "dependsOn"sv;
+
+ case ConfigurationField::Resources: return "resources"sv;
+ case ConfigurationField::ModuleDirective: return "module"sv;
+
+ case ConfigurationField::Schema: return "$schema"sv;
+ case ConfigurationField::Metadata: return "metadata"sv;
+ case ConfigurationField::Parameters: return "parameters"sv;
+ case ConfigurationField::Variables: return "variables"sv;
+ case ConfigurationField::Type: return "type"sv;
+ case ConfigurationField::Description: return "description"sv;
+ case ConfigurationField::Name: return "name"sv;
+ case ConfigurationField::IsGroupMetadata: return "isGroup"sv;
+ case ConfigurationField::DefaultValue: return "defaultValue"sv;
+ case ConfigurationField::AllowedValues: return "allowedValues"sv;
+ case ConfigurationField::MinimumLength: return "minLength"sv;
+ case ConfigurationField::MaximumLength: return "maxLength"sv;
+ case ConfigurationField::MinimumValue: return "minValue"sv;
+ case ConfigurationField::MaximumValue: return "maxValue"sv;
+ }
+
+ THROW_HR(E_UNEXPECTED);
+ }
+
+ hstring GetConfigurationFieldNameHString(ConfigurationField fieldName)
+ {
+ return hstring{ AppInstaller::Utility::ConvertToUTF16(GetConfigurationFieldName(fieldName)) };
+ }
+}
diff --git a/src/Microsoft.Management.Configuration/ConfigurationSetUtilities.h b/src/Microsoft.Management.Configuration/ConfigurationSetUtilities.h
new file mode 100644
index 0000000000..85a0f27ebc
--- /dev/null
+++ b/src/Microsoft.Management.Configuration/ConfigurationSetUtilities.h
@@ -0,0 +1,47 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+#pragma once
+#include
+#include
+
+namespace winrt::Microsoft::Management::Configuration::implementation
+{
+ // The various configuration fields that are used in parsing/serialization.
+ enum class ConfigurationField
+ {
+ // v0.1 and v0.2
+ ConfigurationVersion,
+ Properties,
+ Resource,
+ Directives,
+ Settings,
+ Assertions,
+ Id,
+ DependsOn,
+
+ // Universal
+ Resources,
+ ModuleDirective,
+
+ // v0.3
+ Schema,
+ Metadata,
+ Parameters,
+ Variables,
+ Type,
+ Description,
+ Name,
+ IsGroupMetadata,
+ DefaultValue,
+ AllowedValues,
+ MinimumLength,
+ MaximumLength,
+ MinimumValue,
+ MaximumValue,
+ };
+
+ // Gets the name value of the configuration field.
+ std::string_view GetConfigurationFieldName(ConfigurationField fieldName);
+
+ winrt::hstring GetConfigurationFieldNameHString(ConfigurationField fieldName);
+}
diff --git a/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj b/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj
index feddee7735..7dd3b9d52e 100644
--- a/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj
+++ b/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj
@@ -214,6 +214,9 @@
+
+
+
@@ -252,6 +255,9 @@
+
+
+
diff --git a/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj.filters b/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj.filters
index 16905780ad..ed58521881 100644
--- a/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj.filters
+++ b/src/Microsoft.Management.Configuration/Microsoft.Management.Configuration.vcxproj.filters
@@ -102,6 +102,15 @@
Internals
+
+ Parser
+
+
+ Parser
+
+
+ Parser
+
@@ -213,6 +222,11 @@
Internals
+
+
+
+ Parser
+
diff --git a/src/WinGetServer/WinMain.cpp b/src/WinGetServer/WinMain.cpp
index 541a13deb4..11fe1a3749 100644
--- a/src/WinGetServer/WinMain.cpp
+++ b/src/WinGetServer/WinMain.cpp
@@ -111,6 +111,8 @@ int __stdcall wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ LPWSTR cmdLine,
wil::com_ptr globalOptions;
RETURN_IF_FAILED(CoCreateInstance(CLSID_GlobalOptions, nullptr, CLSCTX_INPROC, IID_PPV_ARGS(&globalOptions)));
RETURN_IF_FAILED(globalOptions->Set(COMGLB_RO_SETTINGS, COMGLB_FAST_RUNDOWN));
+ RETURN_IF_FAILED(globalOptions->Set(COMGLB_UNMARSHALING_POLICY, COMGLB_UNMARSHALING_POLICY_STRONG));
+ RETURN_IF_FAILED(globalOptions->Set(COMGLB_EXCEPTION_HANDLING, COMGLB_EXCEPTION_DONOT_HANDLE_ANY));
}
RETURN_IF_FAILED(WindowsPackageManagerServerInitialize());
diff --git a/src/WindowsPackageManager/ConfigurationStaticFunctions.cpp b/src/WindowsPackageManager/ConfigurationStaticFunctions.cpp
index d45a662acd..0e2a9a2151 100644
--- a/src/WindowsPackageManager/ConfigurationStaticFunctions.cpp
+++ b/src/WindowsPackageManager/ConfigurationStaticFunctions.cpp
@@ -110,6 +110,10 @@ namespace ConfigurationShim
{
result = AppInstaller::CLI::ConfigurationRemoting::CreateOutOfProcessFactory();
}
+ else if (lowerHandler == AppInstaller::Configuration::DynamicRuntimeHandlerIdentifier)
+ {
+ result = AppInstaller::CLI::ConfigurationRemoting::CreateDynamicRuntimeFactory();
+ }
if (result)
{