From 09c87714da8cd08c4db884bd0c0bc4f56343e50a Mon Sep 17 00:00:00 2001 From: Ruben Guerrero Date: Fri, 10 Nov 2023 15:21:24 -0800 Subject: [PATCH] Allow Microsoft.WinGet.Client to run in any PowerShell session running as system (#3816) This PR adds support for running the Microsoft.WinGet.Client module in system context without the need to start pwsh.exe -MTA. Running as MTA is required using inproc Microsoft.Management.Deployment. If the module is running inproc and the current thread is not an MTA, it will create a new MTA thread and execute there. Otherwise, non inproc or already an MTA will use the current thread. This was done by sharing AsyncCommand (renamed to PowerShellCmdlet) from Microsoft.WinGet.Configuration. Originally, I wanted to create a new shared lib, but decided to just share the files between the projects. All cmdlets must inherit from PowerShellCmdlet. All cmdlets that use Microsoft.Management.Deployment must inherit ManagementDeploymentCommand and use Execute at the command engine entry point. As a safe mechanism, if any call to PackageManagerWrapper will verify the thread is not an STA if running inproc and fail. I verified the cmdlets work in system context locally. A future PR will add running our pester tests using psexe.exe --- .../PowerShellCmdlet.cs} | 337 ++++++++++-------- src/PowerShell/CommonFiles/StreamType.cs | 49 +++ .../Commands/CliCommand.cs | 5 +- .../Commands/Common/BaseCommand.cs | 33 +- .../Commands/Common/FinderCommand.cs | 20 +- .../Commands/Common/FinderExtendedCommand.cs | 4 +- .../Commands/Common/InstallCommand.cs | 17 +- .../Common/ManagementDeploymentCommand.cs | 26 +- .../Commands/Common/PackageCommand.cs | 25 +- .../Commands/FinderPackageCommand.cs | 16 +- .../Commands/InstallerPackageCommand.cs | 60 ++-- .../Commands/SourceCommand.cs | 6 +- .../Commands/UninstallPackageCommand.cs | 36 +- .../Commands/UserSettingsCommand.cs | 63 ++-- .../Commands/VersionCommand.cs | 3 +- .../Commands/WinGetPackageManagerCommand.cs | 34 +- .../Common/Utilities.cs | 8 +- .../Common/WinGetIntegrity.cs | 25 +- .../Exceptions/CatalogConnectException.cs | 2 +- .../Exceptions/FindPackagesException.cs | 2 +- .../Exceptions/InvalidSourceException.cs | 2 +- .../Exceptions/InvalidVersionException.cs | 2 +- .../Exceptions/NoPackageFoundException.cs | 2 +- .../SingleThreadedApartmentException.cs | 2 +- .../Exceptions/UserSettingsReadException.cs | 2 +- .../Exceptions/VagueCriteriaException.cs | 2 +- .../Exceptions/WinGetCLIException.cs | 6 +- .../Exceptions/WinGetCLITimeoutException.cs | 4 +- .../Exceptions/WinGetIntegrityException.cs | 2 +- .../Exceptions/WinGetRepairException.cs | 2 +- .../WindowsPowerShellNotSupported.cs | 2 +- .../Extensions/CatalogPackageExtensions.cs | 2 +- .../Helpers/AppxModuleHelper.cs | 49 +-- .../Helpers/ManagementDeploymentFactory.cs | 32 +- .../Helpers/PackageManagerWrapper.cs | 11 +- .../Helpers/TempDirectory.cs | 2 +- .../Helpers/TempFile.cs | 6 +- .../Helpers/WinGetCLICommandResult.cs | 4 +- .../Helpers/WingetCLIWrapper.cs | 2 +- .../Helpers/WriteProgressAdapter.cs | 11 +- .../Microsoft.WinGet.Client.Engine.csproj | 26 +- .../PSObjects/PSCatalogPackage.cs | 2 +- .../Properties/Resources.Designer.cs | 11 +- .../Properties/Resources.resx | 3 + .../Commands/ConfigurationCommand.cs | 24 +- .../Exceptions/ApplyConfigurationException.cs | 2 +- .../Exceptions/GetDetailsException.cs | 2 +- .../OpenConfigurationSetException.cs | 2 +- .../ApplyConfigurationSetProgressOutput.cs | 4 +- .../ConfigurationSetProgressOutputBase.cs | 6 +- .../Helpers/ConfigurationUnitInformation.cs | 2 +- ...etConfigurationSetDetailsProgressOutput.cs | 4 +- .../Helpers/OpenConfigurationParameters.cs | 13 +- .../TestConfigurationSetProgressOutput.cs | 4 +- ...crosoft.WinGet.Configuration.Engine.csproj | 6 + .../PSObjects/PSConfigurationJob.cs | 6 +- .../PSObjects/PSConfigurationProcessor.cs | 18 +- .../PSObjects/PSUnitResult.cs | 2 +- .../Resources/Resources.Designer.cs | 2 +- 59 files changed, 618 insertions(+), 439 deletions(-) rename src/PowerShell/{Microsoft.WinGet.Configuration.Engine/Commands/AsyncCommand.cs => CommonFiles/PowerShellCmdlet.cs} (61%) create mode 100644 src/PowerShell/CommonFiles/StreamType.cs diff --git a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Commands/AsyncCommand.cs b/src/PowerShell/CommonFiles/PowerShellCmdlet.cs similarity index 61% rename from src/PowerShell/Microsoft.WinGet.Configuration.Engine/Commands/AsyncCommand.cs rename to src/PowerShell/CommonFiles/PowerShellCmdlet.cs index d7171c5e5b..a354c5c321 100644 --- a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Commands/AsyncCommand.cs +++ b/src/PowerShell/CommonFiles/PowerShellCmdlet.cs @@ -1,34 +1,35 @@ // ----------------------------------------------------------------------------- -// +// // Copyright (c) Microsoft Corporation. Licensed under the MIT License. // // ----------------------------------------------------------------------------- -namespace Microsoft.WinGet.Configuration.Engine.Commands +namespace Microsoft.WinGet.Common.Command { using System; using System.Collections.Concurrent; + using System.Collections.Generic; using System.Management.Automation; using System.Threading; using System.Threading.Tasks; - using Microsoft.PowerShell.Commands; - using Microsoft.WinGet.Configuration.Engine.Exceptions; - using Microsoft.WinGet.Configuration.Engine.Resources; + using Microsoft.WinGet.Resources; using Microsoft.WinGet.SharedLib.Exceptions; using Microsoft.WinGet.SharedLib.PolicySettings; /// - /// This is the base class for any command that performs async operations. - /// It supports running tasks in an MTA thread via RunOnMta. - /// If the thread is already running on an MTA it will executed it, otherwise - /// it will create a new MTA thread. - /// + /// This must be the base class for every cmdlet for winget PowerShell modules. + /// It supports: + /// - Async operations. + /// - Execute on an MTA. If the thread is already running on an MTA it will executed it, otherwise + /// it will create a new MTA thread. /// Wait must be used to synchronously wait con the task. /// - public abstract class AsyncCommand + public abstract class PowerShellCmdlet { + private const string Debug = "Debug"; private static readonly string[] WriteInformationTags = new string[] { "PSHOST" }; + private readonly PSCmdlet psCmdlet; private readonly Thread originalThread; private readonly CancellationTokenSource source = new (); @@ -38,87 +39,27 @@ public abstract class AsyncCommand private ConcurrentDictionary progressRecords = new (); /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// PSCmdlet. - public AsyncCommand(PSCmdlet psCmdlet) + /// Policies. + public PowerShellCmdlet(PSCmdlet psCmdlet, HashSet policies) { // Passing Debug will make all the message actions to be Inquire. For async operations // and the current queue message implementation this doesn't make sense. // PowerShell will inquire for any message giving the impression that the task is // paused, but the async operation is still running. - if (psCmdlet.MyInvocation.BoundParameters.ContainsKey("Debug")) + if (psCmdlet.MyInvocation.BoundParameters.ContainsKey(Debug)) { throw new NotSupportedException(Resources.DebugNotSupported); } - GroupPolicy groupPolicy = GroupPolicy.GetInstance(); - - if (!groupPolicy.IsEnabled(Policy.WinGet)) - { - throw new GroupPolicyException(Policy.WinGet, GroupPolicyFailureType.BlockedByPolicy); - } - - if (!groupPolicy.IsEnabled(Policy.Configuration)) - { - throw new GroupPolicyException(Policy.Configuration, GroupPolicyFailureType.BlockedByPolicy); - } - - if (!groupPolicy.IsEnabled(Policy.WinGetCommandLineInterfaces)) - { - throw new GroupPolicyException(Policy.WinGetCommandLineInterfaces, GroupPolicyFailureType.BlockedByPolicy); - } + this.ValidatePolicies(policies); - this.PsCmdlet = psCmdlet; + this.psCmdlet = psCmdlet; this.originalThread = Thread.CurrentThread; } - /// - /// The write stream type. - /// - public enum StreamType - { - /// - /// Debug. - /// - Debug, - - /// - /// Verbose. - /// - Verbose, - - /// - /// Warning. - /// - Warning, - - /// - /// Error. - /// - Error, - - /// - /// Progress. - /// - Progress, - - /// - /// Object. - /// - Object, - - /// - /// Information. - /// - Information, - } - - /// - /// Gets the base cmdlet. - /// - protected PSCmdlet PsCmdlet { get; private set; } - /// /// Request cancellation for this command. /// @@ -127,21 +68,18 @@ public void Cancel() this.source.Cancel(); } - /// - /// Complete this operation. - /// - public virtual void Complete() - { - this.queuedStreams.CompleteAdding(); - } - /// /// Execute the delegate in a MTA thread. + /// Caller must wait on task. /// /// Function to execute. /// A representing the asynchronous operation. internal Task RunOnMTA(Func func) { + // .NET 4.8 doesn't support TaskCompletionSource. +#if POWERSHELL_WINDOWS + throw new NotImplementedException(); +#else // This must be called in the main thread. if (this.originalThread != Thread.CurrentThread) { @@ -151,7 +89,14 @@ internal Task RunOnMTA(Func func) if (Thread.CurrentThread.GetApartmentState() == ApartmentState.MTA) { this.Write(StreamType.Verbose, "Already running on MTA"); - return func(); + try + { + return func(); + } + finally + { + this.Complete(); + } } this.Write(StreamType.Verbose, "Creating MTA thread"); @@ -167,15 +112,21 @@ internal Task RunOnMTA(Func func) { tcs.SetException(e); } + finally + { + this.Complete(); + } }); thread.SetApartmentState(ApartmentState.MTA); thread.Start(); return tcs.Task; +#endif } /// /// Execute the delegate in a MTA thread. + /// Caller must wait on task. /// /// Function to execute. /// Return type of function. @@ -191,7 +142,14 @@ internal Task RunOnMTA(Func> func) if (Thread.CurrentThread.GetApartmentState() == ApartmentState.MTA) { this.Write(StreamType.Verbose, "Already running on MTA"); - return func(); + try + { + return func(); + } + finally + { + this.Complete(); + } } this.Write(StreamType.Verbose, "Creating MTA thread"); @@ -207,6 +165,10 @@ internal Task RunOnMTA(Func> func) { tcs.SetException(e); } + finally + { + this.Complete(); + } }); thread.SetApartmentState(ApartmentState.MTA); @@ -214,14 +176,67 @@ internal Task RunOnMTA(Func> func) return tcs.Task; } + /// + /// Execute the delegate in a MTA thread. + /// Synchronous call. + /// + /// Function to execute. + /// Return type of function. + /// A representing the asynchronous operation. + internal TResult RunOnMTA(Func func) + { + // This must be called in the main thread. + if (this.originalThread != Thread.CurrentThread) + { + throw new InvalidOperationException(); + } + + if (Thread.CurrentThread.GetApartmentState() == ApartmentState.MTA) + { + this.Write(StreamType.Verbose, "Already running on MTA"); + try + { + return func(); + } + finally + { + this.Complete(); + } + } + + this.Write(StreamType.Verbose, "Creating MTA thread"); + var tcs = new TaskCompletionSource(); + var thread = new Thread(() => + { + try + { + var result = func(); + tcs.SetResult(result); + } + catch (Exception e) + { + tcs.SetException(e); + } + finally + { + this.Complete(); + } + }); + + thread.SetApartmentState(ApartmentState.MTA); + thread.Start(); + this.Wait(tcs.Task); + return tcs.Task.Result; + } + /// /// Waits for the task to be completed. This MUST be called from the main thread. /// /// Task to wait for. - /// The command that can write to PowerShell. - internal void Wait(Task runningTask, AsyncCommand? writeCommand = null) + /// The cmdlet that can write to PowerShell. + internal void Wait(Task runningTask, PowerShellCmdlet? writeCmdlet = null) { - writeCommand ??= this; + writeCmdlet ??= this; // This must be called in the main thread. if (this.originalThread != Thread.CurrentThread) @@ -231,7 +246,7 @@ internal void Wait(Task runningTask, AsyncCommand? writeCommand = null) do { - this.ConsumeAndWriteStreams(writeCommand); + this.ConsumeAndWriteStreams(writeCmdlet); } while (!(runningTask.IsCompleted && this.queuedStreams.IsCompleted)); @@ -271,41 +286,6 @@ internal void Write(StreamType type, object data) this.queuedStreams.Add(new QueuedStream(type, data)); } - /// - /// Write error with an exception. - /// - /// Error id. - /// Exception. - internal void WriteError(ErrorRecordErrorId errorId, Exception e) - { - this.Write( - StreamType.Error, - new ErrorRecord( - e, - errorId.ToString(), - ErrorCategory.WriteError, - null)); - } - - /// - /// Write error with a message. Create WriteErrorException. - /// - /// Error id. - /// Message. - /// Inner exception. - internal void WriteError(ErrorRecordErrorId errorId, string message, Exception? e = null) - { - // The error record requires a exception that can't be null, but there's no requirement that it was thrown. - // If not specified use WriteErrorException. - this.Write( - StreamType.Error, - new ErrorRecord( - new WriteErrorException(message, e), - errorId.ToString(), - ErrorCategory.WriteError, - null)); - } - /// /// Helper to compute percentage and write progress for processing activities. /// @@ -346,8 +326,8 @@ internal void CompleteProgress(int activityId, string activity, string status) /// This method must be called in the original thread. /// WARNING: You must only call this when the task is completed or in Wait. /// - /// The command that can write to PowerShell. - internal void ConsumeAndWriteStreams(AsyncCommand writeCommand) + /// The cmdlet that can write to PowerShell. + internal void ConsumeAndWriteStreams(PowerShellCmdlet writeCmdlet) { // This must be called in the main thread. if (this.originalThread != Thread.CurrentThread) @@ -363,7 +343,7 @@ internal void ConsumeAndWriteStreams(AsyncCommand writeCommand) var queuedOutput = this.queuedStreams.Take(); if (queuedOutput != null) { - this.CmdletWrite(queuedOutput.Type, queuedOutput.Data, writeCommand); + this.CmdletWrite(queuedOutput.Type, queuedOutput.Data, writeCmdlet); } } } @@ -387,45 +367,124 @@ internal int GetNewProgressActivityId() /// Gets the cancellation token. /// /// CancellationToken. - protected CancellationToken GetCancellationToken() + internal CancellationToken GetCancellationToken() { return this.source.Token; } - private void CmdletWrite(StreamType streamType, object data, AsyncCommand writeCommand) + /// + /// Gets the current file system location from the cmdlet. + /// + /// Path. + internal string GetCurrentFileSystemLocation() + { + return this.psCmdlet.SessionState.Path.CurrentFileSystemLocation.Path; + } + + /// + /// Sets a variable. + /// + /// Variable name. + /// Value. + internal void SetVariable(string variableName, object value) + { + this.psCmdlet.SessionState.PSVariable.Set(variableName, value); + } + + /// + /// Prompts the user if it should continue processing if possible. + /// + /// Message. + /// If the operation should continue. + internal bool ShouldProcess(string target) + { + // If not on the main thread just continue. + if (this.originalThread != Thread.CurrentThread) + { + return true; + } + + return this.psCmdlet.ShouldProcess(target); + } + + private void Complete() + { + this.queuedStreams.CompleteAdding(); + } + + private void CmdletWrite(StreamType streamType, object data, PowerShellCmdlet writeCmdlet) { switch (streamType) { case StreamType.Debug: - writeCommand.PsCmdlet.WriteDebug((string)data); - break; + throw new NotSupportedException(); case StreamType.Verbose: - writeCommand.PsCmdlet.WriteVerbose((string)data); + writeCmdlet.psCmdlet.WriteVerbose((string)data); break; case StreamType.Warning: - writeCommand.PsCmdlet.WriteWarning((string)data); + writeCmdlet.psCmdlet.WriteWarning((string)data); break; case StreamType.Error: - writeCommand.PsCmdlet.WriteError((ErrorRecord)data); + writeCmdlet.psCmdlet.WriteError((ErrorRecord)data); break; case StreamType.Progress: // If the activity is already completed don't write progress. var progressRecord = (ProgressRecord)data; if (this.progressRecords[progressRecord.ActivityId] == ProgressRecordType.Processing) { - writeCommand.PsCmdlet.WriteProgress(progressRecord); + writeCmdlet.psCmdlet.WriteProgress(progressRecord); } break; case StreamType.Object: - writeCommand.PsCmdlet.WriteObject(data); + writeCmdlet.psCmdlet.WriteObject(data); break; case StreamType.Information: - writeCommand.PsCmdlet.WriteInformation(data, WriteInformationTags); + writeCmdlet.psCmdlet.WriteInformation(data, WriteInformationTags); break; } } + private void ValidatePolicies(HashSet policies) + { + GroupPolicy groupPolicy = GroupPolicy.GetInstance(); + + if (policies.Contains(Policy.WinGet)) + { + if (!groupPolicy.IsEnabled(Policy.WinGet)) + { + throw new GroupPolicyException(Policy.WinGet, GroupPolicyFailureType.BlockedByPolicy); + } + + policies.Remove(Policy.WinGet); + } + + if (policies.Contains(Policy.Configuration)) + { + if (!groupPolicy.IsEnabled(Policy.Configuration)) + { + throw new GroupPolicyException(Policy.Configuration, GroupPolicyFailureType.BlockedByPolicy); + } + + policies.Remove(Policy.Configuration); + } + + if (policies.Contains(Policy.WinGetCommandLineInterfaces)) + { + if (!groupPolicy.IsEnabled(Policy.WinGetCommandLineInterfaces)) + { + throw new GroupPolicyException(Policy.WinGetCommandLineInterfaces, GroupPolicyFailureType.BlockedByPolicy); + } + + policies.Remove(Policy.WinGetCommandLineInterfaces); + } + + if (policies.Count > 0) + { + throw new NotSupportedException($"Invalid policies {string.Join(",", policies)}"); + } + } + private class QueuedStream { public QueuedStream(StreamType type, object data) diff --git a/src/PowerShell/CommonFiles/StreamType.cs b/src/PowerShell/CommonFiles/StreamType.cs new file mode 100644 index 0000000000..251f93c5fa --- /dev/null +++ b/src/PowerShell/CommonFiles/StreamType.cs @@ -0,0 +1,49 @@ +// ----------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. Licensed under the MIT License. +// +// ----------------------------------------------------------------------------- + +namespace Microsoft.WinGet.Common.Command +{ + /// + /// The write stream type of the cmdlet. + /// + public enum StreamType + { + /// + /// Debug. + /// + Debug, + + /// + /// Verbose. + /// + Verbose, + + /// + /// Warning. + /// + Warning, + + /// + /// Error. + /// + Error, + + /// + /// Progress. + /// + Progress, + + /// + /// Object. + /// + Object, + + /// + /// Information. + /// + Information, + } +} diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/CliCommand.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/CliCommand.cs index 4a49169e47..452e36b447 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/CliCommand.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/CliCommand.cs @@ -10,6 +10,7 @@ namespace Microsoft.WinGet.Client.Engine.Commands using Microsoft.WinGet.Client.Engine.Commands.Common; using Microsoft.WinGet.Client.Engine.Common; using Microsoft.WinGet.Client.Engine.Helpers; + using Microsoft.WinGet.Common.Command; /// /// Commands that just calls winget.exe underneath. @@ -55,11 +56,11 @@ public void GetSettings(bool asPlainText) if (asPlainText) { - this.PsCmdlet.WriteObject(result.StdOut); + this.Write(StreamType.Object, result.StdOut); } else { - this.PsCmdlet.WriteObject(Utilities.ConvertToHashtable(result.StdOut)); + this.Write(StreamType.Object, Utilities.ConvertToHashtable(result.StdOut)); } } diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/BaseCommand.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/BaseCommand.cs index ac92b3865e..40ab1d3f25 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/BaseCommand.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/BaseCommand.cs @@ -6,48 +6,23 @@ namespace Microsoft.WinGet.Client.Engine.Commands.Common { + using System.Collections.Generic; using System.Management.Automation; - using Microsoft.WinGet.Client.Engine.Common; - using Microsoft.WinGet.Client.Engine.Exceptions; - using Microsoft.WinGet.SharedLib.Exceptions; + using Microsoft.WinGet.Common.Command; using Microsoft.WinGet.SharedLib.PolicySettings; /// /// Base class for all Cmdlets. /// - public abstract class BaseCommand + public abstract class BaseCommand : PowerShellCmdlet { /// /// Initializes a new instance of the class. /// /// PSCmdlet. internal BaseCommand(PSCmdlet psCmdlet) - : base() + : base(psCmdlet, new HashSet { Policy.WinGet, Policy.WinGetCommandLineInterfaces }) { - // The inproc COM API may deadlock on an STA thread. - if (Utilities.UsesInProcWinget && Utilities.ThreadIsSTA) - { - throw new SingleThreadedApartmentException(); - } - - GroupPolicy groupPolicy = GroupPolicy.GetInstance(); - - if (!groupPolicy.IsEnabled(Policy.WinGet)) - { - throw new GroupPolicyException(Policy.WinGet, GroupPolicyFailureType.BlockedByPolicy); - } - - if (!groupPolicy.IsEnabled(Policy.WinGetCommandLineInterfaces)) - { - throw new GroupPolicyException(Policy.WinGetCommandLineInterfaces, GroupPolicyFailureType.BlockedByPolicy); - } - - this.PsCmdlet = psCmdlet; } - - /// - /// Gets the caller PSCmdlet. - /// - protected PSCmdlet PsCmdlet { get; private set; } } } diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/FinderCommand.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/FinderCommand.cs index 1a82fe6553..c05e4365aa 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/FinderCommand.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/FinderCommand.cs @@ -35,31 +35,33 @@ internal FinderCommand(PSCmdlet psCmdlet) /// Gets or sets the field that is matched against the identifier of a package. /// [Filter(Field = PackageMatchField.Id)] - protected string Id { get; set; } + protected string? Id { get; set; } /// /// Gets or sets the field that is matched against the name of a package. /// [Filter(Field = PackageMatchField.Name)] - protected string Name { get; set; } + protected string? Name { get; set; } /// /// Gets or sets the field that is matched against the moniker of a package. /// [Filter(Field = PackageMatchField.Moniker)] - protected string Moniker { get; set; } + protected string? Moniker { get; set; } /// /// Gets or sets the name of the source to search for packages. If null, then all sources are searched. /// - protected string Source { get; set; } + protected string? Source { get; set; } /// /// Gets or sets how to match against package fields. /// - protected string[] Query { get; set; } +#pragma warning disable SA1011 // Closing square brackets should be spaced correctly + protected string[]? Query { get; set; } +#pragma warning restore SA1011 // Closing square brackets should be spaced correctly - private string QueryAsJoinedString + private string? QueryAsJoinedString { get { @@ -98,7 +100,7 @@ protected IReadOnlyList FindPackages( protected virtual void SetQueryInFindPackagesOptions( ref FindPackagesOptions options, string match, - string value) + string? value) { var selector = ManagementDeploymentFactory.Instance.CreatePackageMatchFilter(); selector.Field = PackageMatchField.CatalogDefault; @@ -111,7 +113,7 @@ private void AddFilterToFindPackagesOptionsIfNotNull( ref FindPackagesOptions options, PackageMatchField field, PackageFieldMatchOption match, - string value) + string? value) { if (value != null) { @@ -187,7 +189,7 @@ private void AddAttributedFiltersToFindPackagesOptions( if (info.GetCustomAttribute(typeof(FilterAttribute), true) is FilterAttribute attribute) { PackageMatchField field = attribute.Field; - string value = info.GetValue(this, null) as string; + string? value = info.GetValue(this, null) as string; this.AddFilterToFindPackagesOptionsIfNotNull(ref options, field, match, value); } } diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/FinderExtendedCommand.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/FinderExtendedCommand.cs index 4b7ee5827d..a10649e067 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/FinderExtendedCommand.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/FinderExtendedCommand.cs @@ -31,13 +31,13 @@ internal FinderExtendedCommand(PSCmdlet psCmdlet) /// Gets or sets the filter that is matched against the tags of the package. /// [Filter(Field = PackageMatchField.Tag)] - protected string Tag { get; set; } + protected string? Tag { get; set; } /// /// Gets or sets the filter that is matched against the commands of the package. /// [Filter(Field = PackageMatchField.Command)] - protected string Command { get; set; } + protected string? Command { get; set; } /// /// Gets or sets the maximum number of results returned. diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/InstallCommand.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/InstallCommand.cs index 028aba9766..f29acd8060 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/InstallCommand.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/InstallCommand.cs @@ -29,17 +29,17 @@ internal InstallCommand(PSCmdlet psCmdlet) /// /// Gets or sets the override arguments to be passed on to the installer. /// - protected string Override { get; set; } + protected string? Override { get; set; } /// /// Gets or sets the arguments to be passed on to the installer in addition to the defaults. /// - protected string Custom { get; set; } + protected string? Custom { get; set; } /// /// Gets or sets the installation location. /// - protected string Location { get; set; } + protected string? Location { get; set; } /// /// Gets or sets a value indicating whether to skip the installer hash validation check. @@ -54,7 +54,7 @@ internal InstallCommand(PSCmdlet psCmdlet) /// /// Gets or sets the optional HTTP Header to pass on to the REST Source. /// - protected string Header { get; set; } + protected string? Header { get; set; } /// /// Gets the install options from the configured parameters. @@ -65,7 +65,7 @@ internal InstallCommand(PSCmdlet psCmdlet) /// The to install. /// Package install mode as string. /// An instance. - protected virtual InstallOptions GetInstallOptions(PackageVersionId version, string mode) + protected virtual InstallOptions GetInstallOptions(PackageVersionId? version, string mode) { InstallOptions options = ManagementDeploymentFactory.Instance.CreateInstallOptions(); options.AllowHashMismatch = this.AllowHashMismatch; @@ -115,10 +115,11 @@ protected InstallResult RegisterCallbacksAndWait( IAsyncOperationWithProgress operation, string activity) { - WriteProgressAdapter adapter = new (this.PsCmdlet); + var activityId = this.GetNewProgressActivityId(); + WriteProgressAdapter adapter = new (this); operation.Progress = (context, progress) => { - ProgressRecord record = new (1, activity, progress.State.ToString()) + ProgressRecord record = new (activityId, activity, progress.State.ToString()) { RecordType = ProgressRecordType.Processing, }; @@ -137,7 +138,7 @@ protected InstallResult RegisterCallbacksAndWait( }; operation.Completed = (context, status) => { - adapter.WriteProgress(new ProgressRecord(1, activity, status.ToString()) + adapter.WriteProgress(new ProgressRecord(activityId, activity, status.ToString()) { RecordType = ProgressRecordType.Completed, }); diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/ManagementDeploymentCommand.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/ManagementDeploymentCommand.cs index fffda73d64..4d39a3a78d 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/ManagementDeploymentCommand.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/ManagementDeploymentCommand.cs @@ -11,9 +11,10 @@ namespace Microsoft.WinGet.Client.Engine.Commands.Common using System.Management.Automation; using System.Runtime.InteropServices; using Microsoft.Management.Deployment; + using Microsoft.WinGet.Client.Engine.Common; using Microsoft.WinGet.Client.Engine.Exceptions; using Microsoft.WinGet.Client.Engine.Helpers; - using Microsoft.WinGet.Client.Engine.Properties; + using Microsoft.WinGet.Resources; /// /// This is the base class for all of the commands in this module that use the COM APIs. @@ -39,13 +40,30 @@ internal ManagementDeploymentCommand(PSCmdlet psCmdlet) #endif } + /// + /// Executes the cmdlet. All cmdlets that uses the COM APIs MUST use this method. + /// The inproc COM API may deadlock on an STA thread. + /// + /// The type of result of the cmdlet. + /// Cmdlet function. + /// The result of the cmdlet. + protected TResult Execute(Func func) + { + if (Utilities.UsesInProcWinget) + { + return this.RunOnMTA(func); + } + + return func(); + } + /// /// Retrieves the specified source or all sources if is null. /// /// A list of instances. /// The name of the source to retrieve. If null, then all sources are returned. /// The source does not exist. - protected IReadOnlyList GetPackageCatalogReferences(string source) + protected IReadOnlyList GetPackageCatalogReferences(string? source) { if (string.IsNullOrEmpty(source)) { @@ -55,8 +73,8 @@ protected IReadOnlyList GetPackageCatalogReferences(str { return new List() { - PackageManagerWrapper.Instance.GetPackageCatalogByName(source) - ?? throw new InvalidSourceException(source), + PackageManagerWrapper.Instance.GetPackageCatalogByName(source!) + ?? throw new InvalidSourceException(source!), }; } } diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/PackageCommand.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/PackageCommand.cs index 31d522835c..d67bad6e0b 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/PackageCommand.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/Common/PackageCommand.cs @@ -36,35 +36,40 @@ internal PackageCommand(PSCmdlet psCmdlet) /// /// Must match the name of the field on the class. /// - protected PSCatalogPackage CatalogPackage { get; set; } = null; + protected PSCatalogPackage? CatalogPackage { get; set; } = null; /// /// Gets or sets the version to install. /// - protected string Version { get; set; } + protected string? Version { get; set; } /// /// Gets or sets the path to the logging file. /// - protected string Log { get; set; } + protected string? Log { get; set; } /// /// Executes a command targeting a specific package version. /// + /// Type of callback's result. /// The value. /// The match option. /// The method to call after retrieving the package and version to operate upon. - protected void GetPackageAndExecute( + /// Result of the callback. + protected TResult? GetPackageAndExecute( CompositeSearchBehavior behavior, PackageFieldMatchOption match, - Action callback) + Func callback) + where TResult : class { CatalogPackage package = this.GetCatalogPackage(behavior, match); - PackageVersionId version = this.GetPackageVersionId(package); - if (this.PsCmdlet.ShouldProcess(package.ToString(version))) + PackageVersionId? version = this.GetPackageVersionId(package); + if (this.ShouldProcess(package.ToString(version))) { - callback(package, version); + return callback(package, version); } + + return null; } /// @@ -79,7 +84,7 @@ protected void GetPackageAndExecute( protected override void SetQueryInFindPackagesOptions( ref FindPackagesOptions options, string match, - string value) + string? value) { var matchOption = PSEnumHelpers.ToPackageFieldMatchOption(match); foreach (PackageMatchField field in new PackageMatchField[] { PackageMatchField.Id, PackageMatchField.Name, PackageMatchField.Moniker }) @@ -120,7 +125,7 @@ private CatalogPackage GetCatalogPackage(CompositeSearchBehavior behavior, Packa } } - private PackageVersionId GetPackageVersionId(CatalogPackage package) + private PackageVersionId? GetPackageVersionId(CatalogPackage package) { if (this.Version != null) { diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/FinderPackageCommand.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/FinderPackageCommand.cs index ef94f5b6b1..fdfa06ce3e 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/FinderPackageCommand.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/FinderPackageCommand.cs @@ -11,6 +11,7 @@ namespace Microsoft.WinGet.Client.Engine.Commands using Microsoft.WinGet.Client.Engine.Commands.Common; using Microsoft.WinGet.Client.Engine.Helpers; using Microsoft.WinGet.Client.Engine.PSObjects; + using Microsoft.WinGet.Common.Command; /// /// Searches configured sources for packages. @@ -60,10 +61,14 @@ public FinderPackageCommand( /// PSPackageFieldMatchOption. public void Find(string psPackageFieldMatchOption) { - var results = this.FindPackages(CompositeSearchBehavior.RemotePackagesFromRemoteCatalogs, PSEnumHelpers.ToPackageFieldMatchOption(psPackageFieldMatchOption)); + var results = this.Execute( + () => this.FindPackages( + CompositeSearchBehavior.RemotePackagesFromRemoteCatalogs, + PSEnumHelpers.ToPackageFieldMatchOption(psPackageFieldMatchOption))); + for (var i = 0; i < results.Count; i++) { - this.PsCmdlet.WriteObject(new PSFoundCatalogPackage(results[i].CatalogPackage)); + this.Write(StreamType.Object, new PSFoundCatalogPackage(results[i].CatalogPackage)); } } @@ -73,10 +78,13 @@ public void Find(string psPackageFieldMatchOption) /// PSPackageFieldMatchOption. public void Get(string psPackageFieldMatchOption) { - var results = this.FindPackages(CompositeSearchBehavior.LocalCatalogs, PSEnumHelpers.ToPackageFieldMatchOption(psPackageFieldMatchOption)); + var results = this.Execute( + () => this.FindPackages( + CompositeSearchBehavior.LocalCatalogs, + PSEnumHelpers.ToPackageFieldMatchOption(psPackageFieldMatchOption))); for (var i = 0; i < results.Count; i++) { - this.PsCmdlet.WriteObject(new PSInstalledCatalogPackage(results[i].CatalogPackage)); + this.Write(StreamType.Object, new PSInstalledCatalogPackage(results[i].CatalogPackage)); } } } diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/InstallerPackageCommand.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/InstallerPackageCommand.cs index ef2bf88561..2502b8ada1 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/InstallerPackageCommand.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/InstallerPackageCommand.cs @@ -10,8 +10,9 @@ namespace Microsoft.WinGet.Client.Engine.Commands using Microsoft.Management.Deployment; using Microsoft.WinGet.Client.Engine.Commands.Common; using Microsoft.WinGet.Client.Engine.Helpers; - using Microsoft.WinGet.Client.Engine.Properties; using Microsoft.WinGet.Client.Engine.PSObjects; + using Microsoft.WinGet.Common.Command; + using Microsoft.WinGet.Resources; /// /// Installs or updates a package from the pipeline or from a configured source. @@ -94,23 +95,28 @@ public void Install( string psPackageFieldMatchOption, string psPackageInstallMode) { - this.GetPackageAndExecute( - CompositeSearchBehavior.RemotePackagesFromRemoteCatalogs, - PSEnumHelpers.ToPackageFieldMatchOption(psPackageFieldMatchOption), - (package, version) => - { - InstallOptions options = this.GetInstallOptions(version, psPackageInstallMode); - if (psProcessorArchitecture != "Default") + var result = this.Execute( + () => this.GetPackageAndExecute( + CompositeSearchBehavior.RemotePackagesFromRemoteCatalogs, + PSEnumHelpers.ToPackageFieldMatchOption(psPackageFieldMatchOption), + (package, version) => { - options.AllowedArchitectures.Clear(); - options.AllowedArchitectures.Add(PSEnumHelpers.ToProcessorArchitecture(psProcessorArchitecture)); - } + InstallOptions options = this.GetInstallOptions(version, psPackageInstallMode); + if (psProcessorArchitecture != "Default") + { + options.AllowedArchitectures.Clear(); + options.AllowedArchitectures.Add(PSEnumHelpers.ToProcessorArchitecture(psProcessorArchitecture)); + } - options.PackageInstallScope = PSEnumHelpers.ToPackageInstallScope(psPackageInstallScope); + options.PackageInstallScope = PSEnumHelpers.ToPackageInstallScope(psPackageInstallScope); - InstallResult result = this.InstallPackage(package, options); - this.PsCmdlet.WriteObject(new PSInstallResult(result)); - }); + return this.InstallPackage(package, options); + })); + + if (result != null) + { + this.Write(StreamType.Object, new PSInstallResult(result)); + } } /// @@ -124,17 +130,21 @@ public void Update( string psPackageFieldMatchOption, string psPackageInstallMode) { - this.GetPackageAndExecute( - CompositeSearchBehavior.LocalCatalogs, - PSEnumHelpers.ToPackageFieldMatchOption(psPackageFieldMatchOption), - (package, version) => - { - InstallOptions options = this.GetInstallOptions(version, psPackageInstallMode); - options.AllowUpgradeToUnknownVersion = includeUnknown; + var result = this.Execute( + () => this.GetPackageAndExecute( + CompositeSearchBehavior.LocalCatalogs, + PSEnumHelpers.ToPackageFieldMatchOption(psPackageFieldMatchOption), + (package, version) => + { + InstallOptions options = this.GetInstallOptions(version, psPackageInstallMode); + options.AllowUpgradeToUnknownVersion = includeUnknown; + return this.UpgradePackage(package, options); + })); - InstallResult result = this.UpgradePackage(package, options); - this.PsCmdlet.WriteObject(new PSInstallResult(result)); - }); + if (result != null) + { + this.Write(StreamType.Object, new PSInstallResult(result)); + } } private InstallResult InstallPackage( diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/SourceCommand.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/SourceCommand.cs index 0b6e10947d..78c6559bca 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/SourceCommand.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/SourceCommand.cs @@ -9,6 +9,7 @@ namespace Microsoft.WinGet.Client.Engine.Commands using System.Management.Automation; using Microsoft.WinGet.Client.Engine.Commands.Common; using Microsoft.WinGet.Client.Engine.PSObjects; + using Microsoft.WinGet.Common.Command; /// /// Wrapper for source cmdlets. @@ -31,10 +32,11 @@ public SourceCommand(PSCmdlet psCmdlet) /// Optional name. public void Get(string name) { - var results = this.GetPackageCatalogReferences(name); + var results = this.Execute( + () => this.GetPackageCatalogReferences(name)); for (var i = 0; i < results.Count; i++) { - this.PsCmdlet.WriteObject(new PSSourceResult(results[i])); + this.Write(StreamType.Object, new PSSourceResult(results[i])); } } } diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/UninstallPackageCommand.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/UninstallPackageCommand.cs index 5cd291231e..14fa8919c3 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/UninstallPackageCommand.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/UninstallPackageCommand.cs @@ -11,8 +11,9 @@ namespace Microsoft.WinGet.Client.Engine.Commands using Microsoft.Management.Deployment; using Microsoft.WinGet.Client.Engine.Commands.Common; using Microsoft.WinGet.Client.Engine.Helpers; - using Microsoft.WinGet.Client.Engine.Properties; using Microsoft.WinGet.Client.Engine.PSObjects; + using Microsoft.WinGet.Common.Command; + using Microsoft.WinGet.Resources; /// /// Uninstalls a package from the local system. @@ -71,19 +72,24 @@ public void Uninstall( string psPackageFieldMatchOption, bool force) { - this.GetPackageAndExecute( - CompositeSearchBehavior.LocalCatalogs, - PSEnumHelpers.ToPackageFieldMatchOption(psPackageFieldMatchOption), - (package, version) => - { - UninstallOptions options = this.GetUninstallOptions(version, PSEnumHelpers.ToPackageUninstallMode(psPackageUninstallMode), force); - UninstallResult result = this.UninstallPackage(package, options); - this.PsCmdlet.WriteObject(new PSUninstallResult(result)); - }); + var result = this.Execute( + () => this.GetPackageAndExecute( + CompositeSearchBehavior.LocalCatalogs, + PSEnumHelpers.ToPackageFieldMatchOption(psPackageFieldMatchOption), + (package, version) => + { + UninstallOptions options = this.GetUninstallOptions(version, PSEnumHelpers.ToPackageUninstallMode(psPackageUninstallMode), force); + return this.UninstallPackage(package, options); + })); + + if (result != null) + { + this.Write(StreamType.Object, new PSUninstallResult(result)); + } } private UninstallOptions GetUninstallOptions( - PackageVersionId version, + PackageVersionId? version, PackageUninstallMode packageUninstallMode, bool force) { @@ -113,17 +119,19 @@ private UninstallResult UninstallPackage( package.Name); var operation = PackageManagerWrapper.Instance.UninstallPackageAsync(package, options); - WriteProgressAdapter adapter = new (this.PsCmdlet); + + var activityId = this.GetNewProgressActivityId(); + WriteProgressAdapter adapter = new (this); operation.Progress = (context, progress) => { - adapter.WriteProgress(new ProgressRecord(1, activity, progress.State.ToString()) + adapter.WriteProgress(new ProgressRecord(activityId, activity, progress.State.ToString()) { RecordType = ProgressRecordType.Processing, }); }; operation.Completed = (context, status) => { - adapter.WriteProgress(new ProgressRecord(1, activity, status.ToString()) + adapter.WriteProgress(new ProgressRecord(activityId, activity, status.ToString()) { RecordType = ProgressRecordType.Completed, }); diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/UserSettingsCommand.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/UserSettingsCommand.cs index a485de4774..8c8f1a78c1 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/UserSettingsCommand.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/UserSettingsCommand.cs @@ -16,6 +16,7 @@ namespace Microsoft.WinGet.Client.Engine.Commands using Microsoft.WinGet.Client.Engine.Common; using Microsoft.WinGet.Client.Engine.Exceptions; using Microsoft.WinGet.Client.Engine.Helpers; + using Microsoft.WinGet.Common.Command; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -27,16 +28,7 @@ public sealed class UserSettingsCommand : BaseCommand private const string SchemaKey = "$schema"; private const string SchemaValue = "https://aka.ms/winget-settings.schema.json"; - private static readonly string WinGetSettingsFilePath; - - static UserSettingsCommand() - { - var wingetCliWrapper = new WingetCLIWrapper(); - var settingsResult = wingetCliWrapper.RunCommand("settings", "export"); - - // Read the user settings file property. - WinGetSettingsFilePath = (string)Utilities.ConvertToHashtable(settingsResult.StdOut)["userSettingsFile"]; - } + private static string? winGetSettingsFilePath; /// /// Initializes a new instance of the class. @@ -45,6 +37,18 @@ static UserSettingsCommand() public UserSettingsCommand(PSCmdlet psCmdlet) : base(psCmdlet) { + // Doing it in the static constructor will show the user running in system context: + // The type initializer for 'Microsoft.WinGet.Client.Engine.Commands.UserSettingsCommand' threw an exception. + // Here would be "The specified method is not supported." + if (winGetSettingsFilePath == null) + { + var wingetCliWrapper = new WingetCLIWrapper(); + var settingsResult = wingetCliWrapper.RunCommand("settings", "export"); + + // Read the user settings file property. + var userSettingsFile = Utilities.ConvertToHashtable(settingsResult.StdOut)["userSettingsFile"] ?? throw new ArgumentNullException("userSettingsFile"); + winGetSettingsFilePath = (string)userSettingsFile; + } } /// @@ -52,7 +56,7 @@ public UserSettingsCommand(PSCmdlet psCmdlet) /// public void Get() { - this.PsCmdlet.WriteObject(this.GetLocalSettingsAsHashtable()); + this.Write(StreamType.Object, this.GetLocalSettingsAsHashtable()); } /// @@ -62,7 +66,7 @@ public void Get() /// Ignore comparing settings that are not part of the input. public void Test(Hashtable userSettings, bool ignoreNotSet) { - this.PsCmdlet.WriteObject(this.CompareUserSettings(userSettings, ignoreNotSet)); + this.Write(StreamType.Object, this.CompareUserSettings(userSettings, ignoreNotSet)); } /// @@ -100,10 +104,10 @@ public void Set(Hashtable userSettings, bool merge) // Write settings. var settingsJson = orderedSettings.ToString(Formatting.Indented); File.WriteAllText( - WinGetSettingsFilePath, + winGetSettingsFilePath!, settingsJson); - this.PsCmdlet.WriteObject(Utilities.ConvertToHashtable(settingsJson)); + this.Write(StreamType.Object, Utilities.ConvertToHashtable(settingsJson)); } private static JObject HashtableToJObject(Hashtable hashtable) @@ -113,8 +117,8 @@ private static JObject HashtableToJObject(Hashtable hashtable) private Hashtable GetLocalSettingsAsHashtable() { - var content = File.Exists(WinGetSettingsFilePath) ? - File.ReadAllText(WinGetSettingsFilePath) : + var content = File.Exists(winGetSettingsFilePath) ? + File.ReadAllText(winGetSettingsFilePath) : string.Empty; return Utilities.ConvertToHashtable(content); @@ -124,13 +128,13 @@ private JObject LocalSettingsFileToJObject() { try { - return File.Exists(WinGetSettingsFilePath) ? - JObject.Parse(File.ReadAllText(WinGetSettingsFilePath)) : + return File.Exists(winGetSettingsFilePath) ? + JObject.Parse(File.ReadAllText(winGetSettingsFilePath)) : new JObject(); } catch (JsonReaderException e) { - this.PsCmdlet.WriteDebug(e.Message); + this.Write(StreamType.Verbose, e.Message); throw new UserSettingsReadException(e); } } @@ -162,7 +166,7 @@ private bool CompareUserSettings(Hashtable userSettings, bool ignoreNotSet) } catch (Exception e) { - this.PsCmdlet.WriteDebug(e.Message); + this.Write(StreamType.Verbose, e.Message); return false; } } @@ -175,27 +179,34 @@ private bool CompareUserSettings(Hashtable userSettings, bool ignoreNotSet) /// Main json. /// otherJson. /// True is otherJson partially contains json. - private bool PartialDeepEquals(JToken json, JToken otherJson) + private bool PartialDeepEquals(JToken json, JToken? otherJson) { if (JToken.DeepEquals(json, otherJson)) { return true; } + if (otherJson == null) + { + return false; + } + // If they are a JValue (string, integer, date, etc) or they are a JArray and DeepEquals fails then not equal. if ((json is JValue && otherJson is JValue) || (json is JArray && otherJson is JArray)) { - this.PsCmdlet.WriteDebug($"'{json.ToString(Newtonsoft.Json.Formatting.None)}' != " + - $"'{otherJson.ToString(Newtonsoft.Json.Formatting.None)}'"); + this.Write( + StreamType.Verbose, + $"'{json.ToString(Formatting.None)}' != '{otherJson.ToString(Formatting.None)}'"); return false; } // If its not the same type then don't bother. if (json.Type != otherJson.Type) { - this.PsCmdlet.WriteDebug($"Mismatch types '{json.ToString(Newtonsoft.Json.Formatting.None)}' " + - $"'{otherJson.ToString(Newtonsoft.Json.Formatting.None)}'"); + this.Write( + StreamType.Verbose, + $"Mismatch types '{json.ToString(Formatting.None)}' '{otherJson.ToString(Formatting.None)}'"); return false; } @@ -211,7 +222,7 @@ private bool PartialDeepEquals(JToken json, JToken otherJson) // If the property is not there then give up. if (!otherJObject.ContainsKey(property.Name)) { - this.PsCmdlet.WriteDebug($"{property.Name} not found."); + this.Write(StreamType.Verbose, $"{property.Name} not found."); return false; } diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/VersionCommand.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/VersionCommand.cs index b4b7f595d5..393ee39ee2 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/VersionCommand.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/VersionCommand.cs @@ -9,6 +9,7 @@ namespace Microsoft.WinGet.Client.Engine.Commands using System.Management.Automation; using Microsoft.WinGet.Client.Engine.Commands.Common; using Microsoft.WinGet.Client.Engine.Helpers; + using Microsoft.WinGet.Common.Command; /// /// Version commands. @@ -29,7 +30,7 @@ public VersionCommand(PSCmdlet psCmdlet) /// public void Get() { - this.PsCmdlet.WriteObject(WinGetVersion.InstalledWinGetVersion.TagVersion); + this.Write(StreamType.Object, WinGetVersion.InstalledWinGetVersion.TagVersion); } } } diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/WinGetPackageManagerCommand.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/WinGetPackageManagerCommand.cs index a6d534449d..60fe311905 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/WinGetPackageManagerCommand.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Commands/WinGetPackageManagerCommand.cs @@ -13,7 +13,8 @@ namespace Microsoft.WinGet.Client.Engine.Commands using Microsoft.WinGet.Client.Engine.Common; using Microsoft.WinGet.Client.Engine.Exceptions; using Microsoft.WinGet.Client.Engine.Helpers; - using Microsoft.WinGet.Client.Engine.Properties; + using Microsoft.WinGet.Common.Command; + using Microsoft.WinGet.Resources; using static Microsoft.WinGet.Client.Engine.Common.Constants; /// @@ -49,7 +50,7 @@ public void AssertUsingLatest(bool preRelease) /// The expected version. public void Assert(string expectedVersion) { - WinGetIntegrity.AssertWinGet(this.PsCmdlet, expectedVersion); + WinGetIntegrity.AssertWinGet(this, expectedVersion); } /// @@ -96,8 +97,8 @@ private void RepairStateMachine(string expectedVersion, bool allUsers) { try { - WinGetIntegrity.AssertWinGet(this.PsCmdlet, expectedVersion); - this.PsCmdlet.WriteDebug($"WinGet is in a good state."); + WinGetIntegrity.AssertWinGet(this, expectedVersion); + this.Write(StreamType.Verbose, $"WinGet is in a good state."); currentCategory = IntegrityCategory.Installed; } catch (WinGetIntegrityException e) @@ -106,11 +107,11 @@ private void RepairStateMachine(string expectedVersion, bool allUsers) if (seenCategories.Contains(currentCategory)) { - this.PsCmdlet.WriteDebug($"{currentCategory} encountered previously"); + this.Write(StreamType.Verbose, $"{currentCategory} encountered previously"); throw; } - this.PsCmdlet.WriteDebug($"Integrity category type: {currentCategory}"); + this.Write(StreamType.Verbose, $"Integrity category type: {currentCategory}"); seenCategories.Add(currentCategory); switch (currentCategory) @@ -156,10 +157,13 @@ private void InstallDifferentVersion(WinGetVersion toInstallVersion, bool allUse var installedVersion = WinGetVersion.InstalledWinGetVersion; bool isDowngrade = installedVersion.CompareAsDeployment(toInstallVersion) > 0; - this.PsCmdlet.WriteDebug($"Installed WinGet version '{installedVersion.TagVersion}' " + + string message = $"Installed WinGet version '{installedVersion.TagVersion}' " + $"Installing WinGet version '{toInstallVersion.TagVersion}' " + - $"Is downgrade {isDowngrade}"); - var appxModule = new AppxModuleHelper(this.PsCmdlet); + $"Is downgrade {isDowngrade}"; + this.Write( + StreamType.Verbose, + message); + var appxModule = new AppxModuleHelper(this); appxModule.InstallFromGitHubRelease(toInstallVersion.TagVersion, allUsers, isDowngrade); } @@ -174,13 +178,13 @@ private void Install(string toInstallVersion, bool allUsers) toInstallVersion = gitHubClient.GetLatestVersionTagName(false); } - var appxModule = new AppxModuleHelper(this.PsCmdlet); + var appxModule = new AppxModuleHelper(this); appxModule.InstallFromGitHubRelease(toInstallVersion, allUsers, false); } private void Register() { - var appxModule = new AppxModuleHelper(this.PsCmdlet); + var appxModule = new AppxModuleHelper(this); appxModule.RegisterAppInstaller(); } @@ -190,12 +194,12 @@ private void RepairEnvPath() Utilities.AddWindowsAppToPath(); // Update this sessions PowerShell environment so the user doesn't have to restart the terminal. - string envPathUser = Environment.GetEnvironmentVariable(Constants.PathEnvVar, EnvironmentVariableTarget.User); - string envPathMachine = Environment.GetEnvironmentVariable(Constants.PathEnvVar, EnvironmentVariableTarget.Machine); + string? envPathUser = Environment.GetEnvironmentVariable(Constants.PathEnvVar, EnvironmentVariableTarget.User); + string? envPathMachine = Environment.GetEnvironmentVariable(Constants.PathEnvVar, EnvironmentVariableTarget.Machine); string newPwshPathEnv = $"{envPathMachine};{envPathUser}"; - this.PsCmdlet.SessionState.PSVariable.Set(EnvPath, newPwshPathEnv); + this.SetVariable(EnvPath, newPwshPathEnv); - this.PsCmdlet.WriteDebug($"PATH environment variable updated"); + this.Write(StreamType.Verbose, $"PATH environment variable updated"); } } } diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Common/Utilities.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Common/Utilities.cs index 38fee22f34..05b205ef5a 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Common/Utilities.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Common/Utilities.cs @@ -13,7 +13,7 @@ namespace Microsoft.WinGet.Client.Engine.Common using System.Management.Automation; using System.Security.Principal; using System.Threading; - using Microsoft.WinGet.Client.Engine.Properties; + using Microsoft.WinGet.Resources; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -110,7 +110,7 @@ public static void VerifyAdmin() public static void AddWindowsAppToPath() { var scope = EnvironmentVariableTarget.User; - string envPathValue = Environment.GetEnvironmentVariable(Constants.PathEnvVar, scope); + string? envPathValue = Environment.GetEnvironmentVariable(Constants.PathEnvVar, scope); if (string.IsNullOrEmpty(envPathValue) || !envPathValue.Contains(Utilities.LocalDataWindowsAppPath)) { Environment.SetEnvironmentVariable( @@ -185,9 +185,9 @@ private static Hashtable PopulateHashTableFromJDictionary(JObject entries) return result; } - private static ICollection PopulateHashTableFromJArray(JArray list) + private static ICollection PopulateHashTableFromJArray(JArray list) { - var result = new object[list.Count]; + var result = new object?[list.Count]; for (var index = 0; index < list.Count; index++) { diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Common/WinGetIntegrity.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Common/WinGetIntegrity.cs index 62df3098a3..62d5714fb0 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Common/WinGetIntegrity.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Common/WinGetIntegrity.cs @@ -12,7 +12,8 @@ namespace Microsoft.WinGet.Client.Engine.Common using System.Management.Automation; using Microsoft.WinGet.Client.Engine.Exceptions; using Microsoft.WinGet.Client.Engine.Helpers; - using Microsoft.WinGet.Client.Engine.Properties; + using Microsoft.WinGet.Common.Command; + using Microsoft.WinGet.Resources; /// /// Validates winget runs correctly. @@ -22,9 +23,9 @@ internal static class WinGetIntegrity /// /// Verifies winget runs correctly. If it doesn't, tries to find the reason why it failed. /// - /// The calling cmdlet. + /// The calling cmdlet. /// Expected version. - public static void AssertWinGet(PSCmdlet psCmdlet, string expectedVersion) + public static void AssertWinGet(PowerShellCmdlet pwshCmdlet, string expectedVersion) { // In-proc shouldn't have other dependencies and thus should be ok. if (Utilities.UsesInProcWinget) @@ -48,17 +49,17 @@ public static void AssertWinGet(PSCmdlet psCmdlet, string expectedVersion) } catch (Win32Exception e) { - psCmdlet.WriteDebug($"'winget.exe' Win32Exception {e.Message}"); - throw new WinGetIntegrityException(GetReason(psCmdlet)); + pwshCmdlet.Write(StreamType.Verbose, $"'winget.exe' Win32Exception {e.Message}"); + throw new WinGetIntegrityException(GetReason(pwshCmdlet)); } catch (Exception e) when (e is WinGetCLIException || e is WinGetCLITimeoutException) { - psCmdlet.WriteDebug($"'winget.exe' WinGetCLIException {e.Message}"); + pwshCmdlet.Write(StreamType.Verbose, $"'winget.exe' WinGetCLIException {e.Message}"); throw new WinGetIntegrityException(IntegrityCategory.Failure, e); } catch (Exception e) { - psCmdlet.WriteDebug($"'winget.exe' Exception {e.Message}"); + pwshCmdlet.Write(StreamType.Verbose, $"'winget.exe' Exception {e.Message}"); throw new WinGetIntegrityException(IntegrityCategory.Unknown, e); } @@ -80,7 +81,7 @@ public static void AssertWinGet(PSCmdlet psCmdlet, string expectedVersion) } } - private static IntegrityCategory GetReason(PSCmdlet psCmdlet) + private static IntegrityCategory GetReason(PowerShellCmdlet pwshCmdlet) { // Ok, so you are here because calling winget --version failed. Lets try to figure out why. @@ -96,7 +97,7 @@ private static IntegrityCategory GetReason(PSCmdlet psCmdlet) } catch (ApplicationFailedException e) { - psCmdlet.WriteDebug(e.Message); + pwshCmdlet.Write(StreamType.Verbose, e.Message); return IntegrityCategory.AppInstallerNoLicense; } catch (Exception) @@ -112,7 +113,7 @@ private static IntegrityCategory GetReason(PSCmdlet psCmdlet) if (File.Exists(wingetAliasPath)) { // App execution alias is enabled. Then maybe the path? - string envPath = Environment.GetEnvironmentVariable(Constants.PathEnvVar, EnvironmentVariableTarget.User); + string? envPath = Environment.GetEnvironmentVariable(Constants.PathEnvVar, EnvironmentVariableTarget.User); if (string.IsNullOrEmpty(envPath) || !envPath.EndsWith(Utilities.LocalDataWindowsAppPath) || !envPath.Contains($"{Utilities.LocalDataWindowsAppPath};")) @@ -136,8 +137,8 @@ private static IntegrityCategory GetReason(PSCmdlet psCmdlet) // It could be that AppInstaller package is old or the package is not // registered at this point. To know that, call Get-AppxPackage. - var appxModule = new AppxModuleHelper(psCmdlet); - string version = appxModule.GetAppInstallerPropertyValue("Version"); + var appxModule = new AppxModuleHelper(pwshCmdlet); + string? version = appxModule.GetAppInstallerPropertyValue("Version"); if (version is null) { // This can happen in Windows Sandbox. diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/CatalogConnectException.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/CatalogConnectException.cs index 5fa035e2cc..bd3003f60e 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/CatalogConnectException.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/CatalogConnectException.cs @@ -8,7 +8,7 @@ namespace Microsoft.WinGet.Client.Engine.Exceptions { using System; using System.Management.Automation; - using Microsoft.WinGet.Client.Engine.Properties; + using Microsoft.WinGet.Resources; /// /// Failed connecting to catalog. diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/FindPackagesException.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/FindPackagesException.cs index 80194e0365..fe7fdd1df8 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/FindPackagesException.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/FindPackagesException.cs @@ -9,7 +9,7 @@ namespace Microsoft.WinGet.Client.Engine.Exceptions using System; using System.Management.Automation; using Microsoft.Management.Deployment; - using Microsoft.WinGet.Client.Engine.Properties; + using Microsoft.WinGet.Resources; /// /// Raised when there is an error searching for packages. diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/InvalidSourceException.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/InvalidSourceException.cs index 3e1944ad36..ca2680be6f 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/InvalidSourceException.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/InvalidSourceException.cs @@ -7,7 +7,7 @@ namespace Microsoft.WinGet.Client.Engine.Exceptions { using System; - using Microsoft.WinGet.Client.Engine.Properties; + using Microsoft.WinGet.Resources; /// /// Invalid source. diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/InvalidVersionException.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/InvalidVersionException.cs index 70577e65ff..0dc69ae10f 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/InvalidVersionException.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/InvalidVersionException.cs @@ -7,7 +7,7 @@ namespace Microsoft.WinGet.Client.Engine.Exceptions { using System; - using Microsoft.WinGet.Client.Engine.Properties; + using Microsoft.WinGet.Resources; /// /// Invalid version. diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/NoPackageFoundException.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/NoPackageFoundException.cs index 4d1651b2f5..835356cf58 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/NoPackageFoundException.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/NoPackageFoundException.cs @@ -8,7 +8,7 @@ namespace Microsoft.WinGet.Client.Engine.Exceptions { using System; using System.Management.Automation; - using Microsoft.WinGet.Client.Engine.Properties; + using Microsoft.WinGet.Resources; /// /// No package found. diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/SingleThreadedApartmentException.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/SingleThreadedApartmentException.cs index 976902bb85..eeb6f594b4 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/SingleThreadedApartmentException.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/SingleThreadedApartmentException.cs @@ -8,7 +8,7 @@ namespace Microsoft.WinGet.Client.Engine.Exceptions { using System; using System.Management.Automation; - using Microsoft.WinGet.Client.Engine.Properties; + using Microsoft.WinGet.Resources; /// /// No package found. diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/UserSettingsReadException.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/UserSettingsReadException.cs index 8a43c6014b..1bfb691798 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/UserSettingsReadException.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/UserSettingsReadException.cs @@ -8,7 +8,7 @@ namespace Microsoft.WinGet.Client.Engine.Exceptions { using System; using System.Management.Automation; - using Microsoft.WinGet.Client.Engine.Properties; + using Microsoft.WinGet.Resources; /// /// Settings.json file is invalid. diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/VagueCriteriaException.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/VagueCriteriaException.cs index 6dbd9c8006..2a754d3deb 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/VagueCriteriaException.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/VagueCriteriaException.cs @@ -11,7 +11,7 @@ namespace Microsoft.WinGet.Client.Engine.Exceptions using System.Management.Automation; using Microsoft.Management.Deployment; using Microsoft.WinGet.Client.Engine.Extensions; - using Microsoft.WinGet.Client.Engine.Properties; + using Microsoft.WinGet.Resources; /// /// Raised when search criteria for installing or updating a package is too vague. diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/WinGetCLIException.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/WinGetCLIException.cs index 4d86b570a6..b00b8f40b1 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/WinGetCLIException.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/WinGetCLIException.cs @@ -7,7 +7,7 @@ namespace Microsoft.WinGet.Client.Engine.Exceptions { using System.Management.Automation; - using Microsoft.WinGet.Client.Engine.Properties; + using Microsoft.WinGet.Resources; /// /// WinGet cli exception. @@ -22,7 +22,7 @@ public class WinGetCLIException : RuntimeException /// Exit code. /// Standard output. /// Standard error. - public WinGetCLIException(string command, string parameters, int exitCode, string stdOut, string stdErr) + public WinGetCLIException(string command, string? parameters, int exitCode, string stdOut, string stdErr) : base(string.Format(Resources.WinGetCLIExceptionMessage, command, parameters, exitCode)) { this.Command = command; @@ -40,7 +40,7 @@ public WinGetCLIException(string command, string parameters, int exitCode, strin /// /// Gets the parameters. /// - public string Parameters { get; private set; } + public string? Parameters { get; private set; } /// /// Gets the exit code. diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/WinGetCLITimeoutException.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/WinGetCLITimeoutException.cs index 18bb5c5ab8..38f36dff0d 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/WinGetCLITimeoutException.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/WinGetCLITimeoutException.cs @@ -7,7 +7,7 @@ namespace Microsoft.WinGet.Client.Engine.Exceptions { using System; - using Microsoft.WinGet.Client.Engine.Properties; + using Microsoft.WinGet.Resources; /// /// Time out exception for a winget cli command. @@ -19,7 +19,7 @@ public class WinGetCLITimeoutException : TimeoutException /// /// Command. /// Parameters. - public WinGetCLITimeoutException(string command, string parameters) + public WinGetCLITimeoutException(string command, string? parameters) : base(string.Format(Resources.WinGetCLITimeoutExceptionMessage, command, parameters)) { } diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/WinGetIntegrityException.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/WinGetIntegrityException.cs index fe716d172d..63a7939038 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/WinGetIntegrityException.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/WinGetIntegrityException.cs @@ -9,7 +9,7 @@ namespace Microsoft.WinGet.Client.Engine.Exceptions using System; using System.Management.Automation; using Microsoft.WinGet.Client.Engine.Common; - using Microsoft.WinGet.Client.Engine.Properties; + using Microsoft.WinGet.Resources; /// /// WinGet Integrity exception. diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/WinGetRepairException.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/WinGetRepairException.cs index 3334ca8d63..b3513b0b81 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/WinGetRepairException.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/WinGetRepairException.cs @@ -9,7 +9,7 @@ namespace Microsoft.WinGet.Client.Engine.Exceptions using System; using System.Management.Automation; using Microsoft.WinGet.Client.Engine.Common; - using Microsoft.WinGet.Client.Engine.Properties; + using Microsoft.WinGet.Resources; /// /// WinGet repair exception. diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/WindowsPowerShellNotSupported.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/WindowsPowerShellNotSupported.cs index 5c4f8a6c84..ca5d3a538a 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/WindowsPowerShellNotSupported.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Exceptions/WindowsPowerShellNotSupported.cs @@ -8,7 +8,7 @@ namespace Microsoft.WinGet.Client.Engine.Exceptions { using System; using System.Management.Automation; - using Microsoft.WinGet.Client.Engine.Properties; + using Microsoft.WinGet.Resources; /// /// Windows PowerShell is not supported. diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Extensions/CatalogPackageExtensions.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Extensions/CatalogPackageExtensions.cs index 18b7da3841..19c5df2a88 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Extensions/CatalogPackageExtensions.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Extensions/CatalogPackageExtensions.cs @@ -21,7 +21,7 @@ internal static class CatalogPackageExtensions /// A instance. public static string ToString( this CatalogPackage package, - PackageVersionId version) + PackageVersionId? version) { if ((version != null) || (package.AvailableVersions.Count > 0)) { diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/AppxModuleHelper.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/AppxModuleHelper.cs index 403c1e9d14..5273d97631 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/AppxModuleHelper.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/AppxModuleHelper.cs @@ -13,6 +13,7 @@ namespace Microsoft.WinGet.Client.Engine.Helpers using System.Management.Automation; using System.Runtime.InteropServices; using Microsoft.WinGet.Client.Engine.Common; + using Microsoft.WinGet.Common.Command; using static Microsoft.WinGet.Client.Engine.Common.Constants; /// @@ -71,22 +72,22 @@ internal class AppxModuleHelper private const string XamlAssetArm = "Microsoft.UI.Xaml.2.7.arm.appx"; private const string XamlAssetArm64 = "Microsoft.UI.Xaml.2.7.arm64.appx"; - private readonly PSCmdlet psCmdlet; + private readonly PowerShellCmdlet pwshCmdlet; /// /// Initializes a new instance of the class. /// - /// The calling cmdlet. - public AppxModuleHelper(PSCmdlet psCmdlet) + /// The calling cmdlet. + public AppxModuleHelper(PowerShellCmdlet pwshCmdlet) { - this.psCmdlet = psCmdlet; + this.pwshCmdlet = pwshCmdlet; } /// /// Calls Get-AppxPackage Microsoft.DesktopAppInstaller. /// /// Result of Get-AppxPackage. - public PSObject GetAppInstallerObject() + public PSObject? GetAppInstallerObject() { return this.GetAppxObject(AppInstallerName); } @@ -96,9 +97,9 @@ public PSObject GetAppInstallerObject() /// /// Property name. /// Value, null if doesn't exist. - public string GetAppInstallerPropertyValue(string propertyName) + public string? GetAppInstallerPropertyValue(string propertyName) { - string result = null; + string? result = null; var packageObj = this.GetAppInstallerObject(); if (packageObj is not null) { @@ -117,7 +118,13 @@ public string GetAppInstallerPropertyValue(string propertyName) /// public void RegisterAppInstaller() { - string packageFullName = this.GetAppInstallerPropertyValue(PackageFullName); + string? packageFullName = this.GetAppInstallerPropertyValue(PackageFullName); + + if (packageFullName == null) + { + throw new ArgumentNullException(PackageFullName); + } + string appxManifestPath = System.IO.Path.Combine( Utilities.ProgramFilesWindowsAppPath, packageFullName, @@ -194,7 +201,7 @@ private void AddProvisionPackage(string releaseTag) } catch (RuntimeException e) { - this.psCmdlet.WriteDebug($"Failed installing bundle via Add-AppxProvisionedPackage {e}"); + this.pwshCmdlet.Write(StreamType.Verbose, $"Failed installing bundle via Add-AppxProvisionedPackage {e}"); throw e; } } @@ -218,12 +225,12 @@ private void AddAppInstallerBundle(string releaseTag, bool downgrade) } catch (RuntimeException e) { - this.psCmdlet.WriteDebug($"Failed installing bundle via Add-AppxPackage {e}"); + this.pwshCmdlet.Write(StreamType.Verbose, $"Failed installing bundle via Add-AppxPackage {e}"); throw e; } } - private PSObject GetAppxObject(string packageName) + private PSObject? GetAppxObject(string packageName) { return this.ExecuteAppxCmdlet( GetAppxPackage, @@ -266,7 +273,7 @@ private void InstallVCLibsDependencies() { foreach (dynamic psobject in result) { - string versionString = psobject?.Version?.ToString(); + string? versionString = psobject?.Version?.ToString(); if (versionString == null) { continue; @@ -276,7 +283,7 @@ private void InstallVCLibsDependencies() if (packageVersion >= minimumVersion) { - this.psCmdlet.WriteDebug($"VCLibs dependency satisfied by: {psobject?.PackageFullName ?? ""}"); + this.pwshCmdlet.Write(StreamType.Verbose, $"VCLibs dependency satisfied by: {psobject?.PackageFullName ?? ""}"); isInstalled = true; break; } @@ -285,7 +292,7 @@ private void InstallVCLibsDependencies() if (!isInstalled) { - this.psCmdlet.WriteDebug("Couldn't find required VCLibs package"); + this.pwshCmdlet.Write(StreamType.Verbose, "Couldn't find required VCLibs package"); var vcLibsDependencies = new List(); var arch = RuntimeInformation.OSArchitecture; @@ -317,7 +324,7 @@ private void InstallVCLibsDependencies() } else { - this.psCmdlet.WriteDebug($"VCLibs are updated."); + this.pwshCmdlet.Write(StreamType.Verbose, $"VCLibs are updated."); } } @@ -360,7 +367,7 @@ private void InstallUiXaml() } } - private void AddAppxPackageAsUri(string packageUri, IList options = null) + private void AddAppxPackageAsUri(string packageUri, IList? options = null) { try { @@ -378,18 +385,18 @@ private void AddAppxPackageAsUri(string packageUri, IList options = null // If we couldn't install it via URI, try download and install. if (e.ErrorRecord.CategoryInfo.Category == ErrorCategory.OpenError) { - this.psCmdlet.WriteDebug($"Failed adding package [{packageUri}]. Retrying downloading it."); + this.pwshCmdlet.Write(StreamType.Verbose, $"Failed adding package [{packageUri}]. Retrying downloading it."); this.DownloadPackageAndAdd(packageUri, options); } else { - this.psCmdlet.WriteError(e.ErrorRecord); + this.pwshCmdlet.Write(StreamType.Error, e.ErrorRecord); throw e; } } } - private void DownloadPackageAndAdd(string packageUrl, IList options) + private void DownloadPackageAndAdd(string packageUrl, IList? options) { using var tempFile = new TempFile(); @@ -407,7 +414,7 @@ private void DownloadPackageAndAdd(string packageUrl, IList options) options); } - private Collection ExecuteAppxCmdlet(string cmdlet, Dictionary parameters = null, IList options = null) + private Collection ExecuteAppxCmdlet(string cmdlet, Dictionary? parameters = null, IList? options = null) { var ps = PowerShell.Create(RunspaceMode.CurrentRunspace); @@ -447,7 +454,7 @@ private Collection ExecuteAppxCmdlet(string cmdlet, Dictionary(Type type, in Guid iid) + private static T Create(Type? type, in Guid iid) where T : new() { + if (type == null) + { + throw new ArgumentNullException(iid.ToString()); + } + if (Utilities.UsesInProcWinget) { var arch = RuntimeInformation.ProcessArchitecture; @@ -169,7 +174,7 @@ private static T Create(Type type, in Guid iid) } string executingAssemblyLocation = Assembly.GetExecutingAssembly().Location; - string executingAssemblyDirectory = Path.Combine(Path.GetDirectoryName(executingAssemblyLocation), arch.ToString().ToLower()); + string executingAssemblyDirectory = Path.Combine(Path.GetDirectoryName(executingAssemblyLocation) !, arch.ToString().ToLower()); SetDllDirectoryW(executingAssemblyDirectory); @@ -183,7 +188,7 @@ private static T Create(Type type, in Guid iid) } } - object instance = null; + object? instance = null; if (Utilities.ExecutingAsAdministrator) { @@ -206,6 +211,11 @@ private static T Create(Type type, in Guid iid) instance = Activator.CreateInstance(type); } + if (instance == null) + { + throw new ArgumentNullException(); + } + #if NET IntPtr pointer = Marshal.GetIUnknownForObject(instance); return MarshalInterface.FromAbi(pointer); @@ -223,6 +233,6 @@ private static extern int WinGetServerManualActivation_CreateInstance( [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool SetDllDirectoryW([MarshalAs(UnmanagedType.LPWStr)] string directory); + private static extern bool SetDllDirectoryW([MarshalAs(UnmanagedType.LPWStr)] string? directory); } } diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/PackageManagerWrapper.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/PackageManagerWrapper.cs index 29f3c1ab72..4fcbe66b7f 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/PackageManagerWrapper.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/PackageManagerWrapper.cs @@ -11,6 +11,7 @@ namespace Microsoft.WinGet.Client.Engine.Helpers using System.Runtime.InteropServices; using Microsoft.Management.Deployment; using Microsoft.WinGet.Client.Engine.Common; + using Microsoft.WinGet.Client.Engine.Exceptions; using Windows.Foundation; /// @@ -21,7 +22,7 @@ internal sealed class PackageManagerWrapper { private static readonly Lazy Lazy = new (() => new PackageManagerWrapper()); - private PackageManager packageManager = null; + private PackageManager packageManager = null!; private PackageManagerWrapper() { @@ -111,6 +112,12 @@ public PackageCatalogReference CreateCompositePackageCatalog(CreateCompositePack private TReturn Execute(Func func, bool canRetry) { + if (Utilities.UsesInProcWinget && Utilities.ThreadIsSTA) + { + // If you failed here, then you didn't wrap your call in ManagementDeploymentCommand.Execute + throw new SingleThreadedApartmentException(); + } + bool stopRetry = false; while (true) { @@ -125,7 +132,7 @@ private TReturn Execute(Func func, bool canRetry) } catch (COMException ex) when (ex.HResult == ErrorCode.RpcServerUnavailable || ex.HResult == ErrorCode.RpcCallFailed) { - this.packageManager = null; + this.packageManager = null!; if (stopRetry || !canRetry) { diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/TempDirectory.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/TempDirectory.cs index b9a94bc26b..8c087860d2 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/TempDirectory.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/TempDirectory.cs @@ -24,7 +24,7 @@ internal class TempDirectory : IDisposable /// Delete directory if already exists. Default true. /// Deletes directory at disposing time. Default true. public TempDirectory( - string directoryName = null, + string? directoryName = null, bool deleteIfExists = true, bool cleanup = true) { diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/TempFile.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/TempFile.cs index 6034dcb864..3c5b26db29 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/TempFile.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/TempFile.cs @@ -26,9 +26,9 @@ internal class TempFile : IDisposable /// Optional content. If not null or empty, creates file and writes to it. /// Deletes file at disposing time. Default true. public TempFile( - string fileName = null, + string? fileName = null, bool deleteIfExists = true, - string content = null, + string? content = null, bool cleanup = true) { if (fileName is null) @@ -78,7 +78,7 @@ public void Dispose() /// Creates the file. /// /// Content. - public void CreateFile(string content = null) + public void CreateFile(string? content = null) { if (content is null) { diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/WinGetCLICommandResult.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/WinGetCLICommandResult.cs index d41262bf53..3b65d4753d 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/WinGetCLICommandResult.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/WinGetCLICommandResult.cs @@ -21,7 +21,7 @@ internal class WinGetCLICommandResult /// Exit code. /// Standard output. /// Standard error. - public WinGetCLICommandResult(string command, string parameters, int exitCode, string stdOut, string stdErr) + public WinGetCLICommandResult(string command, string? parameters, int exitCode, string stdOut, string stdErr) { this.Command = command; this.Parameters = parameters; @@ -38,7 +38,7 @@ public WinGetCLICommandResult(string command, string parameters, int exitCode, s /// /// Gets the parameters. /// - public string Parameters { get; private set; } + public string? Parameters { get; private set; } /// /// Gets the exit code. diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/WingetCLIWrapper.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/WingetCLIWrapper.cs index f7780f1e6d..690149622e 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/WingetCLIWrapper.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/WingetCLIWrapper.cs @@ -69,7 +69,7 @@ public static string WinGetFullPath /// Parameters. /// Time out. /// WinGetCommandResult. - public WinGetCLICommandResult RunCommand(string command, string parameters = null, int timeOut = 60000) + public WinGetCLICommandResult RunCommand(string command, string? parameters = null, int timeOut = 60000) { string args = command; if (!string.IsNullOrEmpty(parameters)) diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/WriteProgressAdapter.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/WriteProgressAdapter.cs index 1325d0e273..de7b749bf8 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/WriteProgressAdapter.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Helpers/WriteProgressAdapter.cs @@ -9,6 +9,7 @@ namespace Microsoft.WinGet.Client.Engine.Helpers using System.Collections.Generic; using System.Management.Automation; using System.Threading; + using Microsoft.WinGet.Common.Command; /// /// Marshals calls to back to the main thread. @@ -17,16 +18,16 @@ internal class WriteProgressAdapter { private readonly AutoResetEvent resetEvent = new (false); private readonly Queue records = new (); - private readonly Cmdlet cmdlet; + private readonly PowerShellCmdlet pwshCmdlet; private volatile bool completed = false; /// /// Initializes a new instance of the class. /// - /// A instance. - public WriteProgressAdapter(Cmdlet cmdlet) + /// A instance. + public WriteProgressAdapter(PowerShellCmdlet pwshCmdlet) { - this.cmdlet = cmdlet; + this.pwshCmdlet = pwshCmdlet; } /// @@ -83,7 +84,7 @@ private void Flush() { while (this.records.Count > 0) { - this.cmdlet.WriteProgress(this.records.Dequeue()); + this.pwshCmdlet.Write(StreamType.Progress, this.records.Dequeue()); } } } diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Microsoft.WinGet.Client.Engine.csproj b/src/PowerShell/Microsoft.WinGet.Client.Engine/Microsoft.WinGet.Client.Engine.csproj index d813887740..1c0dc85d76 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Microsoft.WinGet.Client.Engine.csproj +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Microsoft.WinGet.Client.Engine.csproj @@ -18,6 +18,7 @@ $(CoreFramework);$(DesktopFramework) $(OutputPath)\Microsoft.WinGet.Client.Engine.xml 10.0.18362.0 + enable @@ -32,6 +33,11 @@ None + + + + + @@ -42,8 +48,8 @@ - - + + @@ -84,21 +90,6 @@ win10 - - - True - True - Resources.resx - - - - - - ResXFileCodeGenerator - Resources.Designer.cs - - - $(DefineConstants);POWERSHELL_WINDOWS @@ -115,6 +106,7 @@ ResXFileCodeGenerator Resources.Designer.cs + Microsoft.WinGet.Resources diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/PSObjects/PSCatalogPackage.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/PSObjects/PSCatalogPackage.cs index f18b08a39a..c01ae0b462 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/PSObjects/PSCatalogPackage.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/PSObjects/PSCatalogPackage.cs @@ -114,7 +114,7 @@ public string CheckInstalledStatus() public PSPackageVersionInfo GetPackageVersionInfo(string version) { // get specific version that matches - PackageVersionId packageVersionId = this.AvailablePackageVersionIds.FirstOrDefault(x => x.Version == version); + PackageVersionId? packageVersionId = this.AvailablePackageVersionIds.FirstOrDefault(x => x.Version == version); if (packageVersionId != null) { return new PSPackageVersionInfo(this.CatalogPackageCOM.GetPackageVersionInfo(packageVersionId)); diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Properties/Resources.Designer.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Properties/Resources.Designer.cs index d301d1d34f..f527562768 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Properties/Resources.Designer.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Properties/Resources.Designer.cs @@ -8,7 +8,7 @@ // //------------------------------------------------------------------------------ -namespace Microsoft.WinGet.Client.Engine.Properties { +namespace Microsoft.WinGet.Resources { using System; @@ -69,6 +69,15 @@ internal static string CatalogConnectExceptionMessage { } } + /// + /// Looks up a localized string similar to Debug parameter not supported. + /// + internal static string DebugNotSupported { + get { + return ResourceManager.GetString("DebugNotSupported", resourceCulture); + } + } + /// /// Looks up a localized string similar to An error occurred while searching for packages: {0}. /// diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Properties/Resources.resx b/src/PowerShell/Microsoft.WinGet.Client.Engine/Properties/Resources.resx index 0fcc51851b..557dc639d9 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Properties/Resources.resx +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Properties/Resources.resx @@ -224,4 +224,7 @@ This cmdlet requires administrator privileges to execute. + + Debug parameter not supported + \ No newline at end of file diff --git a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Commands/ConfigurationCommand.cs b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Commands/ConfigurationCommand.cs index bd59d6e290..b0e98f1289 100644 --- a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Commands/ConfigurationCommand.cs +++ b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Commands/ConfigurationCommand.cs @@ -7,6 +7,7 @@ namespace Microsoft.WinGet.Configuration.Engine.Commands { using System; + using System.Collections.Generic; using System.IO; using System.Linq; using System.Management.Automation; @@ -14,10 +15,12 @@ namespace Microsoft.WinGet.Configuration.Engine.Commands using Microsoft.Management.Configuration; using Microsoft.Management.Configuration.Processor; using Microsoft.PowerShell; + using Microsoft.WinGet.Common.Command; using Microsoft.WinGet.Configuration.Engine.Exceptions; using Microsoft.WinGet.Configuration.Engine.Helpers; using Microsoft.WinGet.Configuration.Engine.PSObjects; - using Microsoft.WinGet.Configuration.Engine.Resources; + using Microsoft.WinGet.Resources; + using Microsoft.WinGet.SharedLib.PolicySettings; using Windows.Storage; using Windows.Storage.Streams; using WinRT; @@ -25,14 +28,14 @@ namespace Microsoft.WinGet.Configuration.Engine.Commands /// /// Class that deals configuration commands. /// - public sealed class ConfigurationCommand : AsyncCommand + public sealed class ConfigurationCommand : PowerShellCmdlet { /// /// Initializes a new instance of the class. /// /// PSCmdlet. public ConfigurationCommand(PSCmdlet psCmdlet) - : base(psCmdlet) + : base(psCmdlet, new HashSet { Policy.WinGet, Policy.Configuration, Policy.WinGetCommandLineInterfaces }) { } @@ -87,20 +90,13 @@ public void Get( bool canUseTelemetry) { var openParams = new OpenConfigurationParameters( - this.PsCmdlet, configFile, modulePath, executionPolicy, canUseTelemetry); + this, configFile, modulePath, executionPolicy, canUseTelemetry); // Start task. var runningTask = this.RunOnMTA( async () => { - try - { - return await this.OpenConfigurationSetAsync(openParams); - } - finally - { - this.Complete(); - } + return await this.OpenConfigurationSetAsync(openParams); }); this.Wait(runningTask); @@ -131,7 +127,6 @@ public void GetDetails(PSConfigurationSet psConfigurationSet) } finally { - this.Complete(); psConfigurationSet.DoneProcessing(); } @@ -229,7 +224,6 @@ public void Test(PSConfigurationSet psConfigurationSet) } finally { - this.Complete(); psConfigurationSet.DoneProcessing(); } }); @@ -261,7 +255,6 @@ public void Validate(PSConfigurationSet psConfigurationSet) } finally { - this.Complete(); psConfigurationSet.DoneProcessing(); } }); @@ -344,7 +337,6 @@ private PSConfigurationJob StartApplyInternal(PSConfigurationSet psConfiguration } finally { - this.Complete(); psConfigurationSet.DoneProcessing(); } }); diff --git a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Exceptions/ApplyConfigurationException.cs b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Exceptions/ApplyConfigurationException.cs index 3f6dcf4f5c..cf2c77d33b 100644 --- a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Exceptions/ApplyConfigurationException.cs +++ b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Exceptions/ApplyConfigurationException.cs @@ -10,7 +10,7 @@ namespace Microsoft.WinGet.Configuration.Engine.Exceptions using System.Collections.Generic; using Microsoft.Management.Configuration; using Microsoft.WinGet.Configuration.Engine.PSObjects; - using Microsoft.WinGet.Configuration.Engine.Resources; + using Microsoft.WinGet.Resources; /// /// Exception thrown when there's an error when configuration is applied. diff --git a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Exceptions/GetDetailsException.cs b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Exceptions/GetDetailsException.cs index 6a422a041b..fccdad5896 100644 --- a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Exceptions/GetDetailsException.cs +++ b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Exceptions/GetDetailsException.cs @@ -10,7 +10,7 @@ namespace Microsoft.WinGet.Configuration.Engine.Exceptions using System.Collections.Generic; using Microsoft.Management.Configuration; using Microsoft.WinGet.Configuration.Engine.PSObjects; - using Microsoft.WinGet.Configuration.Engine.Resources; + using Microsoft.WinGet.Resources; /// /// Exception thrown while getting details. diff --git a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Exceptions/OpenConfigurationSetException.cs b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Exceptions/OpenConfigurationSetException.cs index fe998db8bb..da1d811ce3 100644 --- a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Exceptions/OpenConfigurationSetException.cs +++ b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Exceptions/OpenConfigurationSetException.cs @@ -9,7 +9,7 @@ namespace Microsoft.WinGet.Configuration.Engine.Exceptions using System; using System.Text; using Microsoft.Management.Configuration; - using Microsoft.WinGet.Configuration.Engine.Resources; + using Microsoft.WinGet.Resources; /// /// Exception thrown when failed to open a configuration set. diff --git a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/ApplyConfigurationSetProgressOutput.cs b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/ApplyConfigurationSetProgressOutput.cs index 9b077d832b..b6dd427d14 100644 --- a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/ApplyConfigurationSetProgressOutput.cs +++ b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/ApplyConfigurationSetProgressOutput.cs @@ -7,7 +7,7 @@ namespace Microsoft.WinGet.Configuration.Engine.Helpers { using Microsoft.Management.Configuration; - using Microsoft.WinGet.Configuration.Engine.Commands; + using Microsoft.WinGet.Common.Command; using Windows.Foundation; /// @@ -26,7 +26,7 @@ internal class ApplyConfigurationSetProgressOutput : ConfigurationSetProgressOut /// The message in the progress bar. /// The activity complete message. /// Total of units expected. - public ApplyConfigurationSetProgressOutput(AsyncCommand cmd, int activityId, string activity, string inProgressMessage, string completeMessage, int totalUnitsExpected) + public ApplyConfigurationSetProgressOutput(PowerShellCmdlet cmd, int activityId, string activity, string inProgressMessage, string completeMessage, int totalUnitsExpected) : base(cmd, activityId, activity, inProgressMessage, completeMessage, totalUnitsExpected) { } diff --git a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/ConfigurationSetProgressOutputBase.cs b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/ConfigurationSetProgressOutputBase.cs index 9d04918a21..b03fa13536 100644 --- a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/ConfigurationSetProgressOutputBase.cs +++ b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/ConfigurationSetProgressOutputBase.cs @@ -9,7 +9,7 @@ namespace Microsoft.WinGet.Configuration.Engine.Helpers using System; using System.Collections.Generic; using Microsoft.Management.Configuration; - using Microsoft.WinGet.Configuration.Engine.Commands; + using Microsoft.WinGet.Common.Command; using Windows.Foundation; /// @@ -19,7 +19,7 @@ namespace Microsoft.WinGet.Configuration.Engine.Helpers /// Progress data. internal abstract class ConfigurationSetProgressOutputBase { - private readonly AsyncCommand cmd; + private readonly PowerShellCmdlet cmd; private readonly int activityId; private readonly string activity; private readonly string inProgressMessage; @@ -35,7 +35,7 @@ internal abstract class ConfigurationSetProgressOutputBaseThe message in the progress bar. /// The activity complete message. /// Total of units expected. - public ConfigurationSetProgressOutputBase(AsyncCommand cmd, int activityId, string activity, string inProgressMessage, string completeMessage, int totalUnitsExpected) + public ConfigurationSetProgressOutputBase(PowerShellCmdlet cmd, int activityId, string activity, string inProgressMessage, string completeMessage, int totalUnitsExpected) { this.cmd = cmd; this.activityId = activityId; diff --git a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/ConfigurationUnitInformation.cs b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/ConfigurationUnitInformation.cs index 0cfbbe40ea..59a74fcd1e 100644 --- a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/ConfigurationUnitInformation.cs +++ b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/ConfigurationUnitInformation.cs @@ -13,7 +13,7 @@ namespace Microsoft.WinGet.Configuration.Engine.Helpers using System.Text; using Microsoft.Management.Configuration; using Microsoft.WinGet.Configuration.Engine.Extensions; - using Microsoft.WinGet.Configuration.Engine.Resources; + using Microsoft.WinGet.Resources; using Windows.Foundation.Collections; /// diff --git a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/GetConfigurationSetDetailsProgressOutput.cs b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/GetConfigurationSetDetailsProgressOutput.cs index ae0bc37910..b667bef98e 100644 --- a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/GetConfigurationSetDetailsProgressOutput.cs +++ b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/GetConfigurationSetDetailsProgressOutput.cs @@ -7,7 +7,7 @@ namespace Microsoft.WinGet.Configuration.Engine.Helpers { using Microsoft.Management.Configuration; - using Microsoft.WinGet.Configuration.Engine.Commands; + using Microsoft.WinGet.Common.Command; using Windows.Foundation; /// @@ -24,7 +24,7 @@ internal class GetConfigurationSetDetailsProgressOutput : ConfigurationSetProgre /// The message in the progress bar. /// The activity complete message. /// Total of units expected. - public GetConfigurationSetDetailsProgressOutput(AsyncCommand cmd, int activityId, string activity, string inProgressMessage, string completeMessage, int totalUnitsExpected) + public GetConfigurationSetDetailsProgressOutput(PowerShellCmdlet cmd, int activityId, string activity, string inProgressMessage, string completeMessage, int totalUnitsExpected) : base(cmd, activityId, activity, inProgressMessage, completeMessage, totalUnitsExpected) { } diff --git a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/OpenConfigurationParameters.cs b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/OpenConfigurationParameters.cs index e7080b0bd8..cc0107c4c3 100644 --- a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/OpenConfigurationParameters.cs +++ b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/OpenConfigurationParameters.cs @@ -11,7 +11,8 @@ namespace Microsoft.WinGet.Configuration.Engine.Helpers using System.Management.Automation; using Microsoft.Management.Configuration.Processor; using Microsoft.PowerShell; - using Microsoft.WinGet.Configuration.Engine.Resources; + using Microsoft.WinGet.Common.Command; + using Microsoft.WinGet.Resources; /// /// The parameters used to open a configuration. @@ -25,19 +26,19 @@ internal class OpenConfigurationParameters /// /// Initializes a new instance of the class. /// - /// PsCmdlet caller. + /// PowerShellCmdlet. /// The configuration file. /// The module path to use. /// Execution policy. /// If telemetry can be used. public OpenConfigurationParameters( - PSCmdlet psCmdlet, + PowerShellCmdlet pwshCmdlet, string file, string modulePath, ExecutionPolicy executionPolicy, bool canUseTelemetry) { - this.ConfigFile = this.VerifyFile(file, psCmdlet); + this.ConfigFile = this.VerifyFile(file, pwshCmdlet); this.InitializeModulePath(modulePath); this.Policy = this.GetConfigurationProcessorPolicy(executionPolicy); this.CanUseTelemetry = canUseTelemetry; @@ -68,13 +69,13 @@ public OpenConfigurationParameters( /// public bool CanUseTelemetry { get; } - private string VerifyFile(string filePath, PSCmdlet psCmdlet) + private string VerifyFile(string filePath, PowerShellCmdlet pwshCmdlet) { if (!Path.IsPathRooted(filePath)) { filePath = Path.GetFullPath( Path.Combine( - psCmdlet.SessionState.Path.CurrentFileSystemLocation.Path, + pwshCmdlet.GetCurrentFileSystemLocation(), filePath)); } else diff --git a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/TestConfigurationSetProgressOutput.cs b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/TestConfigurationSetProgressOutput.cs index b00a392d7c..e8aa26a7b4 100644 --- a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/TestConfigurationSetProgressOutput.cs +++ b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Helpers/TestConfigurationSetProgressOutput.cs @@ -7,7 +7,7 @@ namespace Microsoft.WinGet.Configuration.Engine.Helpers { using Microsoft.Management.Configuration; - using Microsoft.WinGet.Configuration.Engine.Commands; + using Microsoft.WinGet.Common.Command; using Windows.Foundation; /// @@ -26,7 +26,7 @@ internal class TestConfigurationSetProgressOutput : ConfigurationSetProgressOutp /// The message in the progress bar. /// The activity complete message. /// Total of units expected. - public TestConfigurationSetProgressOutput(AsyncCommand cmd, int activityId, string activity, string inProgressMessage, string completeMessage, int totalUnitsExpected) + public TestConfigurationSetProgressOutput(PowerShellCmdlet cmd, int activityId, string activity, string inProgressMessage, string completeMessage, int totalUnitsExpected) : base(cmd, activityId, activity, inProgressMessage, completeMessage, totalUnitsExpected) { } diff --git a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Microsoft.WinGet.Configuration.Engine.csproj b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Microsoft.WinGet.Configuration.Engine.csproj index 2bb261f249..259db92b1e 100644 --- a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Microsoft.WinGet.Configuration.Engine.csproj +++ b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Microsoft.WinGet.Configuration.Engine.csproj @@ -24,6 +24,11 @@ + + + + + @@ -58,6 +63,7 @@ ResXFileCodeGenerator Resources.Designer.cs + Microsoft.WinGet.Resources diff --git a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/PSObjects/PSConfigurationJob.cs b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/PSObjects/PSConfigurationJob.cs index 0b8fb21b9d..d9f28fee63 100644 --- a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/PSObjects/PSConfigurationJob.cs +++ b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/PSObjects/PSConfigurationJob.cs @@ -7,7 +7,7 @@ namespace Microsoft.WinGet.Configuration.Engine.PSObjects { using System.Threading.Tasks; - using Microsoft.WinGet.Configuration.Engine.Commands; + using Microsoft.WinGet.Common.Command; /// /// This is a wrapper object for asynchronous task for this module. @@ -22,7 +22,7 @@ public class PSConfigurationJob /// The start command. internal PSConfigurationJob( Task applyConfigTask, - AsyncCommand startCommand) + PowerShellCmdlet startCommand) { this.ApplyConfigurationTask = applyConfigTask; this.StartCommand = startCommand; @@ -36,6 +36,6 @@ internal PSConfigurationJob( /// /// Gets the command that started async operation. /// - internal AsyncCommand StartCommand { get; private set; } + internal PowerShellCmdlet StartCommand { get; private set; } } } diff --git a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/PSObjects/PSConfigurationProcessor.cs b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/PSObjects/PSConfigurationProcessor.cs index bd457585d9..65c562bc79 100644 --- a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/PSObjects/PSConfigurationProcessor.cs +++ b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/PSObjects/PSConfigurationProcessor.cs @@ -7,12 +7,8 @@ namespace Microsoft.WinGet.Configuration.Engine.PSObjects { using System; - using System.Management.Automation; using Microsoft.Management.Configuration; - using Microsoft.PowerShell.Commands; - using Microsoft.WinGet.Configuration.Engine.Commands; - using Microsoft.WinGet.Configuration.Engine.Exceptions; - using static Microsoft.WinGet.Configuration.Engine.Commands.AsyncCommand; + using Microsoft.WinGet.Common.Command; /// /// Creates configuration processor and set up diagnostic logging. @@ -26,7 +22,7 @@ public class PSConfigurationProcessor { private static readonly object CmdletLock = new (); - private AsyncCommand diagnosticCommand; + private PowerShellCmdlet diagnosticCommand; /// /// Initializes a new instance of the class. @@ -34,7 +30,7 @@ public class PSConfigurationProcessor /// Factory. /// AsyncCommand to use for diagnostics. /// If telemetry can be used. - internal PSConfigurationProcessor(IConfigurationSetProcessorFactory factory, AsyncCommand diagnosticCommand, bool canUseTelemetry) + internal PSConfigurationProcessor(IConfigurationSetProcessorFactory factory, PowerShellCmdlet diagnosticCommand, bool canUseTelemetry) { this.Processor = new ConfigurationProcessor(factory); this.Processor.MinimumLevel = DiagnosticLevel.Verbose; @@ -53,7 +49,7 @@ internal PSConfigurationProcessor(IConfigurationSetProcessorFactory factory, Asy /// Updates the cmdlet that is used for diagnostics. /// /// New diagnostic command. - internal void UpdateDiagnosticCmdlet(AsyncCommand newDiagnosticCommand) + internal void UpdateDiagnosticCmdlet(PowerShellCmdlet newDiagnosticCommand) { lock (CmdletLock) { @@ -65,13 +61,13 @@ private void LogConfigurationDiagnostics(IDiagnosticInformation diagnosticInform { try { - AsyncCommand asyncCommand = this.diagnosticCommand; - if (asyncCommand != null) + PowerShellCmdlet pwshCmdlet = this.diagnosticCommand; + if (pwshCmdlet != null) { // Printing each diagnostic error in their own equivalent stream is too noisy. // If users want them they have to specify -Verbose. string tag = $"[Diagnostic{diagnosticInformation.Level}] "; - asyncCommand.Write(StreamType.Verbose, $"{tag}{diagnosticInformation.Message}"); + pwshCmdlet.Write(StreamType.Verbose, $"{tag}{diagnosticInformation.Message}"); } } catch (Exception) diff --git a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/PSObjects/PSUnitResult.cs b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/PSObjects/PSUnitResult.cs index 8e78eb118a..b53d380644 100644 --- a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/PSObjects/PSUnitResult.cs +++ b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/PSObjects/PSUnitResult.cs @@ -8,7 +8,7 @@ namespace Microsoft.WinGet.Configuration.Engine.PSObjects { using Microsoft.Management.Configuration; using Microsoft.WinGet.Configuration.Engine.Exceptions; - using Microsoft.WinGet.Configuration.Engine.Resources; + using Microsoft.WinGet.Resources; /// /// Unit result. diff --git a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Resources/Resources.Designer.cs b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Resources/Resources.Designer.cs index 1b0d7b7892..4e45b5d170 100644 --- a/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Resources/Resources.Designer.cs +++ b/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Resources/Resources.Designer.cs @@ -8,7 +8,7 @@ // //------------------------------------------------------------------------------ -namespace Microsoft.WinGet.Configuration.Engine.Resources { +namespace Microsoft.WinGet.Resources { using System;