Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for optional limitation set in configuration remote server and processors #4349 #4360

Merged
merged 1 commit into from
Apr 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading