winget-cli

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

commit bba4994c747dee8b6d558bfb3d7e5677b2188c9d
parent e8b8f31f948531a11b170fc17d270381d97c67c8
Author: KEINOS <github+fork-qiita-news@keinos.com>
Date:   Fri, 13 Jun 2025 02:16:37 +0000

Merge remote-tracking branch 'upstream/master'

Diffstat:
Mazure-pipelines.yml | 16+---------------
Msrc/ConfigurationRemotingServer/ConfigurationRemotingServer.csproj | 1+
Msrc/ConfigurationRemotingServer/Program.cs | 37++++++++++++++++++++++++++++++++++++-
Msrc/Microsoft.Management.Configuration.Processor/DSCv3/Helpers/ProcessExecution.cs | 9++++++++-
Asrc/Microsoft.Management.Configuration.Processor/Public/PathEnvironmentVariableHandler.cs | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 115 insertions(+), 17 deletions(-)

diff --git a/azure-pipelines.yml b/azure-pipelines.yml @@ -64,7 +64,6 @@ jobs: buildOutDirAnyCpu: $(Build.SourcesDirectory)\src\AnyCPU\$(buildConfiguration) artifactsDir: $(Build.ArtifactStagingDirectory)\$(buildPlatform) appxPackageDir: $(Build.ArtifactStagingDirectory)\$(buildPlatform)\AppxPackages - packageLayoutDir: $(Build.BinariesDirectory)\WingetPackageLayout steps: - task: NuGetToolInstaller@1 @@ -153,7 +152,6 @@ jobs: displayName: 'Copy specific build artifacts' inputs: Contents: | - $(buildOutDir)\WindowsPackageManager\WindowsPackageManager.pdb $(buildOutDir)\WinGetUtil\WinGetUtil.dll $(buildOutDir)\WinGetUtil\WinGetUtil.pdb TargetFolder: '$(artifactsDir)' @@ -162,22 +160,10 @@ jobs: displayName: Create Package Layout inputs: filePath: 'src\AppInstallerCLIPackage\Execute-AppxRecipe.ps1' - arguments: '-AppxRecipePath AppInstallerCLIPackage\bin\$(buildPlatform)\$(buildConfiguration)\AppInstallerCLIPackage.build.appxrecipe -LayoutPath $(packageLayoutDir) -Force -Verbose' + arguments: '-AppxRecipePath AppInstallerCLIPackage\bin\$(buildPlatform)\$(buildConfiguration)\AppInstallerCLIPackage.build.appxrecipe -LayoutPath $(artifactsDir)\DevPackage -Force -Verbose' workingDirectory: 'src' - task: CopyFiles@2 - displayName: 'Copy Dev Package (Loose Files)' - inputs: - SourceFolder: '$(packageLayoutDir)' - TargetFolder: '$(artifactsDir)\DevPackage' - - - task: DeleteFiles@1 - displayName: Clean up Package Layout after copy - inputs: - SourceFolder: '$(packageLayoutDir)' - Contents: '**/*' - - - task: CopyFiles@2 displayName: 'Copy native binaries for Microsoft.WinGet.Client (net8)' inputs: SourceFolder: $(buildOutDir) diff --git a/src/ConfigurationRemotingServer/ConfigurationRemotingServer.csproj b/src/ConfigurationRemotingServer/ConfigurationRemotingServer.csproj @@ -3,6 +3,7 @@ <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0-windows10.0.26100.0</TargetFramework> + <UseWindowsForms>true</UseWindowsForms> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <SupportedOSPlatformVersion>10.0.17763.0</SupportedOSPlatformVersion> diff --git a/src/ConfigurationRemotingServer/Program.cs b/src/ConfigurationRemotingServer/Program.cs @@ -8,6 +8,8 @@ using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.Management.Configuration; using Microsoft.Management.Configuration.Processor; +using Microsoft.Management.Configuration.Processor.Helpers; +using Microsoft.Win32; using WinRT; using IConfigurationSetProcessorFactory = global::Microsoft.Management.Configuration.IConfigurationSetProcessorFactory; @@ -79,6 +81,18 @@ namespace ConfigurationRemotingServer string staticsCallback = args[1]; + // Listen for setting change message and update PATH if needed. + HiddenForm hiddenForm = new HiddenForm(); + var settingChangedListenerThread = new Thread( + () => + { + SystemEvents.UserPreferenceChanged += OnUserPreferenceChanged; + Application.Run(hiddenForm); + SystemEvents.UserPreferenceChanged -= OnUserPreferenceChanged; + }); + + settingChangedListenerThread.Start(); + try { string completionEventName = args[2]; @@ -147,11 +161,32 @@ namespace ConfigurationRemotingServer return WindowsPackageManagerConfigurationCompleteOutOfProcessFactoryInitialization(0, factoryInterface.ThisPtr, staticsCallback, completionEventName, parentProcessId); } - catch(Exception ex) + catch (Exception ex) { WindowsPackageManagerConfigurationCompleteOutOfProcessFactoryInitialization(ex.HResult, IntPtr.Zero, staticsCallback, null, 0); return ex.HResult; } + finally + { + hiddenForm.BeginInvoke(new Action(() => { hiddenForm.Close(); })); + settingChangedListenerThread.Join(); + } + } + + private class HiddenForm : Form + { + public HiddenForm() + { + this.Width = 0; + this.Height = 0; + this.Visible = false; + this.Load += (sender, e) => this.Hide(); + } + } + + private static void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) + { + PathEnvironmentVariableHandler.UpdatePath(); } private class LimitationSetMetadata diff --git a/src/Microsoft.Management.Configuration.Processor/DSCv3/Helpers/ProcessExecution.cs b/src/Microsoft.Management.Configuration.Processor/DSCv3/Helpers/ProcessExecution.cs @@ -11,6 +11,7 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Helpers using System.Diagnostics; using System.Text; using System.Threading; + using Microsoft.Management.Configuration.Processor.Helpers; /// <summary> /// Wrapper for a single process execution and its output. @@ -134,7 +135,13 @@ namespace Microsoft.Management.Configuration.Processor.DSCv3.Helpers throw new InvalidOperationException("Process has already been started."); } - ProcessStartInfo startInfo = new ProcessStartInfo(this.ExecutablePath, this.SerializedArguments); + ProcessStartInfo startInfo; + + lock (PathEnvironmentVariableHandler.Lock) + { + startInfo = new ProcessStartInfo(this.ExecutablePath, this.SerializedArguments); + } + this.Process = new Process() { StartInfo = startInfo }; startInfo.UseShellExecute = false; diff --git a/src/Microsoft.Management.Configuration.Processor/Public/PathEnvironmentVariableHandler.cs b/src/Microsoft.Management.Configuration.Processor/Public/PathEnvironmentVariableHandler.cs @@ -0,0 +1,69 @@ +// ----------------------------------------------------------------------------- +// <copyright file="PathEnvironmentVariableHandler.cs" company="Microsoft Corporation"> +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// </copyright> +// ----------------------------------------------------------------------------- + +namespace Microsoft.Management.Configuration.Processor.Helpers +{ + using System; + using System.Collections.Generic; + + /// <summary> + /// Class for handling PATH environment variable. + /// </summary> + public static class PathEnvironmentVariableHandler + { + private const string PathEnvironmentVariable = "PATH"; + + private static readonly object EnvironmentVariableLock = new object(); + + /// <summary> + /// Gets the lock to read or write PATH environment variable. + /// </summary> + public static object Lock + { + get { return EnvironmentVariableLock; } + } + + /// <summary> + /// Updates the process's PATH environment variable if new paths added. + /// Only adds new paths since we add to PATH in other code which may not be in the registry. + /// </summary> + public static void UpdatePath() + { + HashSet<string> paths = new HashSet<string>(Environment.GetEnvironmentVariable(PathEnvironmentVariable)?.Split(';') ?? Array.Empty<string>()); + var originalPathsSize = paths.Count; + + AddPathsIfNotExist(paths, Environment.GetEnvironmentVariable(PathEnvironmentVariable, EnvironmentVariableTarget.Machine)?.Split(';')); + AddPathsIfNotExist(paths, Environment.GetEnvironmentVariable(PathEnvironmentVariable, EnvironmentVariableTarget.User)?.Split(';')); + + if (paths.Count > originalPathsSize) + { + lock (Lock) + { + Environment.SetEnvironmentVariable(PathEnvironmentVariable, string.Join(';', paths)); + } + } + } + + // TODO: Currently it always adds new paths to the end. The "proper" thing to do would probably be to calculate + // the full new list of paths (what one would expect to get from a new process launch) and use a line merge algorithm + // with a strategy that puts the ephemeral entries before the new permanent ones. +#pragma warning disable SA1011 // Closing square brackets should be spaced correctly + private static void AddPathsIfNotExist(HashSet<string> currentPaths, string[]? paths) +#pragma warning restore SA1011 // Closing square brackets should be spaced correctly + { + if (paths is not null) + { + foreach (var path in paths) + { + if (!currentPaths.Contains(path)) + { + currentPaths.Add(path); + } + } + } + } + } +}