// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Management.Automation;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using Microsoft.PowerShell.Commands.ShowCommandExtension;
namespace Microsoft.PowerShell.Commands
{
///
/// Show-Command displays a GUI for a cmdlet, or for all cmdlets if no specific cmdlet is specified.
///
[Cmdlet(VerbsCommon.Show, "Command", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2109589")]
public class ShowCommandCommand : PSCmdlet, IDisposable
{
#region Private Fields
///
/// Set to true when ProcessRecord is reached, since it will always open a window.
///
private bool _hasOpenedWindow;
///
/// Determines if the command should be sent to the pipeline as a string instead of run.
///
private bool _passThrough;
///
/// Uses ShowCommandProxy to invoke WPF GUI object.
///
private ShowCommandProxy _showCommandProxy;
///
/// Data container for all cmdlets. This is populated when show-command is called with no command name.
///
private List _commands;
///
/// List of modules that have been loaded indexed by module name.
///
private Dictionary _importedModules;
///
/// Record the EndProcessing error.
///
private PSDataCollection _errors = new();
///
/// Field used for the NoCommonParameter parameter.
///
private SwitchParameter _noCommonParameter;
///
/// Object used for ShowCommand with a command name that holds the view model created for the command.
///
private object _commandViewModelObj;
#endregion
#region Input Cmdlet Parameter
///
/// Gets or sets the command name.
///
[Parameter(Position = 0)]
[Alias("CommandName")]
public string Name { get; set; }
///
/// Gets or sets the Width.
///
[Parameter]
[ValidateRange(300, int.MaxValue)]
public double Height { get; set; }
///
/// Gets or sets the Width.
///
[Parameter]
[ValidateRange(300, int.MaxValue)]
public double Width { get; set; }
///
/// Gets or sets a value indicating Common Parameters should not be displayed.
///
[Parameter]
public SwitchParameter NoCommonParameter
{
get { return _noCommonParameter; }
set { _noCommonParameter = value; }
}
///
/// Gets or sets a value indicating errors should not cause a message window to be displayed.
///
[Parameter]
public SwitchParameter ErrorPopup { get; set; }
///
/// Gets or sets a value indicating the command should be sent to the pipeline as a string instead of run.
///
[Parameter]
public SwitchParameter PassThru
{
get { return _passThrough; }
set { _passThrough = value; }
}
#endregion
#region Public and Protected Methods
///
/// Executes a PowerShell script, writing the output objects to the pipeline.
///
/// Script to execute.
public void RunScript(string script)
{
if (_showCommandProxy == null || string.IsNullOrEmpty(script))
{
return;
}
if (_passThrough)
{
this.WriteObject(script);
return;
}
if (ErrorPopup)
{
this.RunScriptSilentlyAndWithErrorHookup(script);
return;
}
if (_showCommandProxy.HasHostWindow)
{
if (!_showCommandProxy.SetPendingISECommand(script))
{
this.RunScriptSilentlyAndWithErrorHookup(script);
}
return;
}
// Don't send newline at end as PSReadLine shows it rather than executing
if (!ConsoleInputWithNativeMethods.AddToConsoleInputBuffer(script, newLine: false))
{
this.WriteDebug(FormatAndOut_out_gridview.CannotWriteToConsoleInputBuffer);
this.RunScriptSilentlyAndWithErrorHookup(script);
}
}
///
/// Dispose method in IDisposable.
///
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
///
/// Initialize a proxy instance for show-command.
///
protected override void BeginProcessing()
{
_showCommandProxy = new ShowCommandProxy(this);
if (_showCommandProxy.ScreenHeight < this.Height)
{
ErrorRecord error = new(
new NotSupportedException(string.Format(CultureInfo.CurrentUICulture, FormatAndOut_out_gridview.PropertyValidate, "Height", _showCommandProxy.ScreenHeight)),
"PARAMETER_DATA_ERROR",
ErrorCategory.InvalidData,
null);
this.ThrowTerminatingError(error);
}
if (_showCommandProxy.ScreenWidth < this.Width)
{
ErrorRecord error = new(
new NotSupportedException(string.Format(CultureInfo.CurrentUICulture, FormatAndOut_out_gridview.PropertyValidate, "Width", _showCommandProxy.ScreenWidth)),
"PARAMETER_DATA_ERROR",
ErrorCategory.InvalidData,
null);
this.ThrowTerminatingError(error);
}
}
///
/// ProcessRecord with or without CommandName.
///
protected override void ProcessRecord()
{
if (Name == null)
{
_hasOpenedWindow = this.CanProcessRecordForAllCommands();
}
else
{
_hasOpenedWindow = this.CanProcessRecordForOneCommand();
}
}
///
/// Optionally displays errors in a message.
///
protected override void EndProcessing()
{
if (!_hasOpenedWindow)
{
return;
}
// We wait until the window is loaded and then activate it
// to work around the console window gaining activation somewhere
// in the end of ProcessRecord, which causes the keyboard focus
// (and use oif tab key to focus controls) to go away from the window
_showCommandProxy.WindowLoaded.WaitOne();
_showCommandProxy.ActivateWindow();
this.WaitForWindowClosedOrHelpNeeded();
this.RunScript(_showCommandProxy.GetScript());
if (_errors.Count == 0 || !ErrorPopup)
{
return;
}
StringBuilder errorString = new();
for (int i = 0; i < _errors.Count; i++)
{
if (i != 0)
{
errorString.AppendLine();
}
ErrorRecord error = _errors[i];
errorString.Append(error.Exception.Message);
}
_showCommandProxy.ShowErrorString(errorString.ToString());
}
///
/// StopProcessing is called close the window when user press Ctrl+C in the command prompt.
///
protected override void StopProcessing()
{
_showCommandProxy.CloseWindow();
}
#endregion
#region Private Methods
///
/// Runs the script in a new PowerShell instance and hooks up error stream to potentially display error popup.
/// This method has the inconvenience of not showing to the console user the script being executed.
///
/// Script to be run.
private void RunScriptSilentlyAndWithErrorHookup(string script)
{
// errors are not created here, because there is a field for it used in the final pop up
PSDataCollection