PowerShell/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs
PowerShell Team c748652c34 Copy all mapped files from [SD:725290]
commit 8cec8f150da7583b7af5efbe2853efee0179750c
2016-07-28 23:23:03 -07:00

2935 lines
108 KiB
C#

/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
#pragma warning disable 1634, 1691
using System;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Management.Automation;
using System.Management.Automation.Host;
using System.Management.Automation.Internal;
using System.Management.Automation.Runspaces;
using System.Management.Automation.Remoting;
using System.Management.Automation.Security;
using System.Threading;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
using System.Management.Automation.Language;
#if CORECLR
using Microsoft.PowerShell.CoreClr.Stubs;
// Some APIs are missing from System.Environment. We use System.Management.Automation.Environment as a proxy type:
// - for missing APIs, System.Management.Automation.Environment has extension implementation.
// - for existing APIs, System.Management.Automation.Environment redirect the call to System.Environment.
using Environment = System.Management.Automation.Environment;
#endif
using Dbg = System.Management.Automation.Diagnostics;
using ConsoleHandle = Microsoft.Win32.SafeHandles.SafeFileHandle;
using NakedWin32Handle = System.IntPtr;
using System.Management.Automation.Tracing;
using Microsoft.PowerShell.Telemetry.Internal;
using Debugger = System.Management.Automation.Debugger;
namespace Microsoft.PowerShell
{
/// <summary>
///
/// Subclasses S.M.A.Host to implement a console-mode monad host.
///
/// </summary>
///
[SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
internal sealed partial class ConsoleHost
:
PSHost,
IDisposable,
IHostSupportsInteractiveSession,
IHostProvidesTelemetryData
{
#region static methods
internal const uint ExitCodeSuccess = 0x00000000;
internal const uint ExitCodeCtrlBreak = 0xFFFE0000;
internal const uint ExitCodeInitFailure = 0xFFFF0000;
internal const uint ExitCodeBadCommandLineParameter = 0xFFFD0000;
#if CORECLR
// AccessViolationException/StackOverflowException Not In CoreCLR.
// The CoreCLR team told us to not check for these exceptions because they
// usually won't be caught.
internal static void CheckForSevereException(Exception e) { }
#else
// Keep in sync:
// S.M.A.CommandProcessorBase.CheckForSevereException
// S.M.A.Internal.ConsoleHost.CheckForSevereException
// S.M.A.Commands.CommandsCommon.CheckForSevereException
// S.M.A.Commands.UtilityCommon.CheckForSevereException
/// <summary>
/// Checks whether the exception is a severe exception which should
/// cause immediate process failure.
/// </summary>
/// <param name="e"></param>
/// <remarks>
/// CB says 02/23/2005: I personally would err on the side
/// of treating OOM like an application exception, rather than
/// a critical system failure.I think this will be easier to justify
/// in Orcas, if we tease apart the two cases of OOM better.
/// But even in Whidbey, how likely is it that we couldn't JIT
/// some backout code? At that point, the process or possibly
/// the machine is likely to stop executing soon no matter
/// what you do in this routine. So I would just consider
/// AccessViolationException. (I understand why you have SO here,
/// at least temporarily).
///
/// JN/GX 04/15/2005: There is currently no way to log host events,
/// so these FailFasts cannot be logged.
/// </remarks>
internal static void CheckForSevereException(Exception e)
{
if (e is AccessViolationException || e is StackOverflowException)
{
WindowsErrorReporting.FailFast(e);
}
}
#endif
// NTRAID#Windows Out Of Band Releases-915506-2005/09/09
// Removed HandleUnexpectedExceptions infrastructure
/// <summary>
///
/// internal Entry point in msh console host implementation
///
/// </summary>
///
/// <param name="configuration">
/// Configuration information to use for creating runspace.
/// </param>
///
/// <param name="bannerText">
/// Banner text to be displayed by ConsoleHost
/// </param>
///
/// <param name="helpText">
/// Help text for minishell. This is displayed on 'minishell -?'.
/// </param>
///
/// <param name="preStartWarning">
///
/// Warning occurred prior to this point, for example, a snap-in fails to load beforehand.
/// This string will be printed out.
///
/// </param>
/// <param name = "args">
///
/// Command line parameters to powershell.exe
///
/// </param>
/// <returns>
///
/// The exit code for the shell.
///
/// NTRAID#Windows OS Bugs-1036968-2005/01/20-sburns The behavior here is related to monitor work. The low word of the
/// exit code is available for the user. The high word is reserved for the shell and monitor.
///
/// The shell process needs to return:
///
/// - if the shell.exe fails init, 0xFFFF0000
/// - if the exit keyword is called with no parameter at the point of top-level prompt, 0x80000000 (e.g. 0 with the high
/// bit set)
/// - if the exit keyword is called with any int param less than or equal to 0xFFFF, then that int masked with the high
/// bit set. e.g. "exit 3" results in 0x80000003
/// - if the script ends (in the case of msh -command or msh -commandfile), then 0x80000000.
/// - if ctrl-break is pressed, with 0xFFFE0000
/// - if the shell.exe is passed a bad command-line parameter, with 0xFFFD0000.
/// - if the shell.exe crashes, with 0x00000000
///
/// The monitor process gets the exit code. If the high bit is set, then the shell process exited normally (though
/// possibly due to an error). If not, the shell process crashed. If the shell.exe exit code is x00000000 (crashed)
/// or 0xFFFE0000 (user hit ctrl-break), the monitor should restart the shell.exe. Otherwise, the monitor should exit
/// with the same exit code as the shell.exe.
///
/// Anyone checking the exit code of the shell or monitor can mask off the hiword to determine the exit code passed
/// by the script that the shell last executed.
///
/// </returns>
internal static int Start(
RunspaceConfiguration configuration,
string bannerText,
string helpText,
string preStartWarning,
string[] args)
{
#if DEBUG
if (Environment.GetEnvironmentVariable("POWERSHELL_DEBUG_STARTUP") != null)
{
while (!System.Diagnostics.Debugger.IsAttached)
{
Thread.Sleep(1000);
}
}
#endif
try
{
var profileDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) +
@"\Microsoft\Windows\PowerShell";
if (!Directory.Exists(profileDir))
{
Directory.CreateDirectory(profileDir);
}
ClrFacade.SetProfileOptimizationRoot(profileDir);
}
catch
{
// It's safe to ignore errors, the guarded code is just there to try and
// improve startup performance.
}
uint exitCode = ExitCodeSuccess;
System.Threading.Thread.CurrentThread.Name = "ConsoleHost main thread";
theConsoleHost = ConsoleHost.CreateSingletonInstance(configuration);
theConsoleHost.BindBreakHandler();
PSHost.IsStdOutputRedirected = Console.IsOutputRedirected;
if (args == null)
{
args = new string[0];
}
if (!string.IsNullOrEmpty(preStartWarning))
{
theConsoleHost.UI.WriteWarningLine(preStartWarning);
}
try
{
cpp = new CommandLineParameterParser(theConsoleHost, theConsoleHost.ver, bannerText, helpText);
string[] tempArgs = new string[args.GetLength(0)];
args.CopyTo(tempArgs, 0);
cpp.Parse(tempArgs);
// Servermode parameter validation check.
if ((cpp.ServerMode && cpp.NamedPipeServerMode) || (cpp.ServerMode && cpp.SocketServerMode) || (cpp.NamedPipeServerMode && cpp.SocketServerMode))
{
tracer.TraceError("Conflicting server mode parameters, parameters must be used exclusively.");
theConsoleHost.ui.WriteErrorLine(ConsoleHostStrings.ConflictingServerModeParameters);
unchecked
{
return (int)ExitCodeBadCommandLineParameter;
}
}
if (cpp.ServerMode)
{
ClrFacade.StartProfileOptimization("StartupProfileData-ServerMode");
System.Management.Automation.Remoting.Server.OutOfProcessMediator.Run(cpp.InitialCommand);
exitCode = 0;
}
else if (cpp.NamedPipeServerMode)
{
ClrFacade.StartProfileOptimization("StartupProfileData-NamedPipeServerMode");
System.Management.Automation.Remoting.RemoteSessionNamedPipeServer.RunServerMode(
cpp.ConfigurationName);
exitCode = 0;
}
else if (cpp.SocketServerMode)
{
ClrFacade.StartProfileOptimization("StartupProfileData-SocketServerMode");
System.Management.Automation.Remoting.Server.HyperVSocketMediator.Run(cpp.InitialCommand,
cpp.ConfigurationName);
exitCode = 0;
}
else
{
ClrFacade.StartProfileOptimization(
theConsoleHost.LoadPSReadline()
? "StartupProfileData-Interactive"
: "StartupProfileData-NonInteractive");
exitCode = theConsoleHost.Run(cpp, !string.IsNullOrEmpty(preStartWarning));
}
}
finally
{
TelemetryAPI.ReportExitTelemetry(theConsoleHost);
theConsoleHost.Dispose();
}
unchecked
{
return (int)exitCode;
}
}
private static CommandLineParameterParser cpp;
/// <summary>
///
/// The break handler for the program. Dispatches a break event to the current Executor.
///
/// </summary>
/// <param name="signal"></param>
/// <returns></returns>
private static bool MyBreakHandler(ConsoleControl.ConsoleBreakSignal signal)
{
switch (signal)
{
case ConsoleControl.ConsoleBreakSignal.CtrlBreak:
// Break into script debugger.
BreakIntoDebugger();
return true;
// Run the break handler...
case ConsoleControl.ConsoleBreakSignal.CtrlC:
SpinUpBreakHandlerThread(false);
return true;
case ConsoleControl.ConsoleBreakSignal.Logoff:
// Just ignore the logoff signal. This signal is sent to console
// apps running as service anytime *any* user logs off which means
// that PowerShell could't be used in servces/tasks if we didn't
// suppress this signal...
return true;
case ConsoleControl.ConsoleBreakSignal.Close:
case ConsoleControl.ConsoleBreakSignal.Shutdown:
SpinUpBreakHandlerThread(true);
return false;
default:
// Log as much sqm data as possible before we exit.
SpinUpBreakHandlerThread(true);
return false;
}
}
private static bool BreakIntoDebugger()
{
ConsoleHost host = ConsoleHost.SingletonInstance;
Debugger debugger = null;
lock (host.hostGlobalLock)
{
if (host.runspaceRef.Runspace != null &&
host.runspaceRef.Runspace.GetCurrentlyRunningPipeline() != null)
{
debugger = host.runspaceRef.Runspace.Debugger;
}
}
if (debugger != null)
{
debugger.SetDebuggerStepMode(true);
return true;
}
return false;
}
/// <summary>
///
/// Spin up a new thread to cancel the current pipline. This will allow subsequent break interrupts to be received even
/// if the cancellation is blocked (which can be the case when the pipeline blocks and nothing implements Cmdlet.Stop
/// properly). That is because the OS will not inject another thread when a break event occurs if one has already been
/// injected and is running.
///
/// </summary>
/// <param name="shouldEndSession">
///
/// if true, then flag the parent ConsoleHost that it should shutdown the session. If false, then only the current
/// executing instance is stopped.
///
///</param>
private static void SpinUpBreakHandlerThread(bool shouldEndSession)
{
ConsoleHost host = ConsoleHost.SingletonInstance;
Thread bht = null;
lock (host.hostGlobalLock)
{
if (host.isCtrlCDisabled)
{
return;
}
bht = host.breakHandlerThread;
if (!host.ShouldEndSession && shouldEndSession)
{
host.ShouldEndSession = shouldEndSession;
}
// Creation of the tread and starting it should be an atomic operation.
// otherwise the code in Run method can get instance of the breakhandlerThread
// after it is created and before started and call join on it. This will result
// in ThreadStateException.
// NTRAID#Windows OutofBand Bugs-938289-2006/07/27-hiteshr
if (bht == null)
{
// we're not already running HandleBreak on a separate thread, so run it now.
host.breakHandlerThread = new Thread(new ThreadStart(ConsoleHost.HandleBreak));
host.breakHandlerThread.Name = "ConsoleHost.HandleBreak";
host.breakHandlerThread.Start();
}
}
}
private static void HandleBreak()
{
ConsoleHost consoleHost = theConsoleHost;
if (consoleHost != null)
{
if (consoleHost.InDebugMode)
{
// Only stop a running user command, ignore prompt evaluation.
if (consoleHost.DebuggerCanStopCommand)
{
// Cancel any executing debugger command if in debug mode.
try
{
consoleHost.Runspace.Debugger.StopProcessCommand();
}
catch (Exception e)
{
CommandProcessorBase.CheckForSevereException(e);
}
}
}
else
{
// Cancel the reconnected debugged running pipeline command.
if (!StopPipeline(consoleHost.runningCmd))
{
Executor.CancelCurrentExecutor();
}
}
if (consoleHost.ShouldEndSession)
{
var runspaceRef = ConsoleHost.SingletonInstance.runspaceRef;
if (runspaceRef != null)
{
var runspace = runspaceRef.Runspace;
if (runspace != null)
{
runspace.Close();
}
}
}
}
// call the console APIs directly, instead of ui.rawui.FlushInputHandle, as ui may be finalized
// already if this thread is lagging behind the main thread.
ConsoleHandle handle = ConsoleControl.GetConioDeviceHandle();
ConsoleControl.FlushConsoleInputBuffer(handle);
ConsoleHost.SingletonInstance.breakHandlerThread = null;
}
private static bool StopPipeline(Pipeline cmd)
{
if (cmd != null &&
(cmd.PipelineStateInfo.State == PipelineState.Running ||
cmd.PipelineStateInfo.State == PipelineState.Disconnected))
{
try
{
cmd.StopAsync();
return true;
}
catch (Exception e)
{
CommandProcessorBase.CheckForSevereException(e);
}
}
return false;
}
/// <summary>
/// Create single instance of ConsoleHost.
/// </summary>
internal static ConsoleHost CreateSingletonInstance(RunspaceConfiguration configuration)
{
Dbg.Assert(theConsoleHost == null, "CreateSingletonInstance should not be called multiple times");
theConsoleHost = new ConsoleHost(configuration);
return theConsoleHost;
}
internal static ConsoleHost SingletonInstance
{
get
{
Dbg.Assert(theConsoleHost != null, "CreateSingletonInstance should be called before calling this method");
return theConsoleHost;
}
}
#endregion static methods
#region overrides
/// <summary>
///
/// See base class
///
/// </summary>
/// <value></value>
/// <exception/>
public override string Name
{
get
{
const string myName = "ConsoleHost";
// const, no lock needed.
return myName;
}
}
/// <summary>
///
/// See base class
///
/// </summary>
/// <value></value>
/// <exception/>
public override System.Version Version
{
get
{
// const, no lock needed.
return ver;
}
}
/// <summary>
///
/// See base class
///
/// </summary>
/// <value></value>
/// <exception/>
public override System.Guid InstanceId
{
get
{
// const, no lock needed.
return id;
}
}
/// <summary>
///
/// See base class
///
/// </summary>
/// <value></value>
/// <exception/>
public override PSHostUserInterface UI
{
get
{
Dbg.Assert(ui != null, "ui should have been allocated in ctor");
return ui;
}
}
/// <summary>
/// See base class.
/// </summary>
public void PushRunspace(Runspace newRunspace)
{
if (this.runspaceRef == null) { return; }
RemoteRunspace remoteRunspace = newRunspace as RemoteRunspace;
Dbg.Assert(remoteRunspace != null, "Expected remoteRunspace != null");
remoteRunspace.StateChanged += new EventHandler<RunspaceStateEventArgs>(HandleRemoteRunspaceStateChanged);
// Unsubscribe the local session debugger.
if (this.runspaceRef.Runspace.Debugger != null)
{
this.runspaceRef.Runspace.Debugger.DebuggerStop -= OnExecutionSuspended;
}
// Subscribe to debugger stop event.
if (remoteRunspace.Debugger != null)
{
remoteRunspace.Debugger.DebuggerStop += OnExecutionSuspended;
}
// Connect a disconnected command.
this.runningCmd = Microsoft.PowerShell.Commands.EnterPSSessionCommand.ConnectRunningPipeline(remoteRunspace);
// Push runspace.
this.runspaceRef.Override(remoteRunspace, hostGlobalLock, out _isRunspacePushed);
RunspacePushed.SafeInvoke(this, EventArgs.Empty);
if (this.runningCmd != null)
{
Microsoft.PowerShell.Commands.EnterPSSessionCommand.ContinueCommand(
remoteRunspace,
this.runningCmd,
this,
this.inDebugMode,
this.runspaceRef.OldRunspace.ExecutionContext);
}
this.runningCmd = null;
}
/// <summary>
/// Handles state changed event of the remote runspace. If the remote runspace
/// gets into a broken or closed state, writes a message and pops out the
/// runspace
/// </summary>
/// <param name="sender">not sure</param>
/// <param name="eventArgs">arguments describing this event</param>
void HandleRemoteRunspaceStateChanged(object sender, RunspaceStateEventArgs eventArgs)
{
RunspaceState state = eventArgs.RunspaceStateInfo.State;
switch (state)
{
case RunspaceState.Opening:
case RunspaceState.Opened:
{
return;
}
case RunspaceState.Closing:
case RunspaceState.Closed:
case RunspaceState.Broken:
case RunspaceState.Disconnected:
{
PopRunspace();
}
break;
}
}
/// <summary>
/// See base class.
/// </summary>
public void PopRunspace()
{
if (this.runspaceRef == null ||
!this.runspaceRef.IsRunspaceOverridden )
{
return;
}
if (this.inPushedConfiguredSession)
{
// For configured endpoint sessions, end session when configured runspace is popped.
this.ShouldEndSession = true;
}
if (this.runspaceRef.Runspace.Debugger != null)
{
// Unsubscribe pushed runspace debugger.
this.runspaceRef.Runspace.Debugger.DebuggerStop -= OnExecutionSuspended;
StopPipeline(this.runningCmd);
if (this.InDebugMode)
{
ExitDebugMode(DebuggerResumeAction.Continue);
}
}
this.runningCmd = null;
lock (hostGlobalLock)
{
this.runspaceRef.Revert();
_isRunspacePushed = false;
}
// Re-subscribe local runspace debugger.
this.runspaceRef.Runspace.Debugger.DebuggerStop += OnExecutionSuspended;
// raise events outside the lock
RunspacePopped.SafeInvoke(this, EventArgs.Empty);
}
/// <summary>
/// True if a runspace is pushed; false otherwise.
/// </summary>
public bool IsRunspacePushed
{
get
{
return _isRunspacePushed;
}
}
private bool _isRunspacePushed = false;
/// <summary>
/// Returns the current runspace associated with this host.
/// </summary>
public Runspace Runspace
{
get
{
if (this.RunspaceRef == null) { return null; }
return this.RunspaceRef.Runspace;
}
}
internal LocalRunspace LocalRunspace
{
get
{
if (_isRunspacePushed)
{
return RunspaceRef.OldRunspace as LocalRunspace;
}
if (RunspaceRef == null) { return null; }
return RunspaceRef.Runspace as LocalRunspace;
}
}
public class ConsoleColorProxy
{
private ConsoleHostUserInterface ui;
public ConsoleColorProxy(ConsoleHostUserInterface ui)
{
if (ui == null) throw new ArgumentNullException("ui");
this.ui = ui;
}
public ConsoleColor ErrorForegroundColor
{
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
get { return ui.ErrorForegroundColor; }
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
set { ui.ErrorForegroundColor = value; }
}
public ConsoleColor ErrorBackgroundColor
{
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
get { return ui.ErrorBackgroundColor; }
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
set { ui.ErrorBackgroundColor = value; }
}
public ConsoleColor WarningForegroundColor
{
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
get { return ui.WarningForegroundColor; }
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
set { ui.WarningForegroundColor = value; }
}
public ConsoleColor WarningBackgroundColor
{
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
get { return ui.WarningBackgroundColor; }
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
set { ui.WarningBackgroundColor = value; }
}
public ConsoleColor DebugForegroundColor
{
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
get { return ui.DebugForegroundColor; }
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
set { ui.DebugForegroundColor = value; }
}
public ConsoleColor DebugBackgroundColor
{
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
get { return ui.DebugBackgroundColor; }
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
set { ui.DebugBackgroundColor = value; }
}
public ConsoleColor VerboseForegroundColor
{
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
get { return ui.VerboseForegroundColor; }
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
set { ui.VerboseForegroundColor = value; }
}
public ConsoleColor VerboseBackgroundColor
{
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
get { return ui.VerboseBackgroundColor; }
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
set { ui.VerboseBackgroundColor = value; }
}
public ConsoleColor ProgressForegroundColor
{
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
get { return ui.ProgressForegroundColor; }
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
set { ui.ProgressForegroundColor = value; }
}
public ConsoleColor ProgressBackgroundColor
{
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
get { return ui.ProgressBackgroundColor; }
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
set { ui.ProgressBackgroundColor = value; }
}
}
/// <summary>
/// Return the actual console host object so that the user can get at
/// the unproxied methods.
/// </summary>
public override PSObject PrivateData
{
get {
if (ui == null) return null;
if (consoleColorProxy == null)
consoleColorProxy = PSObject.AsPSObject(new ConsoleColorProxy(ui));
return consoleColorProxy;
}
}
private PSObject consoleColorProxy;
/// <summary>
///
/// See base class
///
/// </summary>
/// <value></value>
/// <exception/>
public override System.Globalization.CultureInfo CurrentCulture
{
get
{
lock (hostGlobalLock)
{
#if !CORECLR
return NativeCultureResolver.Culture;
#else
return CultureInfo.CurrentCulture;
#endif
}
}
}
/// <summary>
///
/// See base class
///
/// </summary>
/// <value></value>
/// <exception/>
public override System.Globalization.CultureInfo CurrentUICulture
{
get
{
lock (hostGlobalLock)
{
#if !CORECLR
return NativeCultureResolver.UICulture;
#else
return CultureInfo.CurrentUICulture;
#endif
}
}
}
/// <summary>
///
/// </summary>
/// <exception/>
public override void SetShouldExit(int exitCode)
{
lock (hostGlobalLock)
{
// Check for the pushed runspace scenario.
if (this.IsRunspacePushed)
{
this.PopRunspace();
}
else if (this.inDebugMode)
{
ExitDebugMode(DebuggerResumeAction.Continue);
}
else
{
setShouldExitCalled = true;
exitCodeFromRunspace = exitCode;
ShouldEndSession = true;
}
}
}
/// <summary>
///
/// If an input loop is running, then starts a new, nested input loop. If an input loop is not running,
/// throws an exception.
///
/// </summary>
/// <exception cref="InvalidOperationException">
///
/// If a nested prompt is entered while the host is not running at least one prompt loop.
///
/// </exception>
public override void EnterNestedPrompt()
{
// save the old Executor, then clear it so that a break does not cancel the pipeline from which this method
// might be called.
Executor oldCurrent = Executor.CurrentExecutor;
try
{
// this assignment is threadsafe -- protected in CurrentExecutor property
Executor.CurrentExecutor = null;
lock (hostGlobalLock)
{
this.isNested = oldCurrent != null || this.ui.IsCommandCompletionRunning;
}
InputLoop.RunNewInputLoop(this, isNested);
}
finally
{
Executor.CurrentExecutor = oldCurrent;
}
}
/// <summary>
///
/// See base class
///
/// </summary>
/// <exception cref="InvalidOperationException">
///
/// If there is no nested prompt.
///
/// </exception>
public override void ExitNestedPrompt()
{
lock (hostGlobalLock)
{
this.isNested = InputLoop.ExitCurrentLoop();
}
}
/// <summary>
///
/// See base class
///
/// </summary>
public override void NotifyBeginApplication()
{
lock (hostGlobalLock)
{
++beginApplicationNotifyCount;
if (beginApplicationNotifyCount == 1)
{
// save the window title when first notified.
savedWindowTitle = ui.RawUI.WindowTitle;
}
}
}
/// <summary>
///
/// See base class
///
/// <seealso cref="NotifyBeginApplication"/>
/// </summary>
public override void NotifyEndApplication()
{
lock (hostGlobalLock)
{
Dbg.Assert(beginApplicationNotifyCount > 0, "Not running an executable - NotifyBeginApplication was not called!");
--beginApplicationNotifyCount;
if (beginApplicationNotifyCount == 0)
{
// restore the window title when the last application started has ended.
ui.RawUI.WindowTitle = savedWindowTitle;
}
}
}
bool IHostProvidesTelemetryData.HostIsInteractive
{
get
{
return !cpp.NonInteractive && !cpp.AbortStartup &&
((cpp.InitialCommand == null && cpp.File == null) || cpp.NoExit);
}
}
double IHostProvidesTelemetryData.ProfileLoadTimeInMS { get { return _profileLoadTimeInMS; } }
double IHostProvidesTelemetryData.ReadyForInputTimeInMS { get { return _readyForInputTimeInMS; } }
int IHostProvidesTelemetryData.InteractiveCommandCount { get { return _interactiveCommandCount; } }
private double _profileLoadTimeInMS;
private double _readyForInputTimeInMS;
private int _interactiveCommandCount;
#endregion overrides
#region non-overrides
/// <summary>
///
/// Constructs a new instance
///
/// </summary>
internal ConsoleHost(RunspaceConfiguration configuration)
{
ClrFacade.SetCurrentThreadUiCulture(this.CurrentUICulture);
ClrFacade.SetCurrentThreadCulture(this.CurrentCulture);
// BUG: 610329. Tell PowerShell engine to apply console
// related properties while launching Pipeline Execution
// Thread.
base.ShouldSetThreadUILanguageToZero = true;
this.inDebugMode = false;
this.displayDebuggerBanner = true;
this.configuration = configuration;
this.ui = new ConsoleHostUserInterface(this);
this.consoleWriter = new ConsoleTextWriter(ui);
#if !CORECLR // CurrentDomain.UnhandledException not supported on CoreCLR
UnhandledExceptionEventHandler handler = new UnhandledExceptionEventHandler(UnhandledExceptionHandler);
AppDomain.CurrentDomain.UnhandledException += handler;
#endif
}
private void BindBreakHandler()
{
breakHandlerGcHandle = GCHandle.Alloc(new ConsoleControl.BreakHandler(MyBreakHandler));
ConsoleControl.AddBreakHandler((ConsoleControl.BreakHandler)breakHandlerGcHandle.Target);
}
#if !CORECLR // Not used on NanoServer: CurrentDomain.UnhandledException not supported on CoreCLR
private void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args)
{
// FYI: sender is a reference to the source app domain
// The CLR will shut down the app as soon as this handler is complete, but just in case, we want
// to exit at our next opportunity.
shouldEndSession = true;
Exception e = null;
if (args != null)
{
e = (Exception)args.ExceptionObject;
}
ui.WriteLine();
ui.Write(
ConsoleColor.Red,
ui.RawUI.BackgroundColor,
ConsoleHostStrings.UnhandledExceptionShutdownMessage);
ui.WriteLine();
}
#endif
/// <summary>
///
/// Finalizes the instance
///
/// </summary>
~ConsoleHost()
{
Dispose(false);
}
/// <summary>
///
/// Disposes of this instance, per the IDisposable pattern
///
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool isDisposingNotFinalizing)
{
if (!isDisposed)
{
Dbg.Assert(breakHandlerGcHandle != null, "break handler should be set");
ConsoleControl.RemoveBreakHandler();
breakHandlerGcHandle.Free();
if (isDisposingNotFinalizing)
{
if (IsTranscribing)
{
StopTranscribing();
}
if (outputSerializer != null)
{
outputSerializer.End();
}
if (errorSerializer != null)
{
errorSerializer.End();
}
if (runspaceRef != null)
{
// NTRAID#Windows Out Of Band Releases-925297-2005/12/14
try
{
runspaceRef.Runspace.Dispose();
}
catch (InvalidRunspaceStateException)
{
}
}
runspaceRef = null;
ui = null;
}
}
isDisposed = true;
}
/// <summary>
///
/// Indicates if the session should be terminated or not. Typically set by the break handler for Close, Logoff, and
/// Shutdown events. Note that the only valid transition for this property is from false to true: it is not legal to
/// try to set it to false after is was set to true.
///
/// </summary>
/// <value>
///
/// true to shut down the session. false is only allowed if the property is already false.
///
/// </value>
internal bool ShouldEndSession
{
// This might get called from the main thread, or from the pipeline thread, or from a break handler thread.
get
{
bool result = false;
lock (hostGlobalLock)
{
result = shouldEndSession;
}
return result;
}
set
{
lock (hostGlobalLock)
{
// If ShouldEndSession is already true, you can't set it back
Dbg.Assert(shouldEndSession != true || value != false,
"ShouldEndSession can only be set from false to true");
shouldEndSession = value;
}
}
}
/// <summary>
///
/// The Runspace ref object being used by this Host instance. A host only opens one Runspace.
///
/// </summary>
/// <value></value>
internal RunspaceRef RunspaceRef
{
get
{
return runspaceRef;
}
}
internal WrappedSerializer.DataFormat OutputFormat
{
get
{
return outputFormat;
}
}
internal WrappedSerializer.DataFormat InputFormat
{
get
{
return inputFormat;
}
}
internal WrappedDeserializer.DataFormat ErrorFormat
{
get
{
WrappedDeserializer.DataFormat format = outputFormat;
//If this shell is invoked in minishell interop mode and error is redirected,
//always write data in error stream in xml format.
if (IsInteractive == false && Console.IsErrorRedirected && wasInitialCommandEncoded)
{
format = Serialization.DataFormat.XML;
}
return format;
}
}
internal bool IsRunningAsync
{
get
{
return !IsInteractive && ((OutputFormat != Serialization.DataFormat.Text) || Console.IsInputRedirected);
}
}
internal bool IsNested
{
get { return this.isNested; }
}
internal WrappedSerializer OutputSerializer
{
get
{
if (outputSerializer == null)
{
outputSerializer =
new WrappedSerializer(
outputFormat,
"Output",
Console.IsOutputRedirected ? Console.Out : ConsoleTextWriter);
}
return outputSerializer;
}
}
internal WrappedSerializer ErrorSerializer
{
get
{
if (errorSerializer == null)
{
errorSerializer =
new WrappedSerializer(
ErrorFormat,
"Error",
Console.IsErrorRedirected ? Console.Error : ConsoleTextWriter);
}
return errorSerializer;
}
}
internal bool IsInteractive
{
get
{
// we're running interactive if we're in a prompt loop, and we're not reading keyboard input from stdin.
return isRunningPromptLoop && !ui.ReadFromStdin;
}
}
internal TextWriter ConsoleTextWriter
{
get
{
Dbg.Assert(consoleWriter != null, "consoleWriter should have been initialized");
return consoleWriter;
}
}
/// <summary>
///
/// The main run loop of the program: processes command line parameters, and starts up a runspace.
///
/// </summary>
///
/// <param name="cpp">
/// Commandline parameter parser. The commandline parameter parser is expected to parse all the
/// arguments before calling this method.
/// </param>
///
/// <param name="isPrestartWarned">
/// Is there any warning at startup
/// </param>
///
/// <returns>
///
/// The process exit code to be returned by Main.
///
/// </returns>
private uint Run(CommandLineParameterParser cpp, bool isPrestartWarned)
{
Dbg.Assert(null != cpp, "CommandLine parameter parser cannot be null.");
uint exitCode = ExitCodeSuccess;
do
{
runspaceInitTracer.WriteLine("starting parse of command line parameters");
exitCode = ExitCodeSuccess;
if (!string.IsNullOrEmpty(cpp.InitialCommand) && isPrestartWarned)
{
tracer.TraceError("Start up warnings made command \"{0}\" not executed", cpp.InitialCommand);
string msg = StringUtil.Format(ConsoleHostStrings.InitialCommandNotExecuted, cpp.InitialCommand);
ui.WriteErrorLine(msg);
exitCode = ExitCodeInitFailure;
break;
}
if (cpp.AbortStartup)
{
tracer.WriteLine("processing of cmdline args failed, exiting");
exitCode = cpp.ExitCode;
break;
}
outputFormat = cpp.OutputFormat;
inputFormat = cpp.InputFormat;
wasInitialCommandEncoded = cpp.WasInitialCommandEncoded;
ui.ReadFromStdin = cpp.ExplicitReadCommandsFromStdin || Console.IsInputRedirected;
ui.NoPrompt = cpp.NoPrompt;
ui.ThrowOnReadAndPrompt = cpp.ThrowOnReadAndPrompt;
noExit = cpp.NoExit;
// See if we need to change the process-wide execution
// policy
if(! String.IsNullOrEmpty(cpp.ExecutionPolicy))
{
ExecutionPolicy executionPolicy = SecuritySupport.ParseExecutionPolicy(cpp.ExecutionPolicy);
SecuritySupport.SetExecutionPolicy(ExecutionPolicyScope.Process, executionPolicy, null);
}
// NTRAID#Windows Out Of Band Releases-915506-2005/09/09
// Removed HandleUnexpectedExceptions infrastructure
exitCode = DoRunspaceLoop(cpp.InitialCommand, cpp.SkipProfiles, cpp.Args, cpp.StaMode, cpp.ImportSystemModules, cpp.ConfigurationName);
}
while (false);
return exitCode;
}
/// <summary>
/// This method is reatined to make V1 tests compatible with V2 as signature of this method
/// is slighlty changed in v2.
/// </summary>
/// <param name="bannerText"></param>
/// <param name="helpText"></param>
/// <param name="isPrestartWarned"></param>
/// <param name="args"></param>
/// <returns></returns>
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
private uint Run(string bannerText, string helpText, bool isPrestartWarned, string[] args)
{
cpp = new CommandLineParameterParser(this, ver, bannerText, helpText);
cpp.Parse(args);
return Run(cpp, isPrestartWarned);
}
/// <summary>
///
/// Loops over the Host's sole Runspace; opens the runspace, initializes it, then recycles it if the Runspace fails.
///
/// </summary>
/// <returns>
///
/// The process exit code to be returned by Main.
///
/// </returns>
private uint DoRunspaceLoop(string initialCommand, bool skipProfiles, Collection<CommandParameter> initialCommandArgs, bool staMode,
bool importSystemModules, string configurationName)
{
ExitCode = ExitCodeSuccess;
while (!ShouldEndSession)
{
RunspaceCreationEventArgs args = new RunspaceCreationEventArgs(initialCommand, skipProfiles, staMode, importSystemModules, configurationName, initialCommandArgs);
CreateRunspace(args);
if (ExitCode == ExitCodeInitFailure) { break; }
if (!noExit)
{
// Wait for runspace to open, init, and run init script before
// setting ShouldEndSession, to allow debugger to work.
ShouldEndSession = true;
}
else
{
// Start nested prompt loop.
EnterNestedPrompt();
}
if (setShouldExitCalled)
{
ExitCode = unchecked((uint)exitCodeFromRunspace);
}
else
{
Executor exec = new Executor(this, false, false);
bool dollarHook = exec.ExecuteCommandAndGetResultAsBool("$global:?") ?? false;
if (dollarHook && (lastRunspaceInitializationException == null))
{
ExitCode = ExitCodeSuccess;
}
else
{
ExitCode = ExitCodeSuccess | 0x1;
}
}
runspaceRef.Runspace.Close();
runspaceRef = null;
if (staMode) // don't recycle the Runspace in STA mode
{
ShouldEndSession = true;
}
}
return ExitCode;
}
private Exception InitializeRunspaceHelper(string command, Executor exec, Executor.ExecutionOptions options)
{
Dbg.Assert(!String.IsNullOrEmpty(command), "command should have a value");
Dbg.Assert(exec != null, "non-null Executor instance needed");
runspaceInitTracer.WriteLine("running command {0}", command);
Exception e = null;
if (IsRunningAsync)
{
exec.ExecuteCommandAsync(command, out e, options);
}
else
{
exec.ExecuteCommand(command, out e, options);
}
if (e != null)
{
ReportException(e, exec);
}
return e;
}
private void CreateRunspace(object runspaceCreationArgs)
{
RunspaceCreationEventArgs args = null;
try
{
args = runspaceCreationArgs as RunspaceCreationEventArgs;
Dbg.Assert(args != null, "Event Arguments to CreateRunspace should not be null");
DoCreateRunspace(args.InitialCommand, args.SkipProfiles, args.StaMode, args.ImportSystemModules, args.ConfigurationName, args.InitialCommandArgs);
}
catch (ConsoleHostStartupException startupException)
{
ReportExceptionFallback(startupException.InnerException, startupException.Message);
ExitCode = ExitCodeInitFailure;
}
}
/// <summary>
/// This method is here only to make V1 tests compatible with V2. DO NOT USE THIS FUNCTION! Use DoCreateRunspace instead
/// </summary>
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
private void InitializeRunspace(string initialCommand, bool skipProfiles, Collection<CommandParameter> initialCommandArgs)
{
DoCreateRunspace(initialCommand, skipProfiles, staMode: false, importSystemModules: false, configurationName: null, initialCommandArgs: initialCommandArgs);
}
private bool LoadPSReadline()
{
// Don't load PSReadline if:
// * we don't think the process will be interactive, e.g. -command or -file
// - exception: when -noexit is specified, we will be interactive after the command/file finishes
// * -noniteractive: this should be obvious, they've asked that we don't every prompt
//
// Note that PSReadline doesn't support redirected stdin/stdout, but we don't check that here because
// a future version might, and we should automatically load it at that unknown point in the future.
// PSReadline will ideally fall back to Console.ReadLine or whatever when stdin/stdout is redirected.
return ((cpp.InitialCommand == null && cpp.File == null) || cpp.NoExit) && !cpp.NonInteractive;
}
/// <summary>
///
/// Opens and Initializes the Host's sole Runspace. Processes the startup scripts and runs any command passed on the
/// command line.
///
/// </summary>
//private void CreateRunspace(string initialCommand, bool skipProfiles, bool staMode, Collection<CommandParameter> initialCommandArgs)
private void DoCreateRunspace(string initialCommand, bool skipProfiles, bool staMode, bool importSystemModules, string configurationName, Collection<CommandParameter> initialCommandArgs)
{
Dbg.Assert(runspaceRef == null, "runspace should be null");
#if !DEBUG
Dbg.Assert(configuration != null, "configuration should be set");
#endif
runspaceInitTracer.WriteLine("Calling RunspaceFactory.CreateRunspace");
try
{
Runspace consoleRunspace = null;
bool psReadlineFailed = false;
// Use InitialSessionState if available.
if (DefaultInitialSessionState != null)
{
// Load PSReadline by default unless there is no use:
// - we're running a command/file and just exiting
// - stdin is redirected by a parent process
// - we're not interactive
// - we're explicitly reading from stdin (the '-' argument)
// It's also important to have a scenario where PSReadline is not loaded so it can be updated, e.g.
// powershell -command "Update-Module PSReadline"
// This should work just fine as long as no other instances of PowerShell are running.
ReadOnlyCollection<Microsoft.PowerShell.Commands.ModuleSpecification> defaultImportModulesList = null;
if (LoadPSReadline())
{
// Create and open Runspace with PSReadline.
defaultImportModulesList = DefaultInitialSessionState.Modules;
DefaultInitialSessionState.ImportPSModule(new[] { "PSReadline" });
consoleRunspace = RunspaceFactory.CreateRunspace(this, DefaultInitialSessionState);
try
{
OpenConsoleRunspace(consoleRunspace, staMode);
}
catch (Exception e)
{
CommandProcessorBase.CheckForSevereException(e);
consoleRunspace = null;
psReadlineFailed = true;
}
}
if (consoleRunspace == null)
{
if (psReadlineFailed)
{
// Try again but without importing the PSReadline module.
DefaultInitialSessionState.ClearPSModules();
DefaultInitialSessionState.ImportPSModule(defaultImportModulesList);
}
consoleRunspace = RunspaceFactory.CreateRunspace(this, DefaultInitialSessionState);
OpenConsoleRunspace(consoleRunspace, staMode);
}
}
else
{
consoleRunspace = RunspaceFactory.CreateRunspace(this, this.configuration);
OpenConsoleRunspace(consoleRunspace, staMode);
}
runspaceRef = new RunspaceRef(consoleRunspace);
if (psReadlineFailed)
{
// Notify the user that PSReadline could not be loaded.
Console.Error.WriteLine(ConsoleHostStrings.CannotLoadPSReadline);
}
}
catch (Exception e)
{
// no need to do CheckForSevereException here
// since the ConsoleHostStartupException is uncaught
// higher in the call stack and the whole process
// will exit soon
throw new ConsoleHostStartupException(ConsoleHostStrings.ShellCannotBeStarted, e);
}
finally
{
// Stop PerfTrack
PSEtwLog.LogOperationalInformation(PSEventId.Perftrack_ConsoleStartupStop, PSOpcode.WinStop,
PSTask.PowershellConsoleStartup, PSKeyword.UseAlwaysOperational);
}
// Record how long it took from process start to runspace open for telemetry.
_readyForInputTimeInMS = (DateTime.Now - Process.GetCurrentProcess().StartTime).TotalMilliseconds;
DoRunspaceInitialization(importSystemModules, skipProfiles, initialCommand, configurationName, initialCommandArgs);
}
private void OpenConsoleRunspace(Runspace runspace, bool staMode)
{
// staMode will have folowing values:
// On FullPS: 'true'/'false' = default('true'=STA) + possibility of overload through cmdline parameter '-mta'
// On NanoPS: always 'false' = default('false'=MTA) + NO possibility of overload through cmdline parameter '-mta'
// ThreadOptions should match on FullPS and NanoPS for corresponding ApartmentStates.
if (staMode)
{
// we can't change ApartmentStates on CoreCLR
#if !CORECLR
runspace.ApartmentState = ApartmentState.STA;
#endif
runspace.ThreadOptions = PSThreadOptions.ReuseThread;
}
runspace.EngineActivityId = EtwActivity.GetActivityId();
runspaceInitTracer.WriteLine("Calling Runspace.Open");
runspace.Open();
}
private void DoRunspaceInitialization(bool importSystemModules, bool skipProfiles, string initialCommand, string configurationName, Collection<CommandParameter> initialCommandArgs)
{
if (runspaceRef.Runspace.Debugger != null)
{
runspaceRef.Runspace.Debugger.SetDebugMode(DebugModes.LocalScript | DebugModes.RemoteScript);
runspaceRef.Runspace.Debugger.DebuggerStop += this.OnExecutionSuspended;
}
Executor exec = new Executor(this, false, false);
// Run import system modules command
if (importSystemModules)
{
Exception exception = InitializeRunspaceHelper("ImportSystemModules", exec, Executor.ExecutionOptions.None);
}
if (!string.IsNullOrEmpty(configurationName))
{
// If an endpoint configuration is specified then create a loop-back remote runspace targeting
// the endpoint and push onto runspace ref stack. Ignore profile and configuration scripts.
try
{
RemoteRunspace remoteRunspace = HostUtilities.CreateConfiguredRunspace(configurationName, this);
remoteRunspace.ShouldCloseOnPop = true;
PushRunspace(remoteRunspace);
// Ensure that session ends when configured remote runspace is popped.
this.inPushedConfiguredSession = true;
}
catch (Exception e)
{
throw new ConsoleHostStartupException(ConsoleHostStrings.ShellCannotBeStarted, e);
}
}
else
{
// Run the built-in scripts
RunspaceConfigurationEntryCollection<ScriptConfigurationEntry> scripts = new RunspaceConfigurationEntryCollection<ScriptConfigurationEntry>();
if (configuration != null)
scripts = configuration.InitializationScripts;
if ((scripts == null) || (scripts.Count == 0))
{
runspaceInitTracer.WriteLine("There are no built-in scripts to run");
}
else
{
foreach (ScriptConfigurationEntry s in scripts)
{
runspaceInitTracer.WriteLine("Running script: '{0}'", s.Name);
// spec claims that Ctrl-C is not supposed to stop these.
try
{
isCtrlCDisabled = true;
Exception e = InitializeRunspaceHelper(s.Definition, exec, Executor.ExecutionOptions.AddOutputter);
if (e != null)
{
throw new ConsoleHostStartupException(ConsoleHostStrings.InitScriptFailed, e);
}
}
finally
{
isCtrlCDisabled = false;
}
}
}
// If -iss has been specified, then there won't be a runspace
// configuration to get the shell ID from, so we'll use the default...
string shellId = null;
if (configuration != null)
shellId = configuration.ShellId;
else
shellId = "Microsoft.PowerShell"; // TODO: what will happen for custom shells built using Make-Shell.exe
// If the system lockdown policy says "Enforce", do so. Do this after types / formatting, default functions, etc
// are loaded so that they are trusted. (Validation of their signatures is done in F&O)
if (SystemPolicy.GetSystemLockdownPolicy() == SystemEnforcementMode.Enforce)
{
runspaceRef.Runspace.ExecutionContext.LanguageMode = PSLanguageMode.ConstrainedLanguage;
}
string allUsersProfile = HostUtilities.GetFullProfileFileName(null, false);
string allUsersHostSpecificProfile = HostUtilities.GetFullProfileFileName(shellId, false);
string currentUserProfile = HostUtilities.GetFullProfileFileName(null, true);
string currentUserHostSpecificProfile = HostUtilities.GetFullProfileFileName(shellId, true);
// $PROFILE has to be set from the host
// Should be "per-user,host-specific profile.ps1"
// This should be set even if -noprofile is specified
runspaceRef.Runspace.SessionStateProxy.SetVariable("PROFILE",
HostUtilities.GetDollarProfile(
allUsersProfile,
allUsersHostSpecificProfile,
currentUserProfile,
currentUserHostSpecificProfile));
if (!skipProfiles)
{
// Run the profiles.
// Profiles are run in the following order:
// 1. host independent profile meant for all users
// 2. host specific profile meant for all users
// 3. host independent profile of the current user
// 4. host specific profile of the current user
var sw = new Stopwatch();
sw.Start();
RunProfile(allUsersProfile, exec);
RunProfile(allUsersHostSpecificProfile, exec);
RunProfile(currentUserProfile, exec);
RunProfile(currentUserHostSpecificProfile, exec);
sw.Stop();
var profileLoadTimeInMs = sw.ElapsedMilliseconds;
if (profileLoadTimeInMs > 500 && cpp.ShowBanner)
{
Console.Error.WriteLine(ConsoleHostStrings.SlowProfileLoadingMessage, profileLoadTimeInMs);
}
_profileLoadTimeInMS = profileLoadTimeInMs;
}
else
{
tracer.WriteLine("-noprofile option specified: skipping profiles");
}
}
// Startup is reported after possibly running the profile, but before running the initial command (or file)
// if one is specified.
TelemetryAPI.ReportStartupTelemetry(this);
// If a file was specified as the argument to run, then run it...
if (cpp != null && cpp.File != null)
{
string filePath = cpp.File;
tracer.WriteLine("running -file '{0}'", filePath);
Pipeline tempPipeline = exec.CreatePipeline();
Command c = new Command(filePath, false, false);
tempPipeline.Commands.Add(c);
if (initialCommandArgs != null)
{
// add the args passed to the command.
foreach (CommandParameter p in initialCommandArgs)
{
c.Parameters.Add(p);
}
}
// If we're not going to continue, then get the exit code out of the runspace and
// and indicate that it should be returned...
if (!noExit && !(this.Runspace is RemoteRunspace))
{
this.Runspace.ExecutionContext.ScriptCommandProcessorShouldRethrowExit = true;
}
Exception e1;
if (IsRunningAsync)
{
Executor.ExecutionOptions executionOptions = Executor.ExecutionOptions.AddOutputter;
Token[] tokens;
ParseError[] errors;
// Detect if they're using input. If so, read from it.
Ast parsedInput = Parser.ParseFile(filePath, out tokens, out errors);
if (AstSearcher.IsUsingDollarInput(parsedInput))
{
executionOptions |= Executor.ExecutionOptions.ReadInputObjects;
// We will consume all of the input to pass to the script, so don't try to read commands from stdin.
ui.ReadFromStdin = false;
}
exec.ExecuteCommandAsyncHelper(tempPipeline, out e1, executionOptions);
}
else
{
exec.ExecuteCommandHelper(tempPipeline, out e1, Executor.ExecutionOptions.AddOutputter);
}
// Pipeline.Invoke has thrown, that's bad. It means the script did not actually
// execute properly. These exceptions should be reflected in the exit code
if (e1 != null)
{
if (!noExit)
{
// Set ExitCode to 0x1
lock (hostGlobalLock)
{
setShouldExitCalled = true;
exitCodeFromRunspace = 0x1;
ShouldEndSession = true;
}
}
ReportException(e1, exec);
}
}
else if (!String.IsNullOrEmpty(initialCommand))
{
// Run the command passed on the command line
tracer.WriteLine("running initial command");
Pipeline tempPipeline = exec.CreatePipeline(initialCommand, true);
if (initialCommandArgs != null)
{
// add the args passed to the command.
foreach (CommandParameter p in initialCommandArgs)
{
tempPipeline.Commands[0].Parameters.Add(p);
}
}
Exception e1;
if (IsRunningAsync)
{
Executor.ExecutionOptions executionOptions = Executor.ExecutionOptions.AddOutputter;
Token[] tokens;
ParseError[] errors;
// Detect if they're using input. If so, read from it.
Ast parsedInput = Parser.ParseInput(initialCommand, out tokens, out errors);
if (AstSearcher.IsUsingDollarInput(parsedInput))
{
executionOptions |= Executor.ExecutionOptions.ReadInputObjects;
// We will consume all of the input to pass to the script, so don't try to read commands from stdin.
ui.ReadFromStdin = false;
}
exec.ExecuteCommandAsyncHelper(tempPipeline, out e1, executionOptions);
}
else
{
exec.ExecuteCommandHelper(tempPipeline, out e1, Executor.ExecutionOptions.AddOutputter);
}
if (e1 != null)
{
// Remember last exception
lastRunspaceInitializationException = e1;
ReportException(e1, exec);
}
}
}
private void RunProfile(string profileFileName, Executor exec)
{
if (!String.IsNullOrEmpty(profileFileName))
{
runspaceInitTracer.WriteLine("checking profile" + profileFileName);
try
{
if (File.Exists(profileFileName))
{
InitializeRunspaceHelper(
". '" + EscapeSingleQuotes(profileFileName) + "'",
exec,
Executor.ExecutionOptions.AddOutputter);
}
else
{
runspaceInitTracer.WriteLine("profile file not found");
}
}
catch (Exception e) // Catch-all OK, 3rd party callout
{
CommandProcessorBase.CheckForSevereException(e);
ReportException(e, exec);
runspaceInitTracer.WriteLine("Could not load profile.");
}
}
}
/// <summary>
///
/// Escapes backtick and tick characters with a backtick, returns the result
///
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
internal static string EscapeSingleQuotes(string str)
{
// worst case we have to escape every character, so capacity is twice as large as input length
StringBuilder sb = new StringBuilder(str.Length * 2);
for (int i = 0; i < str.Length; ++i)
{
char c = str[i];
if (c == '\'')
{
sb.Append(c);
}
sb.Append(c);
}
string result = sb.ToString();
return result;
}
private void WriteErrorLine(string line)
{
ConsoleColor fg = ConsoleColor.Red;
ConsoleColor bg = UI.RawUI.BackgroundColor;
UI.WriteLine(fg, bg, line);
}
// NTRAID#Windows Out Of Band Releases-915506-2005/09/09
// Removed HandleUnexpectedExceptions infrastructure
private void ReportException(Exception e, Executor exec)
{
Dbg.Assert(e != null, "must supply an Exception");
Dbg.Assert(exec != null, "must supply an Executor");
// NTRAID#Windows Out Of Band Releases-915506-2005/09/09
// Removed HandleUnexpectedExceptions infrastructure
// Attempt to write the exception into the error stream so that the normal F&O machinery will
// display it according to preferences.
object error = null;
Pipeline tempPipeline = exec.CreatePipeline();
// NTRAID#Windows OS Bugs-1143621-2005/04/08-sburns
IContainsErrorRecord icer = e as IContainsErrorRecord;
if (icer != null)
{
error = icer.ErrorRecord;
}
else
{
error = (object)new ErrorRecord(e, "ConsoleHost.ReportException", ErrorCategory.NotSpecified, null);
}
PSObject wrappedError = new PSObject(error);
PSNoteProperty note = new PSNoteProperty("writeErrorStream", true);
wrappedError.Properties.Add(note);
Exception e1 = null;
tempPipeline.Input.Write(wrappedError);
if (IsRunningAsync)
{
exec.ExecuteCommandAsyncHelper(tempPipeline, out e1, Executor.ExecutionOptions.AddOutputter);
}
else
{
exec.ExecuteCommandHelper(tempPipeline, out e1, Executor.ExecutionOptions.AddOutputter);
}
if (e1 != null)
{
// that didn't work. Write out the error ourselves as a last resort.
ReportExceptionFallback(e, null);
}
}
/// <summary>
///
/// Reports an exception accoring to the exception reporting settings in effect.
///
/// </summary>
/// <param name="e">
///
/// The exception to report.
///
/// </param>
/// <param name="header">
///
/// Optional header message. Empty or null means "no header"
///
/// </param>
private void ReportExceptionFallback(Exception e, string header)
{
if (!string.IsNullOrEmpty(header))
{
Console.Error.WriteLine(header);
}
if (e == null)
{
return;
}
// See if the exception has an error record attached to it...
ErrorRecord er = null;
IContainsErrorRecord icer = e as IContainsErrorRecord;
if (icer != null)
er = icer.ErrorRecord;
if (e is PSRemotingTransportException)
{
// For remoting errors use full fidelity error writer.
UI.WriteErrorLine(e.Message);
}
else if (e is TargetInvocationException)
{
Console.Error.WriteLine(e.InnerException.Message);
}
else
{
Console.Error.WriteLine(e.Message);
}
// Add the position message for the error if it's available.
if (er != null && er.InvocationInfo != null)
Console.Error.WriteLine(er.InvocationInfo.PositionMessage);
}
/// <summary>
/// raised when the host pops a runspace
/// </summary>
internal event EventHandler RunspacePopped;
/// <summary>
/// raised when the host pushes a runspace
/// </summary>
internal event EventHandler RunspacePushed;
#endregion non-overrides
#region debugger
/// <summary>
/// Handler for debugger events
/// </summary>
private void OnExecutionSuspended(object sender, DebuggerStopEventArgs e)
{
// Check local runspace internalHost to see if debugging is enabled.
LocalRunspace localrunspace = LocalRunspace;
if ((localrunspace != null) && !localrunspace.ExecutionContext.EngineHostInterface.DebuggerEnabled) { return; }
this.debuggerStopEventArgs = e;
InputLoop baseLoop = null;
try
{
if (this.IsRunspacePushed)
{
// For remote debugging block data coming from the main (not-nested)
// running command.
baseLoop = InputLoop.GetNonNestedLoop();
if (baseLoop != null)
{
baseLoop.BlockCommandOutput();
}
}
//
// Display the banner only once per session
//
if (this.displayDebuggerBanner)
{
WriteDebuggerMessage(ConsoleHostStrings.EnteringDebugger);
WriteDebuggerMessage("");
this.displayDebuggerBanner = false;
}
//
// If we hit a breakpoint output its info
//
if (e.Breakpoints.Count > 0)
{
string format = ConsoleHostStrings.HitBreakpoint;
foreach (Breakpoint breakpoint in e.Breakpoints)
{
WriteDebuggerMessage(String.Format(CultureInfo.CurrentCulture, format, breakpoint));
}
WriteDebuggerMessage("");
}
//
// Write the source line
//
if (e.InvocationInfo != null)
{
// line = StringUtil.Format(ConsoleHostStrings.DebuggerSourceCodeFormat, scriptFileName, e.InvocationInfo.ScriptLineNumber, e.InvocationInfo.Line);
WriteDebuggerMessage(e.InvocationInfo.PositionMessage);
}
//
// Start the debug mode
//
EnterDebugMode();
}
finally
{
this.debuggerStopEventArgs = null;
if (baseLoop != null)
{
baseLoop.ResumeCommandOutput();
}
}
}
/// <summary>
/// Returns true if the host is in debug mode
/// </summary>
private bool InDebugMode
{
get
{
return this.inDebugMode;
}
}
/// <summary>
/// True when debugger command is user and available
/// for stopping.
/// </summary>
internal bool DebuggerCanStopCommand
{
get;
set;
}
private Exception lastRunspaceInitializationException = null;
internal uint ExitCode;
/// <summary>
/// Sets the host to debug mode and enters a nested prompt.
/// </summary>
private void EnterDebugMode()
{
this.inDebugMode = true;
try
{
//
// Note that we need to enter the nested prompt via the InternalHost interface.
//
// EnterNestedPrompt must always be run on the local runspace.
Runspace runspace = this.runspaceRef.OldRunspace ?? this.RunspaceRef.Runspace;
runspace.ExecutionContext.EngineHostInterface.EnterNestedPrompt();
}
catch (PSNotImplementedException)
{
WriteDebuggerMessage(ConsoleHostStrings.SessionDoesNotSupportDebugger);
}
finally
{
this.inDebugMode = false;
}
}
/// <summary>
/// Exits the debugger's nested prompt.
/// </summary>
private void ExitDebugMode(DebuggerResumeAction resumeAction)
{
this.debuggerStopEventArgs.ResumeAction = resumeAction;
try
{
//
// Note that we need to exit the nested prompt via the InternalHost interface.
//
// ExitNestedPrompt must always be run on the local runspace.
Runspace runspace = this.runspaceRef.OldRunspace ?? this.RunspaceRef.Runspace;
runspace.ExecutionContext.EngineHostInterface.ExitNestedPrompt();
}
catch (ExitNestedPromptException)
{
// ignore the exception
}
}
/// <summary>
/// Writes a line using the debugger colors
/// </summary>
private void WriteDebuggerMessage(string line)
{
this.ui.WriteWrappedLine(this.ui.DebugForegroundColor, this.ui.DebugBackgroundColor, line);
}
#endregion debugger
#region aux classes
/// <summary>
///
/// InputLoop represents the prompt-input-execute loop of the interactive host. Input loops can be nested, meaning that
/// one input loop can be interrupted and another started; when the second ends, the first resumes.
///
/// Neither this class' instances nor its static data is threadsafe. Caller is responsible for ensuring threadsafe
/// access.
///
/// </summary>
private class InputLoop
{
internal static void RunNewInputLoop(ConsoleHost parent, bool isNested)
{
// creates an instance and adds it to the stack and starts it running.
int stackCount = instanceStack.Count;
if (stackCount == PSHost.MaximumNestedPromptLevel)
{
throw PSTraceSource.NewInvalidOperationException(ConsoleHostStrings.TooManyNestedPromptsError);
}
InputLoop il = new InputLoop(parent, isNested);
instanceStack.Push(il);
il.Run(instanceStack.Count > 1);
// Once the loop has finished running, remove it from the instance stack.
InputLoop il2 = instanceStack.Pop();
Dbg.Assert(il == il2, "top of instance stack does not correspond to the instance pushed");
}
// Presently, this will not work if the Run loop is blocked on a ReadLine call. Whether that's a
// problem or not depends on when we expect calls to this function to be made.
/// <summary>
///
/// </summary>
/// <returns>True if next input loop is nested, False otherwise.</returns>
/// <exception cref="InvalidOperationException">
///
/// when there is no instanceStack.Count == 0
///
/// </exception>
internal static bool ExitCurrentLoop()
{
if (instanceStack.Count == 0)
{
throw PSTraceSource.NewInvalidOperationException(ConsoleHostStrings.InputExitCurrentLoopOutOfSyncError);
}
InputLoop il = instanceStack.Peek();
il.shouldExit = true;
// The main (non-nested) input loop has Count == 1,
// so Count == 2 is the value that indicates the next
// popped stack input loop is non-nested.
return (instanceStack.Count > 2);
}
/// <summary>
/// Returns current root (non-nested) loop only if there is no
/// nesting. This is used *only* by the debugger for remote debugging
/// where data handling on the base commands needs to be blocked
/// during remote debug stop handling.
/// </summary>
/// <returns></returns>
internal static InputLoop GetNonNestedLoop()
{
if (instanceStack.Count == 1)
{
return instanceStack.Peek();
}
return null;
}
private InputLoop(ConsoleHost parent, bool isNested)
{
this.parent = parent;
this.isNested = isNested;
isRunspacePushed = parent.IsRunspacePushed;
parent.RunspacePopped += new EventHandler(HandleRunspacePopped);
parent.RunspacePushed += new EventHandler(HandleRunspacePushed);
exec = new Executor(parent, isNested, false);
promptExec = new Executor(parent, isNested, true);
}
private void HandleRunspacePushed(object sender, EventArgs e)
{
lock (syncObject)
{
isRunspacePushed = true;
runspacePopped = false;
}
}
/// <summary>
/// When a runspace is popped, we need to reevaluate the
/// prompt
/// </summary>
/// <param name="sender">sender of this event, unused</param>
/// <param name="eventArgs">arguments describing this event, unused</param>
private void HandleRunspacePopped(object sender, EventArgs eventArgs)
{
lock (syncObject)
{
isRunspacePushed = false;
runspacePopped = true;
}
}
// NTRAID#Windows Out Of Band Releases-915506-2005/09/09
// Removed HandleUnexpectedExceptions infrastructure
/// <summary>
///
/// Evaluates the prompt, displays it, gets a command from the console, and executes it. Repeats until the command
/// is "exit", or until the shutdown flag is set.
///
/// </summary>
internal void Run(bool inputLoopIsNested)
{
System.Management.Automation.Host.PSHostUserInterface c = parent.UI;
ConsoleHostUserInterface ui = c as ConsoleHostUserInterface;
Dbg.Assert(ui != null, "Host.UI should return an instance.");
bool inBlockMode = false;
bool previousResponseWasEmpty = false;
StringBuilder inputBlock = new StringBuilder();
while (!parent.ShouldEndSession && !shouldExit)
{
try
{
parent.isRunningPromptLoop = true;
string prompt = null;
string line = null;
if (!ui.NoPrompt)
{
if (inBlockMode)
{
// use a special prompt that denotes block mode
prompt = ">> ";
}
else
{
// Make sure the cursor is at the start of the line - some external programs don't
// write a newline, so we do that for them.
if (ui.RawUI.CursorPosition.X != 0)
ui.WriteLine();
// Evaluate any suggestions
if(! previousResponseWasEmpty)
{
EvaluateSuggestions(ui);
}
// Then output the prompt
if (this.parent.InDebugMode)
{
prompt = EvaluateDebugPrompt();
}
if (prompt == null)
{
prompt = EvaluatePrompt();
}
}
ui.Write(prompt);
}
previousResponseWasEmpty = false;
// There could be a profile. So there could be a user defined custom readline command
line = ui.ReadLineWithTabCompletion(exec);
// line will be null in the case that Ctrl-C terminated the input
if (line == null)
{
previousResponseWasEmpty = true;
tracer.WriteLine("line is null");
if (!ui.ReadFromStdin)
{
// If we're not reading from stdin, the we probably got here
// because the user hit ctrl-C. Do a writeline to clean up
// the output...
ui.WriteLine();
}
inBlockMode = false;
if (Console.IsInputRedirected)
{
// null is also the result of reading stdin to EOF.
parent.ShouldEndSession = true;
break;
}
continue;
}
if (line.Trim().Length == 0)
{
if (inBlockMode)
{
// end block mode and execute the block accumulated block
tracer.WriteLine("exiting block mode");
line = inputBlock.ToString();
inBlockMode = false;
}
else if (!this.parent.InDebugMode)
{
previousResponseWasEmpty = true;
continue;
}
}
else
{
if (inBlockMode)
{
tracer.WriteLine("adding line to block");
inputBlock.Append("\n");
inputBlock.Append(line);
continue;
}
}
Dbg.Assert(line != null, "line should not be null");
Dbg.Assert(line.Length > 0 || this.parent.InDebugMode, "line should not be empty unless the host is in debug mode");
Dbg.Assert(!inBlockMode, "should not be in block mode at point of pipeline execution");
Exception e = null;
if (this.parent.InDebugMode)
{
DebuggerCommandResults results = ProcessDebugCommand(line.Trim(), out e);
if (results.ResumeAction != null)
{
this.parent.ExitDebugMode(results.ResumeAction.Value);
}
if (e != null)
{
var ex = e as PSInvalidOperationException;
if (e is PSRemotingTransportException ||
e is RemoteException ||
(ex != null &&
ex.ErrorRecord != null &&
ex.ErrorRecord.FullyQualifiedErrorId.Equals("Debugger:CannotProcessCommandNotStopped", StringComparison.OrdinalIgnoreCase)))
{
// Debugger session is broken. Exit nested loop.
this.parent.ExitDebugMode(DebuggerResumeAction.Continue);
}
else
{
// Handle incomplete parse and other errors.
inBlockMode = HandleErrors(e, line, inBlockMode, ref inputBlock);
}
}
continue;
}
if (runspacePopped)
{
string msg = StringUtil.Format(ConsoleHostStrings.CommandNotExecuted, line);
ui.WriteErrorLine(msg);
runspacePopped = false;
}
else
{
if (parent.IsRunningAsync && !parent.IsNested)
{
exec.ExecuteCommandAsync(line, out e, Executor.ExecutionOptions.AddOutputter | Executor.ExecutionOptions.AddToHistory);
}
else
{
exec.ExecuteCommand(line, out e, Executor.ExecutionOptions.AddOutputter | Executor.ExecutionOptions.AddToHistory);
}
Thread bht = null;
lock (parent.hostGlobalLock)
{
bht = parent.breakHandlerThread;
}
if (bht != null)
{
bht.Join();
}
// Once the pipeline has been executed, we toss any outstanding progress data and
// take down the display.
ui.ResetProgress();
if (e != null)
{
// Handle incomplete parse and other errors.
inBlockMode = HandleErrors(e, line, inBlockMode, ref inputBlock);
// If a remote runspace is pushed and it is not in a good state
// then pop it.
if (isRunspacePushed && (parent.Runspace != null) &&
((parent.Runspace.RunspaceStateInfo.State != RunspaceState.Opened) ||
(parent.Runspace.RunspaceAvailability != RunspaceAvailability.Available)))
{
parent.PopRunspace();
}
}
if (!inBlockMode)
theConsoleHost._interactiveCommandCount += 1;
}
}
// NTRAID#Windows Out Of Band Releases-915506-2005/09/09
// Removed HandleUnexpectedExceptions infrastructure
finally
{
parent.isRunningPromptLoop = false;
}
} // end while
}
internal void BlockCommandOutput()
{
RemotePipeline rCmdPipeline = parent.runningCmd as RemotePipeline;
if (rCmdPipeline != null)
{
rCmdPipeline.DrainIncomingData();
rCmdPipeline.SuspendIncomingData();
}
else
{
exec.BlockCommandOutput();
}
}
internal void ResumeCommandOutput()
{
RemotePipeline rCmdPipeline = parent.runningCmd as RemotePipeline;
if (rCmdPipeline != null)
{
rCmdPipeline.ResumeIncomingData();
}
else
{
exec.ResumeCommandOutput();
}
}
private bool HandleErrors(Exception e, string line, bool inBlockMode, ref StringBuilder inputBlock)
{
Dbg.Assert(e != null, "Exception reference should not be null.");
if (IsIncompleteParseException(e))
{
if (!inBlockMode)
{
inBlockMode = true;
inputBlock = new StringBuilder(line);
}
else
{
inputBlock.Append(line);
}
}
else
{
// an exception ocurred when the command was executed. Tell the user about it.
parent.ReportException(e, exec);
}
return inBlockMode;
}
private DebuggerCommandResults ProcessDebugCommand(string cmd, out Exception e)
{
DebuggerCommandResults results = null;
try
{
parent.DebuggerCanStopCommand = true;
// Use PowerShell object to write streaming data to host.
using (System.Management.Automation.PowerShell ps = System.Management.Automation.PowerShell.Create())
{
PSInvocationSettings settings = new PSInvocationSettings()
{
Host = this.parent
};
PSDataCollection<PSObject> output = new PSDataCollection<PSObject>();
ps.AddCommand("Out-Default");
IAsyncResult async = ps.BeginInvoke<PSObject>(output, settings, null, null);
// Let debugger evaluate command and stream output data.
results = this.parent.Runspace.Debugger.ProcessCommand(
new PSCommand(
new Command(cmd, true)),
output);
output.Complete();
ps.EndInvoke(async);
}
e = null;
}
catch (Exception ex)
{
ConsoleHost.CheckForSevereException(ex);
e = ex;
results = new DebuggerCommandResults(null, false);
}
finally
{
parent.DebuggerCanStopCommand = false;
}
// Exit debugger if command fails to evaluate.
return results ?? new DebuggerCommandResults(DebuggerResumeAction.Continue, false);
}
private bool IsIncompleteParseException(Exception e)
{
// Check e's type.
if (e is IncompleteParseException)
{
return true;
}
// If it is remote exception ferret out the real exception.
RemoteException remoteException = e as RemoteException;
if (remoteException == null || remoteException.ErrorRecord == null)
{
return false;
}
return remoteException.ErrorRecord.CategoryInfo.Reason == typeof(IncompleteParseException).Name;
}
private void EvaluateSuggestions(ConsoleHostUserInterface ui)
{
// Output any training suggestions
try
{
ArrayList suggestions = HostUtilities.GetSuggestion(this.parent.Runspace);
if (suggestions.Count > 0)
{
ui.WriteLine();
}
bool first = true;
foreach (string suggestion in suggestions)
{
if(! first)
ui.WriteLine();
ui.WriteLine(suggestion);
first = false;
}
}
catch (TerminateException)
{
// A variable breakpoint may be hit by HostUtilities.GetSuggestion. The debugger throws TerminateExceptions to stop the execution
// of the current statement; we do not want to treat these exceptions as errors.
}
catch (Exception e)
{
// Catch-all OK. This is a third-party call-out.
CommandProcessorBase.CheckForSevereException(e);
ui.WriteErrorLine(e.Message);
LocalRunspace localRunspace = (LocalRunspace)this.parent.Runspace;
localRunspace.GetExecutionContext.AppendDollarError(e);
}
}
private string EvaluatePrompt()
{
Exception unused = null;
string promptString = promptExec.ExecuteCommandAndGetResultAsString("prompt", out unused);
if (String.IsNullOrEmpty(promptString))
{
promptString = ConsoleHostStrings.DefaultPrompt;
}
// Check for the pushed runspace scenario.
if (isRunspacePushed)
{
RemoteRunspace remoteRunspace = this.parent.Runspace as RemoteRunspace;
if (remoteRunspace != null)
{
promptString = HostUtilities.GetRemotePrompt(remoteRunspace, promptString, parent.inPushedConfiguredSession);
}
}
else
{
if (runspacePopped)
{
runspacePopped = false;
}
}
// Return composed prompt string.
return promptString;
}
private string EvaluateDebugPrompt()
{
PSDataCollection<PSObject> output = new PSDataCollection<PSObject>();
try
{
this.parent.Runspace.Debugger.ProcessCommand(
new PSCommand(new Command("prompt")),
output);
}
catch (Exception ex)
{
ConsoleHost.CheckForSevereException(ex);
this.parent.ReportException(ex, exec);
}
PSObject prompt = output.ReadAndRemoveAt0();
string promptString = (prompt != null) ? (prompt.BaseObject as string) : null;
if (promptString != null)
{
RemoteRunspace remoteRunspace = this.parent.Runspace as RemoteRunspace;
if (remoteRunspace != null)
{
promptString = HostUtilities.GetRemotePrompt(remoteRunspace, promptString, parent.inPushedConfiguredSession);
}
}
return promptString;
}
private ConsoleHost parent;
private bool isNested;
private bool shouldExit;
private Executor exec;
private Executor promptExec;
private object syncObject = new object();
private bool isRunspacePushed = false;
private bool runspacePopped = false;
// The instance stack is used to keep track of which InputLoop instance should be told to exit
// when PSHost.ExitNestedPrompt is called.
// threadsafety guaranteed by enclosing class
private static Stack<InputLoop> instanceStack = new Stack<InputLoop>();
}
[Serializable]
[SuppressMessage("Microsoft.Design", "CA1064:ExceptionsShouldBePublic", Justification =
"This exception cannot be used outside of the console host application. It is not thrown by a library routine, only by an application.")]
private class ConsoleHostStartupException : Exception
{
internal
ConsoleHostStartupException()
:
base()
{
}
internal
ConsoleHostStartupException(string message)
:
base(message)
{
}
#if !CORECLR // ApplicationException & System.Runtime.Serialization.SerializationInfo are Not In CoreCLR
protected
ConsoleHostStartupException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context)
:
base(info, context)
{
}
#endif
internal
ConsoleHostStartupException(string message, Exception innerException)
:
base(message, innerException)
{
}
}
#endregion aux classes
/// <summary>
/// By declaring runspace as ObjectRef&lt;Runspace&gt; we are able to hide the real runspace with
/// a remote runspace in the PushRunspace scenario. By declaring it as a mask, the variable
/// runspace becomes an indirect reference to the actual runspace which we can override with
/// a remote runspace while it is pushed. Also we can easily revert back to the original
/// runspace when the PopRunspace command is invoked.
/// </summary>
private RunspaceRef runspaceRef;
private GCHandle breakHandlerGcHandle;
private System.Threading.Thread breakHandlerThread;
private bool isDisposed;
internal ConsoleHostUserInterface ui;
private string savedWindowTitle = "";
private Guid id = Guid.NewGuid();
private Version ver = PSVersionInfo.PSVersion;
private int exitCodeFromRunspace;
private bool noExit = true;
private bool isCtrlCDisabled;
private bool setShouldExitCalled;
private Serialization.DataFormat outputFormat;
private Serialization.DataFormat inputFormat;
private bool isRunningPromptLoop;
private bool wasInitialCommandEncoded;
private RunspaceConfiguration configuration;
// hostGlobalLock is used to sync public method calls (in case multiple threads call into the host) and access to
// state that persists across method calls, like progress data. It's internal because the ui object also
// uses this same object.
internal object hostGlobalLock = new object();
// These members are possibly accessed from multiple threads (the break handler thread, a pipeline thread, or the main
// thread). We use hostGlobalLock to sync access to them.
private bool shouldEndSession;
private int beginApplicationNotifyCount;
private ConsoleTextWriter consoleWriter;
private WrappedSerializer outputSerializer;
private WrappedSerializer errorSerializer;
private bool inDebugMode;
private bool displayDebuggerBanner;
private DebuggerStopEventArgs debuggerStopEventArgs;
private bool isNested;
private bool inPushedConfiguredSession;
internal Pipeline runningCmd;
// The ConsoleHost class is a singleton. Note that there is not a thread-safety issue with these statics as there can
// only be one console host per process.
private static ConsoleHost theConsoleHost;
internal static InitialSessionState DefaultInitialSessionState;
[TraceSource("ConsoleHost", "ConsoleHost subclass of S.M.A.PSHost")]
private static
PSTraceSource tracer = PSTraceSource.GetTracer("ConsoleHost", "ConsoleHost subclass of S.M.A.PSHost");
[TraceSource("ConsoleHostRunspaceInit", "Initialization code for ConsoleHost's Runspace")]
private static PSTraceSource runspaceInitTracer =
PSTraceSource.GetTracer("ConsoleHostRunspaceInit", "Initialization code for ConsoleHost's Runspace", false);
} // ConsoleHost
/// <summary>
/// Defines arguments passed to ConsoleHost.CreateRunspace
/// </summary>
internal sealed class RunspaceCreationEventArgs : EventArgs
{
/// <summary>
/// Constructs RunspaceCreationEventArgs
/// </summary>
/// <param name="initialCommand"> </param>
/// <param name="skipProfiles"></param>
/// <param name="staMode"></param>
/// <param name="importSystemModules"></param>
/// <param name="configurationName"></param>
/// <param name="initialCommandArgs"></param>
internal RunspaceCreationEventArgs(string initialCommand,
bool skipProfiles,
bool staMode,
bool importSystemModules,
string configurationName,
Collection<CommandParameter> initialCommandArgs)
{
InitialCommand = initialCommand;
SkipProfiles = skipProfiles;
StaMode = staMode;
ImportSystemModules = importSystemModules;
ConfigurationName = configurationName;
InitialCommandArgs = initialCommandArgs;
}
internal string InitialCommand { get; set; }
internal bool SkipProfiles { get; set; }
internal bool StaMode { get; set; }
internal bool ImportSystemModules { get; set; }
internal string ConfigurationName { get; set; }
internal Collection<CommandParameter> InitialCommandArgs { get; set; }
}
} // namespace