Skip to content

Commit

Permalink
Add support for optional limitation set in configuration remote serve…
Browse files Browse the repository at this point in the history
…r and processors #4349 (#4360)

Cherry pick for #4349 into 1.7 release branch
  • Loading branch information
ryfu-msft authored Apr 8, 2024
1 parent 3a9093b commit 26a4486
Show file tree
Hide file tree
Showing 29 changed files with 928 additions and 88 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ The client is built around the concept of sources; a set of packages effectively
* .NET Desktop Development
* Desktop Development with C++
* Universal Windows Platform Development
* [Windows 10 SDK, version 2004 (10.0.19041.0)](https://developer.microsoft.com/en-us/windows/downloads/sdk-archive/)
> **Note**: You can also get it through `winget install Microsoft.WindowsSDK --version 10.0.19041.685` (use --force if you have a newer version installed) or via Visual Studio > Get Tools and Features > Individual Components > Windows 10 SDK (10.0.19041.0)
* [Windows SDK for Windows 11 (10.0.22000.194)](https://developer.microsoft.com/en-us/windows/downloads/sdk-archive/)
> **Note**: You can also get it through `winget install Microsoft.WindowsSDK --version 10.0.22000.832` (use --force if you have a newer version installed) or via Visual Studio > Get Tools and Features > Individual Components > Windows 10 SDK (10.0.22000.0)
* The following extensions:
* [Microsoft Visual Studio Installer Projects](https://marketplace.visualstudio.com/items?itemName=VisualStudioClient.MicrosoftVisualStudio2022InstallerProjects)

Expand Down
2 changes: 1 addition & 1 deletion azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ jobs:
inputs:
SourceFolder: $(buildOutDirAnyCpu)
Contents: |
Microsoft.Management.Configuration.Projection\net6.0-windows10.0.19041.0\Microsoft.Management.Configuration.Projection.dll
Microsoft.Management.Configuration.Projection\net6.0-windows10.0.22000.0\Microsoft.Management.Configuration.Projection.dll
TargetFolder: $(buildOutDirAnyCpu)\PowerShell\Microsoft.WinGet.Configuration\SharedDependencies\$(BuildPlatform)
flattenFolders: true
condition: succeededOrFailed()
Expand Down
6 changes: 3 additions & 3 deletions src/AppInstallerCLICore/AppInstallerCLICore.vcxproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.230706.1\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.230706.1\build\native\Microsoft.Windows.CppWinRT.props')" />
<PropertyGroup Label="Globals">
Expand Down Expand Up @@ -537,7 +537,7 @@
<Error Condition="!Exists('$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.230706.1\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorTextNuget)', '$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.230706.1\build\native\Microsoft.Windows.CppWinRT.targets'))" />
<PropertyGroup>
<MicrosoftManagementConfigurationProcessorPath>$(OutputPath)..\Microsoft.Management.Configuration.Processor\Microsoft.Management.Configuration.Processor.winmd</MicrosoftManagementConfigurationProcessorPath>
<MicrosoftManagementConfigurationProcessorPath Condition="!Exists('$(MicrosoftManagementConfigurationProcessorPath)')">$(SolutionDir)\AnyCPU\$(Configuration)\Microsoft.Management.Configuration.Processor\net6.0-windows10.0.19041.0\win\Microsoft.Management.Configuration.Processor.winmd</MicrosoftManagementConfigurationProcessorPath>
<MicrosoftManagementConfigurationProcessorPath Condition="!Exists('$(MicrosoftManagementConfigurationProcessorPath)')">$(SolutionDir)\AnyCPU\$(Configuration)\Microsoft.Management.Configuration.Processor\net6.0-windows10.0.22000.0\win\Microsoft.Management.Configuration.Processor.winmd</MicrosoftManagementConfigurationProcessorPath>
</PropertyGroup>
<Message Importance="high" Text="Microsoft.Management.Configuration.Processor.winmd -&gt; $(MicrosoftManagementConfigurationProcessorPath)" />
<Error Condition="!Exists('$(MicrosoftManagementConfigurationProcessorPath)')" Text="Microsoft.Management.Configuration.Processor.winmd was not found in $(MicrosoftManagementConfigurationProcessorPath)" />
Expand All @@ -548,4 +548,4 @@
</Reference>
</ItemGroup>
</Target>
</Project>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@ namespace AppInstaller::CLI::ConfigurationRemoting

namespace
{
// The name of the directory containing additional modules.
constexpr std::wstring_view s_ExternalModulesName = L"ExternalModules";

// The executable file name for the remote server process.
constexpr std::wstring_view s_RemoteServerFileName = L"ConfigurationRemotingServer\\ConfigurationRemotingServer.exe";

Expand Down Expand Up @@ -146,17 +143,6 @@ namespace AppInstaller::CLI::ConfigurationRemoting
AICLI_LOG(Config, Verbose, << "... configuration processing connection established.");
m_remoteFactory = IConfigurationSetProcessorFactory{ output.detach(), winrt::take_ownership_from_abi };

// The additional modules path is a direct child directory to the package root
std::filesystem::path externalModules = Runtime::GetPathTo(Runtime::PathName::SelfPackageRoot) / s_ExternalModulesName;
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), !std::filesystem::is_directory(externalModules));
m_internalAdditionalModulePaths.emplace_back(externalModules.wstring());
m_remoteAdditionalModulePaths = winrt::single_threaded_vector<winrt::hstring>(std::vector<winrt::hstring>{ m_internalAdditionalModulePaths });

auto properties = m_remoteFactory.as<Processor::IPowerShellConfigurationProcessorFactoryProperties>();
AICLI_LOG(Config, Verbose, << "Applying built in additional module path: " << externalModules.u8string());
properties.AdditionalModulePaths(m_remoteAdditionalModulePaths.GetView());
properties.ProcessorType(Processor::PowerShellConfigurationProcessorType::Hosted);

completeEventIfFailureDuringConstruction.release();
}

Expand Down Expand Up @@ -201,11 +187,8 @@ namespace AppInstaller::CLI::ConfigurationRemoting
std::vector<winrt::hstring> newModulePaths{ value.Size() };
value.GetMany(0, newModulePaths);

// Combine with our own values
// Create a copy for remote and set remote module paths
std::vector<winrt::hstring> newRemotePaths{ newModulePaths };
newRemotePaths.insert(newRemotePaths.end(), m_internalAdditionalModulePaths.begin(), m_internalAdditionalModulePaths.end());

// Apply the new combined paths and pass to remote factory
m_remoteAdditionalModulePaths = winrt::single_threaded_vector<winrt::hstring>(std::move(newRemotePaths));
m_remoteFactory.as<Processor::IPowerShellConfigurationProcessorFactoryProperties>().AdditionalModulePaths(m_remoteAdditionalModulePaths.GetView());

Expand Down Expand Up @@ -276,7 +259,6 @@ namespace AppInstaller::CLI::ConfigurationRemoting
IConfigurationSetProcessorFactory m_remoteFactory;
wil::unique_event m_completionEvent;
Collections::IVector<winrt::hstring> m_additionalModulePaths{ winrt::single_threaded_vector<winrt::hstring>() };
std::vector<winrt::hstring> m_internalAdditionalModulePaths;
Collections::IVector<winrt::hstring> m_remoteAdditionalModulePaths{ winrt::single_threaded_vector<winrt::hstring>() };
};
}
Expand Down
4 changes: 2 additions & 2 deletions src/AppInstallerCLIPackage/AppInstallerCLIPackage.wapproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '15.0'">
<VisualStudioVersion>15.0</VisualStudioVersion>
Expand Down Expand Up @@ -251,7 +251,7 @@
<WinGetAdditionalPackageFile Include="$(WinGetAdditonalPackageFileRoot)\AnyCPU\$(Configuration)\Microsoft.Management.Configuration.Projection\**\Microsoft.Management.Configuration.Projection.dll">
<PackagePath>ConfigurationRemotingServer\Microsoft.Management.Configuration.Projection.dll</PackagePath>
</WinGetAdditionalPackageFile>
<WinGetAdditionalPackageFile Include="$(WinGetAdditonalPackageFileRoot)\$(PlatformTarget)\$(Configuration)\ConfigurationRemotingServer\net6.0-windows10.0.19041.0\$(ConfigServerRid)\**\*">
<WinGetAdditionalPackageFile Include="$(WinGetAdditonalPackageFileRoot)\$(PlatformTarget)\$(Configuration)\ConfigurationRemotingServer\net6.0-windows10.0.22000.0\$(ConfigServerRid)\**\*">
<PackagePath>ConfigurationRemotingServer</PackagePath>
<Recurse>true</Recurse>
</WinGetAdditionalPackageFile>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
<TargetFramework>net6.0-windows10.0.22000.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<SupportedOSPlatformVersion>10.0.17763.0</SupportedOSPlatformVersion>
Expand Down
109 changes: 107 additions & 2 deletions src/ConfigurationRemotingServer/Program.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.Management.Configuration.Processor;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Management.Configuration;
using Microsoft.Management.Configuration.Processor;
using Windows.Storage.Streams;
using WinRT;

namespace ConfigurationRemotingServer
{
internal class Program
{
private const string CommandLineSectionSeparator = "~~~~~~";
private const string ExternalModulesName = "ExternalModules";

static int Main(string[] args)
{
ulong memoryHandle = ulong.Parse(args[0]);
Expand All @@ -21,6 +30,74 @@ static int Main(string[] args)

PowerShellConfigurationSetProcessorFactory factory = new PowerShellConfigurationSetProcessorFactory();

// Set default properties.
var externalModulesPath = GetExternalModulesPath();
if (string.IsNullOrWhiteSpace(externalModulesPath))
{
throw new DirectoryNotFoundException("Failed to get ExternalModules.");
}

// Set as implicit module paths so it will be always included in AdditionalModulePaths
factory.ImplicitModulePaths = new List<string>() { externalModulesPath };
factory.ProcessorType = PowerShellConfigurationProcessorType.Hosted;

// Parse limitation set if applicable.
// The format will be:
// <Common args for initialization> ~~~~~~ <Metadata json> ~~~~~~ <Limitation Set in yaml>
// Metadata json format:
// {
// "path": "C:\full\file\path.yaml"
// }
// If a limitation set is provided, the processor will be limited
// to only work on units defined inside the limitation set.
var commandPtr = GetCommandLineW();
var commandStr = Marshal.PtrToStringUni(commandPtr) ?? string.Empty;

// In case the limitation set content contains the separator, we'll not use Split method.
var firstSeparatorIndex = commandStr.IndexOf(CommandLineSectionSeparator);
if (firstSeparatorIndex > 0)
{
var secondSeparatorIndex = commandStr.IndexOf(CommandLineSectionSeparator, firstSeparatorIndex + CommandLineSectionSeparator.Length);
if (secondSeparatorIndex <= 0)
{
throw new ArgumentException("The input command contains only one separator string.");
}

// Parse limitation set.
byte[] limitationSetBytes = Encoding.UTF8.GetBytes(commandStr.Substring(secondSeparatorIndex + CommandLineSectionSeparator.Length));
InMemoryRandomAccessStream limitationSetStream = new InMemoryRandomAccessStream();
DataWriter streamWriter = new DataWriter(limitationSetStream);
streamWriter.WriteBytes(limitationSetBytes);
streamWriter.StoreAsync().GetAwaiter().GetResult();
streamWriter.DetachStream();
limitationSetStream.Seek(0);
ConfigurationProcessor processor = new ConfigurationProcessor(factory);
var limitationSetResult = processor.OpenConfigurationSet(limitationSetStream);
if (limitationSetResult.ResultCode != null)
{
throw limitationSetResult.ResultCode;
}

var limitationSet = limitationSetResult.Set;
if (limitationSet == null)
{
throw new ArgumentException("The limitation set cannot be parsed.");
}

// Now parse metadata json and update the limitation set
var metadataJson = JsonSerializer.Deserialize<LimitationSetMetadata>(commandStr.Substring(
firstSeparatorIndex + CommandLineSectionSeparator.Length,
secondSeparatorIndex - firstSeparatorIndex - CommandLineSectionSeparator.Length));

if (metadataJson != null)
{
limitationSet.Path = metadataJson.Path;
}

// Set the limitation set in factory.
factory.LimitationSet = limitationSet;
}

IObjectReference factoryInterface = MarshalInterface<global::Microsoft.Management.Configuration.IConfigurationSetProcessorFactory>.CreateMarshaler(factory);

return WindowsPackageManagerConfigurationCompleteOutOfProcessFactoryInitialization(0, factoryInterface.ThisPtr, memoryHandle, initEventHandle, completionEventHandle, parentProcessHandle);
Expand All @@ -32,7 +109,35 @@ static int Main(string[] args)
}
}

private class LimitationSetMetadata
{
[JsonPropertyName("path")]
public string Path { get; set; } = string.Empty;
}

private static string GetExternalModulesPath()
{
var currentAssemblyDirectoryPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
if (currentAssemblyDirectoryPath != null)
{
var packageRootPath = Directory.GetParent(currentAssemblyDirectoryPath)?.FullName;
if (packageRootPath != null)
{
var externalModulesPath = Path.Combine(packageRootPath, ExternalModulesName);
if (Directory.Exists(externalModulesPath))
{
return externalModulesPath;
}
}
}

return string.Empty;
}

[DllImport("WindowsPackageManager.dll")]
private static extern int WindowsPackageManagerConfigurationCompleteOutOfProcessFactoryInitialization(int result, IntPtr factory, ulong memoryHandle, ulong initEventHandle, ulong completionMutexHandle, ulong parentProcessHandle);

[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr GetCommandLineW();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ param(

# Copy the winmd into the unit test directory since it will be needed for marshalling
$Local:winmdSourcePath = Join-Path $BuildOutputPath "Microsoft.Management.Configuration\Microsoft.Management.Configuration.winmd"
$Local:winmdTargetPath = Join-Path $BuildOutputPath "Microsoft.Management.Configuration.UnitTests\net6.0-windows10.0.19041.0\Microsoft.Management.Configuration.winmd"
$Local:winmdTargetPath = Join-Path $BuildOutputPath "Microsoft.Management.Configuration.UnitTests\net6.0-windows10.0.22000.0\Microsoft.Management.Configuration.winmd"

Copy-Item $Local:winmdSourcePath $Local:winmdTargetPath -Force

# Copy the OOP helper dll into the unit test directory to make activation look the same as in-proc
$Local:dllSourcePath = Join-Path $BuildOutputPath "Microsoft.Management.Configuration.OutOfProc\Microsoft.Management.Configuration.OutOfProc.dll"
$Local:dllTargetPath = Join-Path $BuildOutputPath "Microsoft.Management.Configuration.UnitTests\net6.0-windows10.0.19041.0\Microsoft.Management.Configuration.dll"
$Local:dllTargetPath = Join-Path $BuildOutputPath "Microsoft.Management.Configuration.UnitTests\net6.0-windows10.0.22000.0\Microsoft.Management.Configuration.dll"

Copy-Item $Local:dllSourcePath $Local:dllTargetPath -Force

Expand Down
Loading

0 comments on commit 26a4486

Please sign in to comment.