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;