2018-02-13 09:23:53 -08:00
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
2016-03-30 14:20:52 -07:00
#pragma warning disable 1634 , 1691
using System.Diagnostics ;
using System.IO ;
using System.ComponentModel ;
using System.Text ;
using System.Collections ;
using System.Threading ;
using System.Management.Automation.Internal ;
using System.Management.Automation.Runspaces ;
using System.Xml ;
using System.Runtime.InteropServices ;
using Dbg = System . Management . Automation . Diagnostics ;
using System.Runtime.Serialization ;
using System.Globalization ;
using System.Diagnostics.CodeAnalysis ;
2016-10-10 18:57:39 -07:00
using System.Collections.Concurrent ;
using System.Collections.Generic ;
2016-03-30 14:20:52 -07:00
namespace System.Management.Automation
{
/// <summary>
/// Various types of input/output supported by native commands.
/// </summary>
/// <remarks>
/// Most native commands only support text. Other formats
/// are supported by minishell
/// </remarks>
internal enum NativeCommandIOFormat
{
Text ,
Xml
};
/// <summary>
/// Different streams produced by minishell output
/// </summary>
internal enum MinishellStream
{
Output ,
Error ,
Verbose ,
Warning ,
Debug ,
Progress ,
Information ,
Unknown
}
/// <summary>
/// Helper class which holds stream names and also provide conversion
/// method
/// </summary>
internal static class StringToMinishellStreamConverter
{
internal const string OutputStream = "output" ;
internal const string ErrorStream = "error" ;
internal const string DebugStream = "debug" ;
internal const string VerboseStream = "verbose" ;
internal const string WarningStream = "warning" ;
internal const string ProgressStream = "progress" ;
internal const string InformationStream = "information" ;
internal static MinishellStream ToMinishellStream ( string stream )
{
Dbg . Assert ( stream != null , "caller should validate the parameter" );
MinishellStream ms = MinishellStream . Unknown ;
if ( OutputStream . Equals ( stream , StringComparison . OrdinalIgnoreCase ))
{
ms = MinishellStream . Output ;
}
else if ( ErrorStream . Equals ( stream , StringComparison . OrdinalIgnoreCase ))
{
ms = MinishellStream . Error ;
}
else if ( DebugStream . Equals ( stream , StringComparison . OrdinalIgnoreCase ))
{
ms = MinishellStream . Debug ;
}
else if ( VerboseStream . Equals ( stream , StringComparison . OrdinalIgnoreCase ))
{
ms = MinishellStream . Verbose ;
}
else if ( WarningStream . Equals ( stream , StringComparison . OrdinalIgnoreCase ))
{
ms = MinishellStream . Warning ;
}
else if ( ProgressStream . Equals ( stream , StringComparison . OrdinalIgnoreCase ))
{
ms = MinishellStream . Progress ;
}
else if ( InformationStream . Equals ( stream , StringComparison . OrdinalIgnoreCase ))
{
ms = MinishellStream . Information ;
}
return ms ;
}
}
/// <summary>
/// An output object from the child process.
/// If it's from the error stream isError will be true
/// </summary>
internal class ProcessOutputObject
{
/// <summary>
/// Get the data from this object
/// </summary>
/// <value>The data</value>
2016-08-02 13:29:09 -07:00
internal object Data { get ; }
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
/// <summary>
/// Stream to which data belongs
/// </summary>
2016-08-02 13:29:09 -07:00
internal MinishellStream Stream { get ; }
2016-03-30 14:20:52 -07:00
/// <summary>
/// Build an output object
/// </summary>
/// <param name="data">The data to output</param>
/// <param name="stream">stream to which data belongs</param>
internal ProcessOutputObject ( object data , MinishellStream stream )
{
2016-08-02 13:29:09 -07:00
Data = data ;
Stream = stream ;
2016-03-30 14:20:52 -07:00
}
}
/// <summary>
/// Provides way to create and execute native commands.
/// </summary>
2016-07-29 13:02:49 -07:00
internal class NativeCommandProcessor : CommandProcessorBase
2016-03-30 14:20:52 -07:00
{
#region ctor / native command properties
/// <summary>
2017-01-16 13:31:14 -08:00
/// Information about application which is invoked by this instance of
/// NativeCommandProcessor
2016-03-30 14:20:52 -07:00
/// </summary>
2016-07-29 13:02:49 -07:00
private ApplicationInfo _applicationInfo ;
2016-03-30 14:20:52 -07:00
/// <summary>
/// Initializes the new instance of NativeCommandProcessor class.
/// </summary>
/// <param name="applicationInfo">
/// The information about the application to run.
/// </param>
/// <param name="context">
/// The execution context for this command.
/// </param>
/// <exception cref="ArgumentNullException">
/// <paramref name="applicationInfo"/> or <paramref name="context"/> is null
/// </exception>
2016-07-29 13:02:49 -07:00
internal NativeCommandProcessor ( ApplicationInfo applicationInfo , ExecutionContext context )
2016-03-30 14:20:52 -07:00
: base ( applicationInfo )
{
if ( applicationInfo == null )
{
throw PSTraceSource . NewArgumentNullException ( "applicationInfo" );
}
2016-07-29 13:02:49 -07:00
_applicationInfo = applicationInfo ;
2016-03-30 14:20:52 -07:00
this . _context = context ;
this . Command = new NativeCommand ();
this . Command . CommandInfo = applicationInfo ;
this . Command . Context = context ;
this . Command . commandRuntime = this . commandRuntime = new MshCommandRuntime ( context , applicationInfo , this . Command );
this . CommandScope = context . EngineSessionState . CurrentScope ;
//provide native command a backpointer to this object.
//When kill is called on the command object,
//it calls this NCP back to kill the process...
(( NativeCommand ) Command ). MyCommandProcessor = this ;
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
//Create input writer for providing input to the process.
2016-07-29 13:02:49 -07:00
_inputWriter = new ProcessInputWriter ( Command );
2017-09-28 17:15:37 -07:00
_isTranscribing = this . Command . Context . EngineHostInterface . UI . IsTranscribing ;
2016-03-30 14:20:52 -07:00
}
/// <summary>
/// Gets the NativeCommand associated with this command processor.
/// </summary>
private NativeCommand nativeCommand
{
get
{
NativeCommand command = this . Command as NativeCommand ;
Diagnostics . Assert ( command != null , "this.Command is created in the constructor." );
return command ;
}
}
/// <summary>
/// Gets or sets the name of the native command.
/// </summary>
private string NativeCommandName
{
get
{
2016-07-29 13:02:49 -07:00
string name = _applicationInfo . Name ;
2016-03-30 14:20:52 -07:00
return name ;
}
}
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
/// <summary>
/// Gets or sets path to the native command.
/// </summary>
private string Path
{
get
{
2016-07-29 13:02:49 -07:00
string path = _applicationInfo . Path ;
2016-03-30 14:20:52 -07:00
return path ;
}
}
#endregion ctor / native command properties
#region parameter binder
/// <summary>
/// Variable which is set to true when prepare is called.
/// Parameter Binder should only be created after Prepare method is called
/// </summary>
2016-07-29 13:02:49 -07:00
private bool _isPreparedCalled = false ;
2016-03-30 14:20:52 -07:00
/// <summary>
/// Parameter binder used by this command processor
/// </summary>
2016-07-29 13:02:49 -07:00
private NativeCommandParameterBinderController _nativeParameterBinderController ;
2016-03-30 14:20:52 -07:00
/// <summary>
/// Gets a new instance of a ParameterBinderController using a NativeCommandParameterBinder
/// </summary>
/// <param name="command">
/// The native command to be run.
/// </param>
/// <returns>
/// A new parameter binder controller for the specified command.
/// </returns>
internal ParameterBinderController NewParameterBinderController ( InternalCommand command )
{
2016-07-29 13:02:49 -07:00
Dbg . Assert ( _isPreparedCalled , "parameter binder should not be created before prepared is called" );
2016-03-30 14:20:52 -07:00
2016-07-29 13:02:49 -07:00
if ( _isMiniShell )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
_nativeParameterBinderController =
2016-03-30 14:20:52 -07:00
new MinishellParameterBinderController (
this . nativeCommand );
}
else
{
2016-07-29 13:02:49 -07:00
_nativeParameterBinderController =
2016-03-30 14:20:52 -07:00
new NativeCommandParameterBinderController (
this . nativeCommand );
}
2016-07-29 13:02:49 -07:00
return _nativeParameterBinderController ;
2016-03-30 14:20:52 -07:00
}
internal NativeCommandParameterBinderController NativeParameterBinderController
{
get
{
2016-07-29 13:02:49 -07:00
if ( _nativeParameterBinderController == null )
2016-03-30 14:20:52 -07:00
{
NewParameterBinderController ( this . Command );
}
2018-12-23 22:20:06 -08:00
2016-07-29 13:02:49 -07:00
return _nativeParameterBinderController ;
2016-03-30 14:20:52 -07:00
}
}
#endregion parameter binder
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
#region internal overrides
/// <summary>
/// Prepares the command for execution with the specified CommandParameterInternal.
/// </summary>
internal override void Prepare ( IDictionary psDefaultParameterValues )
{
2016-07-29 13:02:49 -07:00
_isPreparedCalled = true ;
2016-03-30 14:20:52 -07:00
//Check if the application is minishell
2016-07-29 13:02:49 -07:00
_isMiniShell = IsMiniShell ();
2016-03-30 14:20:52 -07:00
2017-01-16 13:31:14 -08:00
//For minishell parameter binding is done in Complete method because we need
//to know if output is redirected before we can bind parameters.
2016-07-29 13:02:49 -07:00
if (! _isMiniShell )
2016-03-30 14:20:52 -07:00
{
this . NativeParameterBinderController . BindParameters ( arguments );
}
2016-10-10 18:57:39 -07:00
2017-08-10 13:32:57 -07:00
try
{
InitNativeProcess ();
}
catch ( Exception )
{
// Do cleanup in case of exception
CleanUp ();
throw ;
}
2016-03-30 14:20:52 -07:00
}
/// <summary>
/// Executes the command. This method assumes that Prepare is already called.
/// </summary>
internal override void ProcessRecord ()
{
2017-08-10 13:32:57 -07:00
try
2016-03-30 14:20:52 -07:00
{
2017-08-10 13:32:57 -07:00
while ( Read ())
{
_inputWriter . Add ( Command . CurrentPipelineObject );
}
2016-10-12 16:33:23 -07:00
2017-08-10 13:32:57 -07:00
ConsumeAvailableNativeProcessOutput ( blocking : false );
}
catch ( Exception )
{
// Do cleanup in case of exception
CleanUp ();
throw ;
}
2016-03-30 14:20:52 -07:00
}
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
/// <summary>
/// Process object for the invoked application
/// </summary>
2016-07-29 13:02:49 -07:00
private System . Diagnostics . Process _nativeProcess ;
2016-03-30 14:20:52 -07:00
/// <summary>
/// This is used for writing input to the process
/// </summary>
2016-07-29 13:02:49 -07:00
private ProcessInputWriter _inputWriter = null ;
2016-03-30 14:20:52 -07:00
/// <summary>
/// Is true if this command is to be run "standalone" - that is, with
/// no redirection.
2017-01-16 13:31:14 -08:00
/// </summary>
2016-03-30 14:20:52 -07:00
private bool _runStandAlone ;
2016-10-10 18:57:39 -07:00
/// <summary>
/// Indicate whether we need to consider redirecting the output/error of the current native command.
/// Usually a windows program which is the last command in a pipeline can be executed as 'background' -- we don't need to capture its output/error streams.
2017-01-16 13:31:14 -08:00
/// </summary>
2016-10-26 19:06:48 -07:00
private bool _isRunningInBackground ;
2016-10-10 18:57:39 -07:00
2017-08-10 13:32:57 -07:00
/// <summary>
/// Indicate if we have called 'NotifyBeginApplication()' on the host, so that
/// we can call the counterpart 'NotifyEndApplication' as approriate.
/// </summary>
private bool _hasNotifiedBeginApplication ;
2016-10-10 18:57:39 -07:00
/// <summary>
/// This output queue helps us keep the output and error (if redirected) order correct.
2017-01-16 13:31:14 -08:00
/// We could do a blocking read in the Complete block instead,
2016-10-10 18:57:39 -07:00
/// but then we would not be able to restore the order reasonable.
/// </summary>
2016-11-07 18:38:25 -08:00
private BlockingCollection < ProcessOutputObject > _nativeProcessOutputQueue ;
2016-10-10 18:57:39 -07:00
2017-09-28 17:15:37 -07:00
private static bool? s_supportScreenScrape = null ;
private bool _isTranscribing ;
2016-10-10 18:57:39 -07:00
private Host . Coordinates _startPosition ;
2016-03-30 14:20:52 -07:00
/// <summary>
2017-01-16 13:31:14 -08:00
/// object used for synchronization between StopProcessing thread and
2016-03-30 14:20:52 -07:00
/// Pipeline thread.
/// </summary>
2016-07-29 13:02:49 -07:00
private object _sync = new object ();
2016-03-30 14:20:52 -07:00
/// <summary>
/// Executes the native command once all of the input has been gathered.
/// </summary>
/// <exception cref="PipelineStoppedException">
/// The pipeline is stopping
/// </exception>
/// <exception cref="ApplicationFailedException">
/// The native command could not be run
/// </exception>
2016-10-10 18:57:39 -07:00
private void InitNativeProcess ()
2016-03-30 14:20:52 -07:00
{
// Figure out if we're going to run this process "standalone" i.e. without
// redirecting anything. This is a bit tricky as we always run redirected so
// we have to see if the redirection is actually being done at the topmost level or not.
2017-09-28 17:15:37 -07:00
// Calculate if input and output are redirected.
2016-03-30 14:20:52 -07:00
bool redirectOutput ;
bool redirectError ;
2016-10-26 19:06:48 -07:00
bool redirectInput ;
2016-03-30 14:20:52 -07:00
2017-09-28 17:15:37 -07:00
_startPosition = new Host . Coordinates ();
2016-10-26 19:06:48 -07:00
CalculateIORedirection ( out redirectOutput , out redirectError , out redirectInput );
2016-03-30 14:20:52 -07:00
// Find out if it's the only command in the pipeline.
bool soloCommand = this . Command . MyInvocation . PipelineLength == 1 ;
2017-01-16 13:31:14 -08:00
// Get the start info for the process.
2016-10-26 19:06:48 -07:00
ProcessStartInfo startInfo = GetProcessStartInfo ( redirectOutput , redirectError , redirectInput , soloCommand );
2016-03-30 14:20:52 -07:00
if ( this . Command . Context . CurrentPipelineStopping )
{
throw new PipelineStoppedException ();
}
2016-10-26 19:06:48 -07:00
Exception exceptionToRethrow = null ;
2016-03-30 14:20:52 -07:00
try
{
// If this process is being run standalone, tell the host, which might want
2017-01-16 13:31:14 -08:00
// to save off the window title or other such state as might be tweaked by
2016-03-30 14:20:52 -07:00
// the native process
2017-08-10 13:32:57 -07:00
if ( _runStandAlone )
2016-03-30 14:20:52 -07:00
{
this . Command . Context . EngineHostInterface . NotifyBeginApplication ();
2017-08-10 13:32:57 -07:00
_hasNotifiedBeginApplication = true ;
2016-03-30 14:20:52 -07:00
// Also, store the Raw UI coordinates so that we can scrape the screen after
// if we are transcribing.
2017-09-28 17:15:37 -07:00
if ( _isTranscribing && ( true == s_supportScreenScrape ))
2016-03-30 14:20:52 -07:00
{
2017-09-28 17:15:37 -07:00
_startPosition = this . Command . Context . EngineHostInterface . UI . RawUI . CursorPosition ;
_startPosition . X = 0 ;
2016-03-30 14:20:52 -07:00
}
}
//Start the process. If stop has been called, throw exception.
//Note: if StopProcessing is called which this method has the lock,
//Stop thread will wait for nativeProcess to start.
//If StopProcessing gets the lock first, then it will set the stopped
//flag and this method will throw PipelineStoppedException when it gets
//the lock.
2016-07-29 13:02:49 -07:00
lock ( _sync )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
if ( _stopped )
2016-03-30 14:20:52 -07:00
{
throw new PipelineStoppedException ();
}
try
{
2017-08-10 13:32:57 -07:00
_nativeProcess = new Process () { StartInfo = startInfo };
2016-07-29 13:02:49 -07:00
_nativeProcess . Start ();
2016-03-30 14:20:52 -07:00
}
catch ( Win32Exception )
{
2017-08-10 13:32:57 -07:00
// On Unix platforms, nothing can be further done, so just throw
// On headless Windows SKUs, there is no shell to fall back to, so just throw
if (! Platform . IsWindowsDesktop ) { throw ; }
// on Windows desktops, see if there is a file association for this command. If so then we'll use that.
2016-10-26 19:06:48 -07:00
string executable = FindExecutable ( startInfo . FileName );
2016-03-30 14:20:52 -07:00
bool notDone = true ;
if (! String . IsNullOrEmpty ( executable ))
{
if ( IsConsoleApplication ( executable ))
{
// Allocate a console if there isn't one attached already...
ConsoleVisibility . AllocateHiddenConsole ();
}
2016-10-26 19:06:48 -07:00
string oldArguments = startInfo . Arguments ;
string oldFileName = startInfo . FileName ;
startInfo . Arguments = "\"" + startInfo . FileName + "\" " + startInfo . Arguments ;
startInfo . FileName = executable ;
2016-03-30 14:20:52 -07:00
try
{
2016-07-29 13:02:49 -07:00
_nativeProcess . Start ();
2016-03-30 14:20:52 -07:00
notDone = false ;
}
catch ( Win32Exception )
{
// Restore the old filename and arguments to try shell execute last...
2016-10-26 19:06:48 -07:00
startInfo . Arguments = oldArguments ;
startInfo . FileName = oldFileName ;
2016-03-30 14:20:52 -07:00
}
}
2017-01-16 13:31:14 -08:00
// We got here because there was either no executable found for this
2016-03-30 14:20:52 -07:00
// file or we tried to launch the exe and it failed. In either case
// we will try launching one last time using ShellExecute...
if ( notDone )
{
2016-10-26 19:06:48 -07:00
if ( soloCommand && startInfo . UseShellExecute == false )
2016-03-30 14:20:52 -07:00
{
2016-10-26 19:06:48 -07:00
startInfo . UseShellExecute = true ;
startInfo . RedirectStandardInput = false ;
startInfo . RedirectStandardOutput = false ;
startInfo . RedirectStandardError = false ;
2016-07-29 13:02:49 -07:00
_nativeProcess . Start ();
2016-03-30 14:20:52 -07:00
}
else
{
throw ;
}
}
}
}
if ( this . Command . MyInvocation . PipelinePosition < this . Command . MyInvocation . PipelineLength )
{
// Never background unless you're at the end of a pipe.
// Something like
// ls | notepad | sort.exe
// should block until the notepad process is terminated.
2016-10-26 19:06:48 -07:00
_isRunningInBackground = false ;
2016-03-30 14:20:52 -07:00
}
else
{
2016-10-26 19:06:48 -07:00
_isRunningInBackground = true ;
if ( startInfo . UseShellExecute == false )
2016-03-30 14:20:52 -07:00
{
2016-10-26 19:06:48 -07:00
_isRunningInBackground = IsWindowsApplication ( _nativeProcess . StartInfo . FileName );
2016-03-30 14:20:52 -07:00
}
}
try
{
//If input is redirected, start input to process.
2016-10-26 19:06:48 -07:00
if ( startInfo . RedirectStandardInput )
2016-03-30 14:20:52 -07:00
{
NativeCommandIOFormat inputFormat = NativeCommandIOFormat . Text ;
2016-07-29 13:02:49 -07:00
if ( _isMiniShell )
2016-03-30 14:20:52 -07:00
{
inputFormat = (( MinishellParameterBinderController ) NativeParameterBinderController ). InputFormat ;
}
2018-12-23 22:20:06 -08:00
2016-07-29 13:02:49 -07:00
lock ( _sync )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
if (! _stopped )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
_inputWriter . Start ( _nativeProcess , inputFormat );
2016-03-30 14:20:52 -07:00
}
}
}
}
catch ( Exception )
{
StopProcessing ();
throw ;
}
2016-10-10 18:57:39 -07:00
2016-10-26 19:06:48 -07:00
if ( _isRunningInBackground == false )
2016-03-30 14:20:52 -07:00
{
2016-10-10 18:57:39 -07:00
InitOutputQueue ();
2016-07-29 13:02:49 -07:00
}
2016-03-30 14:20:52 -07:00
}
catch ( Win32Exception e )
{
2016-10-26 19:06:48 -07:00
exceptionToRethrow = e ;
2016-10-10 18:57:39 -07:00
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
catch ( PipelineStoppedException )
{
// If we're stopping the process, just rethrow this exception...
throw ;
}
catch ( Exception e )
{
2016-10-26 19:06:48 -07:00
exceptionToRethrow = e ;
2016-10-10 18:57:39 -07:00
}
// An exception was thrown while attempting to run the program
// so wrap and rethrow it here...
2016-10-26 19:06:48 -07:00
if ( exceptionToRethrow != null )
2016-10-10 18:57:39 -07:00
{
// It's a system exception so wrap it in one of ours and re-throw.
string message = StringUtil . Format ( ParserStrings . ProgramFailedToExecute ,
2016-10-26 19:06:48 -07:00
this . NativeCommandName , exceptionToRethrow . Message ,
2016-10-10 18:57:39 -07:00
this . Command . MyInvocation . PositionMessage );
2016-10-26 19:06:48 -07:00
ApplicationFailedException appFailedException = new ApplicationFailedException ( message , exceptionToRethrow );
2016-10-10 18:57:39 -07:00
// There is no need to set this exception here since this exception will eventually be caught by pipeline processor.
// this.commandRuntime.PipelineProcessor.ExecutionFailed = true;
throw appFailedException ;
}
}
private void InitOutputQueue ()
{
//if output is redirected, start reading output of process in queue.
2016-10-26 19:06:48 -07:00
if ( _nativeProcess . StartInfo . RedirectStandardOutput || _nativeProcess . StartInfo . RedirectStandardError )
2016-10-10 18:57:39 -07:00
{
lock ( _sync )
{
if (! _stopped )
{
2016-11-07 18:38:25 -08:00
_nativeProcessOutputQueue = new BlockingCollection < ProcessOutputObject >();
// we don't assign the handler to anything, because it's used only for objects marshaling
new ProcessOutputHandler ( _nativeProcess , _nativeProcessOutputQueue );
2016-10-10 18:57:39 -07:00
}
}
}
}
2016-11-07 18:38:25 -08:00
private ProcessOutputObject DequeueProcessOutput ( bool blocking )
{
if ( blocking )
{
2016-11-15 17:29:05 -08:00
// If adding was completed and collection is empty (IsCompleted == true)
// there is no need to do a blocking Take(), we should just return.
if (! _nativeProcessOutputQueue . IsCompleted )
2016-11-07 18:38:25 -08:00
{
try
{
2017-01-16 13:31:14 -08:00
// If adding is not complete we need a try {} catch {}
2016-11-11 13:20:47 -08:00
// to mitigate a concurrent call to CompleteAdding().
2016-11-07 18:38:25 -08:00
return _nativeProcessOutputQueue . Take ();
}
catch ( InvalidOperationException )
{
// It's a normal situation: another thread can mark collection as CompleteAdding
// in a concurrent way and we will rise an exception in Take().
// Although it's a normal situation it's not the most common path
// and will be executed only on the race condtion case.
}
}
return null ;
}
else
{
ProcessOutputObject record = null ;
_nativeProcessOutputQueue . TryTake ( out record );
return record ;
}
}
2016-10-10 18:57:39 -07:00
/// <summary>
2016-10-12 16:33:23 -07:00
/// Read the output from the native process and send it down the line.
2016-10-10 18:57:39 -07:00
/// </summary>
2016-11-07 18:38:25 -08:00
private void ConsumeAvailableNativeProcessOutput ( bool blocking )
2016-10-10 18:57:39 -07:00
{
2016-10-26 19:06:48 -07:00
if ( _isRunningInBackground == false )
2016-10-10 18:57:39 -07:00
{
2016-10-26 19:06:48 -07:00
if ( _nativeProcess . StartInfo . RedirectStandardOutput || _nativeProcess . StartInfo . RedirectStandardError )
2016-10-10 18:57:39 -07:00
{
ProcessOutputObject record ;
2016-11-07 18:38:25 -08:00
while (( record = DequeueProcessOutput ( blocking )) != null )
2016-10-10 18:57:39 -07:00
{
2016-10-26 19:06:48 -07:00
if ( this . Command . Context . CurrentPipelineStopping )
{
this . StopProcessing ();
return ;
}
2016-10-10 18:57:39 -07:00
ProcessOutputRecord ( record );
}
}
}
}
internal override void Complete ()
{
2016-10-26 19:06:48 -07:00
Exception exceptionToRethrow = null ;
2016-10-10 18:57:39 -07:00
try
{
2017-01-16 13:31:14 -08:00
if ( _isRunningInBackground == false )
2016-10-10 18:57:39 -07:00
{
//Wait for input writer to finish.
_inputWriter . Done ();
2016-11-11 13:20:47 -08:00
// read all the available output in the blocking way
2016-11-07 18:38:25 -08:00
ConsumeAvailableNativeProcessOutput ( blocking : true );
2016-11-11 13:20:47 -08:00
_nativeProcess . WaitForExit ();
2016-10-10 18:57:39 -07:00
2017-09-28 17:15:37 -07:00
// Capture screen output if we are transcribing and running stand alone
if ( _isTranscribing && ( true == s_supportScreenScrape ) && _runStandAlone )
2016-10-10 18:57:39 -07:00
{
Host . Coordinates endPosition = this . Command . Context . EngineHostInterface . UI . RawUI . CursorPosition ;
endPosition . X = this . Command . Context . EngineHostInterface . UI . RawUI . BufferSize . Width - 1 ;
// If the end position is before the start position, then capture the entire buffer.
if ( endPosition . Y < _startPosition . Y )
{
_startPosition . Y = 0 ;
}
Host . BufferCell [,] bufferContents = this . Command . Context . EngineHostInterface . UI . RawUI . GetBufferContents (
new Host . Rectangle ( _startPosition , endPosition ));
StringBuilder lineContents = new StringBuilder ();
StringBuilder bufferText = new StringBuilder ();
for ( int row = 0 ; row < bufferContents . GetLength ( 0 ); row ++)
{
if ( row > 0 )
{
bufferText . Append ( Environment . NewLine );
}
lineContents . Clear ();
for ( int column = 0 ; column < bufferContents . GetLength ( 1 ); column ++)
{
lineContents . Append ( bufferContents [ row , column ]. Character );
}
bufferText . Append ( lineContents . ToString (). TrimEnd ( Utils . Separators . SpaceOrTab ));
}
this . Command . Context . InternalHost . UI . TranscribeResult ( bufferText . ToString ());
}
this . Command . Context . SetVariable ( SpecialVariables . LastExitCodeVarPath , _nativeProcess . ExitCode );
if ( _nativeProcess . ExitCode != 0 )
this . commandRuntime . PipelineProcessor . ExecutionFailed = true ;
}
}
catch ( Win32Exception e )
{
2016-10-26 19:06:48 -07:00
exceptionToRethrow = e ;
2018-12-08 18:53:30 +02:00
}
2016-10-10 18:57:39 -07:00
catch ( PipelineStoppedException )
{
// If we're stopping the process, just rethrow this exception...
throw ;
}
catch ( Exception e )
{
2016-10-26 19:06:48 -07:00
exceptionToRethrow = e ;
2016-03-30 14:20:52 -07:00
}
finally
{
2017-08-10 13:32:57 -07:00
// Do some cleanup
2016-03-30 14:20:52 -07:00
CleanUp ();
}
// An exception was thrown while attempting to run the program
// so wrap and rethrow it here...
2016-10-26 19:06:48 -07:00
if ( exceptionToRethrow != null )
2016-03-30 14:20:52 -07:00
{
// It's a system exception so wrap it in one of ours and re-throw.
string message = StringUtil . Format ( ParserStrings . ProgramFailedToExecute ,
2016-10-26 19:06:48 -07:00
this . NativeCommandName , exceptionToRethrow . Message ,
2016-03-30 14:20:52 -07:00
this . Command . MyInvocation . PositionMessage );
2016-10-26 19:06:48 -07:00
ApplicationFailedException appFailedException = new ApplicationFailedException ( message , exceptionToRethrow );
2016-03-30 14:20:52 -07:00
// There is no need to set this exception here since this exception will eventually be caught by pipeline processor.
// this.commandRuntime.PipelineProcessor.ExecutionFailed = true;
throw appFailedException ;
}
}
#region Process cleanup with Child Process cleanup
/// <summary>
2016-08-28 05:44:55 +00:00
/// Utility routine to kill a process, discarding non-critical exceptions.
2016-03-30 14:20:52 -07:00
/// This utility makes two passes at killing a process. In the first pass,
/// if the process handle is invalid (as seems to be the case with an ntvdm)
/// then we try to get a fresh handle based on the original process id.
/// </summary>
/// <param name="processToKill">The process to kill</param>
2016-07-29 13:02:49 -07:00
private static void KillProcess ( Process processToKill )
2016-03-30 14:20:52 -07:00
{
if ( NativeCommandProcessor . IsServerSide )
{
Process [] currentlyRunningProcs = Process . GetProcesses ();
ProcessWithParentId [] procsWithParentId = ProcessWithParentId . Construct ( currentlyRunningProcs );
KillProcessAndChildProcesses ( processToKill , procsWithParentId );
return ;
}
try
{
processToKill . Kill ();
}
catch ( System . ComponentModel . Win32Exception )
{
try
{
// For processes running in an NTVDM, trying to kill with
2017-01-16 13:31:14 -08:00
// the original handle fails with a Win32 error, so we'll
2016-03-30 14:20:52 -07:00
// use the ID and try to get a new handle...
Process newHandle = Process . GetProcessById ( processToKill . Id );
// If the process was not found, we won't get here...
newHandle . Kill ();
}
2016-11-01 15:24:07 -07:00
catch ( Exception )
2016-03-30 14:20:52 -07:00
{
}
}
2016-11-01 15:24:07 -07:00
catch ( Exception )
2016-03-30 14:20:52 -07:00
{
}
}
/// <summary>
/// Used by remote server to kill a process tree given
/// a process id. Process class does not have ParentId
/// property, so this wrapper uses WMI to get ParentId
/// and wraps the original process.
/// </summary>
internal struct ProcessWithParentId
{
public Process OriginalProcessInstance ;
2016-07-29 13:02:49 -07:00
private int _parentId ;
2016-03-30 14:20:52 -07:00
public int ParentId
{
get
{
// Construct parent id only once.
2016-07-29 13:02:49 -07:00
if ( int . MinValue == _parentId )
2016-03-30 14:20:52 -07:00
{
ConstructParentId ();
}
2018-12-23 22:20:06 -08:00
2016-07-29 13:02:49 -07:00
return _parentId ;
2016-03-30 14:20:52 -07:00
}
}
public ProcessWithParentId ( Process originalProcess )
{
OriginalProcessInstance = originalProcess ;
2016-07-29 13:02:49 -07:00
_parentId = int . MinValue ;
2016-03-30 14:20:52 -07:00
}
public static ProcessWithParentId [] Construct ( Process [] originalProcCollection )
{
ProcessWithParentId [] result = new ProcessWithParentId [ originalProcCollection . Length ];
for ( int index = 0 ; index < originalProcCollection . Length ; index ++)
{
result [ index ] = new ProcessWithParentId ( originalProcCollection [ index ]);
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return result ;
}
private void ConstructParentId ()
{
try
{
2016-08-28 05:44:55 +00:00
// note that we have tried to retrieved parent id once.
// retrieving parent id might throw exceptions..so
2016-03-30 14:20:52 -07:00
// setting this to -1 so that we dont try again to
// get the parent id.
2016-07-29 13:02:49 -07:00
_parentId = - 1 ;
2016-03-30 14:20:52 -07:00
Process parentProcess = PsUtils . GetParentProcess ( OriginalProcessInstance );
if ( parentProcess != null )
{
2016-07-29 13:02:49 -07:00
_parentId = parentProcess . Id ;
2016-03-30 14:20:52 -07:00
}
}
catch ( Win32Exception )
{
}
catch ( InvalidOperationException )
{
}
catch ( Microsoft . Management . Infrastructure . CimException )
{
}
}
}
/// <summary>
/// Kills the process tree (process + associated child processes)
/// </summary>
/// <param name="processToKill"></param>
/// <param name="currentlyRunningProcs"></param>
2016-07-29 13:02:49 -07:00
private static void KillProcessAndChildProcesses ( Process processToKill ,
2016-03-30 14:20:52 -07:00
ProcessWithParentId [] currentlyRunningProcs )
{
try
{
// Kill children first..
int processId = processToKill . Id ;
KillChildProcesses ( processId , currentlyRunningProcs );
// kill the parent after children terminated.
processToKill . Kill ();
}
catch ( System . ComponentModel . Win32Exception )
{
try
{
// For processes running in an NTVDM, trying to kill with
2017-01-16 13:31:14 -08:00
// the original handle fails with a Win32 error, so we'll
2016-03-30 14:20:52 -07:00
// use the ID and try to get a new handle...
Process newHandle = Process . GetProcessById ( processToKill . Id );
// If the process was not found, we won't get here...
newHandle . Kill ();
}
2016-11-01 15:24:07 -07:00
catch ( Exception )
2016-03-30 14:20:52 -07:00
{
}
}
2016-11-01 15:24:07 -07:00
catch ( Exception )
2016-03-30 14:20:52 -07:00
{
}
}
2016-07-29 13:02:49 -07:00
private static void KillChildProcesses ( int parentId , ProcessWithParentId [] currentlyRunningProcs )
2016-03-30 14:20:52 -07:00
{
foreach ( ProcessWithParentId proc in currentlyRunningProcs )
{
if (( proc . ParentId > 0 ) && ( proc . ParentId == parentId ))
{
KillProcessAndChildProcesses ( proc . OriginalProcessInstance , currentlyRunningProcs );
}
}
}
#endregion
#region checkForConsoleApplication
/// <summary>
/// Return true if the passed in process is a console process.
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
2016-07-29 13:02:49 -07:00
private static bool IsConsoleApplication ( string fileName )
2016-03-30 14:20:52 -07:00
{
return ! IsWindowsApplication ( fileName );
}
/// <summary>
/// Check if the passed in process is a windows application.
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
2016-04-28 13:41:25 -07:00
[ArchitectureSensitive]
2016-07-29 13:02:49 -07:00
private static bool IsWindowsApplication ( string fileName )
2016-03-30 14:20:52 -07:00
{
2017-08-10 13:32:57 -07:00
if (! Platform . IsWindowsDesktop ) { return false ; }
2016-11-01 12:57:52 -07:00
2016-03-30 14:20:52 -07:00
SHFILEINFO shinfo = new SHFILEINFO ();
IntPtr type = SHGetFileInfo ( fileName , 0 , ref shinfo , ( uint ) Marshal . SizeOf ( shinfo ), SHGFI_EXETYPE );
switch (( int ) type )
{
case 0x0 :
// 0x0 = not an exe
return false ;
case 0x5a4d :
// 0x5a4d - DOS .exe or .com file
return false ;
case 0x4550 :
// 0x4550 - windows console app or bat file
return false ;
default :
// anything else - is a windows program...
return true ;
}
}
#endregion checkForConsoleApplication
/// <summary>
/// This is set to true when StopProcessing is called
/// </summary>
2016-07-29 13:02:49 -07:00
private bool _stopped = false ;
2016-03-30 14:20:52 -07:00
/// <summary>
/// Routine used to stop this processing on this node...
/// </summary>
internal void StopProcessing ()
{
2016-07-29 13:02:49 -07:00
lock ( _sync )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
if ( _stopped ) return ;
_stopped = true ;
2016-03-30 14:20:52 -07:00
}
2016-07-29 13:02:49 -07:00
if ( _nativeProcess != null )
2016-03-30 14:20:52 -07:00
{
if (! _runStandAlone )
{
//Stop input writer
2016-07-29 13:02:49 -07:00
_inputWriter . Stop ();
2016-03-30 14:20:52 -07:00
2016-07-29 13:02:49 -07:00
KillProcess ( _nativeProcess );
2016-03-30 14:20:52 -07:00
}
}
}
2017-01-16 13:31:14 -08:00
#endregion internal overrides
2016-03-30 14:20:52 -07:00
/// <summary>
/// Aggressively clean everything up...
/// </summary>
2016-07-29 13:02:49 -07:00
private void CleanUp ()
2016-03-30 14:20:52 -07:00
{
2017-08-10 13:32:57 -07:00
// We need to call 'NotifyEndApplication' as appropriate during cleanup
if ( _hasNotifiedBeginApplication )
{
this . Command . Context . EngineHostInterface . NotifyEndApplication ();
}
2016-07-29 13:02:49 -07:00
try
2016-03-30 14:20:52 -07:00
{
2017-08-10 13:32:57 -07:00
// Dispose the process if it's already created
2016-07-29 13:02:49 -07:00
if ( _nativeProcess != null )
2016-03-30 14:20:52 -07:00
{
2016-07-29 13:02:49 -07:00
_nativeProcess . Dispose ();
2016-03-30 14:20:52 -07:00
}
}
2016-11-01 15:24:07 -07:00
catch ( Exception )
2016-03-30 14:20:52 -07:00
{
}
}
2016-10-10 18:57:39 -07:00
private void ProcessOutputRecord ( ProcessOutputObject outputValue )
2016-03-30 14:20:52 -07:00
{
2016-10-10 18:57:39 -07:00
Dbg . Assert ( outputValue != null , "only object of type ProcessOutputObject expected" );
2017-01-16 13:31:14 -08:00
2016-10-10 18:57:39 -07:00
if ( outputValue . Stream == MinishellStream . Error )
2016-03-30 14:20:52 -07:00
{
2016-10-10 18:57:39 -07:00
ErrorRecord record = outputValue . Data as ErrorRecord ;
Dbg . Assert ( record != null , "ProcessReader should ensure that data is ErrorRecord" );
record . SetInvocationInfo ( this . Command . MyInvocation );
this . commandRuntime . _WriteErrorSkipAllowCheck ( record , isNativeError : true );
}
else if ( outputValue . Stream == MinishellStream . Output )
{
this . commandRuntime . _WriteObjectSkipAllowCheck ( outputValue . Data );
}
else if ( outputValue . Stream == MinishellStream . Debug )
{
string temp = outputValue . Data as string ;
Dbg . Assert ( temp != null , "ProcessReader should ensure that data is string" );
this . Command . PSHostInternal . UI . WriteDebugLine ( temp );
}
else if ( outputValue . Stream == MinishellStream . Verbose )
{
string temp = outputValue . Data as string ;
Dbg . Assert ( temp != null , "ProcessReader should ensure that data is string" );
this . Command . PSHostInternal . UI . WriteVerboseLine ( temp );
}
else if ( outputValue . Stream == MinishellStream . Warning )
{
string temp = outputValue . Data as string ;
Dbg . Assert ( temp != null , "ProcessReader should ensure that data is string" );
this . Command . PSHostInternal . UI . WriteWarningLine ( temp );
}
else if ( outputValue . Stream == MinishellStream . Progress )
{
PSObject temp = outputValue . Data as PSObject ;
if ( temp != null )
2016-03-30 14:20:52 -07:00
{
2016-10-10 18:57:39 -07:00
long sourceId = 0 ;
PSMemberInfo info = temp . Properties [ "SourceId" ];
if ( info != null )
2016-03-30 14:20:52 -07:00
{
2016-10-10 18:57:39 -07:00
sourceId = ( long ) info . Value ;
}
2018-12-23 22:20:06 -08:00
2016-10-10 18:57:39 -07:00
info = temp . Properties [ "Record" ];
ProgressRecord rec = null ;
if ( info != null )
{
rec = info . Value as ProgressRecord ;
}
2018-12-23 22:20:06 -08:00
2016-10-10 18:57:39 -07:00
if ( rec != null )
{
this . Command . PSHostInternal . UI . WriteProgress ( sourceId , rec );
2016-03-30 14:20:52 -07:00
}
}
2016-10-10 18:57:39 -07:00
}
else if ( outputValue . Stream == MinishellStream . Information )
{
InformationRecord record = outputValue . Data as InformationRecord ;
Dbg . Assert ( record != null , "ProcessReader should ensure that data is InformationRecord" );
this . commandRuntime . WriteInformation ( record );
}
2016-03-30 14:20:52 -07:00
}
/// <summary>
/// Gets the start info for process
/// </summary>
/// <param name="redirectOutput"></param>
/// <param name="redirectError"></param>
/// <param name="redirectInput"></param>
/// <param name="soloCommand"></param>
/// <returns></returns>
private ProcessStartInfo GetProcessStartInfo ( bool redirectOutput , bool redirectError , bool redirectInput , bool soloCommand )
{
ProcessStartInfo startInfo = new ProcessStartInfo ();
startInfo . FileName = this . Path ;
2018-09-14 08:03:56 +05:00
if ( IsExecutable ( this . Path ))
2016-03-30 14:20:52 -07:00
{
startInfo . UseShellExecute = false ;
if ( redirectInput )
{
startInfo . RedirectStandardInput = true ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
if ( redirectOutput )
{
startInfo . RedirectStandardOutput = true ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
if ( redirectError )
{
startInfo . RedirectStandardError = true ;
}
}
else
{
2017-08-10 13:32:57 -07:00
if ( Platform . IsNanoServer || Platform . IsIoT )
{
// Shell doesn't exist on headless SKUs, so documents cannot be associated with an application.
// Therefore, we cannot run document in this case.
throw InterpreterError . NewInterpreterException ( this . Path , typeof ( RuntimeException ),
this . Command . InvocationExtent , "CantActivateDocumentInPowerShellCore" , ParserStrings . CantActivateDocumentInPowerShellCore , this . Path );
}
2016-03-30 14:20:52 -07:00
// We only want to ShellExecute something that is standalone...
if (! soloCommand )
{
throw InterpreterError . NewInterpreterException ( this . Path , typeof ( RuntimeException ),
this . Command . InvocationExtent , "CantActivateDocumentInPipeline" , ParserStrings . CantActivateDocumentInPipeline , this . Path );
}
startInfo . UseShellExecute = true ;
}
//For minishell value of -outoutFormat parameter depends on value of redirectOutput.
//So we delay the parameter binding. Do parameter binding for minishell now.
2016-07-29 13:02:49 -07:00
if ( _isMiniShell )
2016-03-30 14:20:52 -07:00
{
MinishellParameterBinderController mpc = ( MinishellParameterBinderController ) NativeParameterBinderController ;
mpc . BindParameters ( arguments , redirectOutput , this . Command . Context . EngineHostInterface . Name );
startInfo . CreateNoWindow = mpc . NonInteractive ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
startInfo . Arguments = NativeParameterBinderController . Arguments ;
ExecutionContext context = this . Command . Context ;
// Start command in the current filesystem directory
string rawPath =
context . EngineSessionState . GetNamespaceCurrentLocation (
context . ProviderNames . FileSystem ). ProviderPath ;
startInfo . WorkingDirectory = WildcardPattern . Unescape ( rawPath );
return startInfo ;
}
private bool IsDownstreamOutDefault ( Pipe downstreamPipe )
{
Diagnostics . Assert ( downstreamPipe != null , "Caller makes sure the passed-in parameter is not null." );
// Check if the downstream cmdlet is Out-Default, which is the default outputter.
CommandProcessorBase outputProcessor = downstreamPipe . DownstreamCmdlet ;
if ( outputProcessor != null )
{
// We have the test 'utscript\Engine\TestOutDefaultRedirection.ps1' to check that a user defined
2017-01-16 13:31:14 -08:00
// Out-Default function should not cause a native command to be redirected. So here we should only
2016-03-30 14:20:52 -07:00
// compare the command name to avoid breaking change.
if ( String . Equals ( outputProcessor . CommandInfo . Name , "Out-Default" , StringComparison . OrdinalIgnoreCase ))
{
2016-08-28 05:44:55 +00:00
// Verify that this isn't an Out-Default added for transcribing
2016-03-30 14:20:52 -07:00
if (! outputProcessor . Command . MyInvocation . BoundParameters . ContainsKey ( "Transcript" ))
{
return true ;
}
}
}
return false ;
}
/// <summary>
/// This method calculates if input and output of the process are redirected
/// </summary>
/// <param name="redirectOutput"></param>
/// <param name="redirectError"></param>
/// <param name="redirectInput"></param>
private void CalculateIORedirection ( out bool redirectOutput , out bool redirectError , out bool redirectInput )
{
2016-11-21 16:02:52 -08:00
redirectInput = this . Command . MyInvocation . ExpectingInput ;
2016-03-30 14:20:52 -07:00
redirectOutput = true ;
redirectError = true ;
// Figure out if we're going to run this process "standalone" i.e. without
// redirecting anything. This is a bit tricky as we always run redirected so
// we have to see if the redirection is actually being done at the topmost level or not.
2016-08-28 05:44:55 +00:00
// If we're eligible to be running standalone, that is, without redirection
2016-03-30 14:20:52 -07:00
// use our pipeline position to determine if we really want to redirect
// input and error or not. If we're first in the pipeline, then we don't
// redirect input. If we're last in the pipeline, we don't redirect output.
if ( this . Command . MyInvocation . PipelinePosition == this . Command . MyInvocation . PipelineLength )
{
// If the output pipe is the default outputter, for example, calling the native command from command-line host,
2016-08-28 05:44:55 +00:00
// then we're possibly running standalone.
2016-03-30 14:20:52 -07:00
//
2017-01-16 13:31:14 -08:00
// If the downstream cmdlet is explicitly Out-Default, for example:
// $powershell.AddScript('ipconfig.exe')
// $powershell.AddCommand('Out-Default')
2016-03-30 14:20:52 -07:00
// $powershell.Invoke())
// we should not count it as a redirection.
if ( IsDownstreamOutDefault ( this . commandRuntime . OutputPipe ))
{
redirectOutput = false ;
}
}
// See if the error output stream has been redirected, either through an explicit 2> foo.txt or
// my merging error into output through 2>&1.
if ( CommandRuntime . ErrorMergeTo != MshCommandRuntime . MergeDataStream . Output )
{
// If the error output pipe is the default outputter, for example, calling the native command from command-line host,
2016-08-28 05:44:55 +00:00
// then we're possibly running standalone.
2016-03-30 14:20:52 -07:00
//
2017-01-16 13:31:14 -08:00
// If the downstream cmdlet is explicitly Out-Default, for example:
// $powershell.AddScript('ipconfig.exe')
// $powershell.AddCommand('Out-Default')
2016-03-30 14:20:52 -07:00
// $powershell.Invoke())
// we should not count that as a redirection.
if ( IsDownstreamOutDefault ( this . commandRuntime . ErrorOutputPipe ))
{
redirectError = false ;
}
}
2017-01-16 13:31:14 -08:00
//In minishell scenario, if output is redirected
2016-03-30 14:20:52 -07:00
//then error should also be redirected.
2016-07-29 13:02:49 -07:00
if ( redirectError == false && redirectOutput == true && _isMiniShell )
2016-03-30 14:20:52 -07:00
{
redirectError = true ;
}
// Remoting server consideration.
// Currently, the WinRM is using std io pipes to communicate with PowerShell server.
// To protect these std io pipes from access from user command, we have replaced the original std io pipes with null pipes.
// The original std io pipes are taken private, to be used by remoting infrastructure only.
2017-01-16 13:31:14 -08:00
// Doing so prevents user data to corrupt PowerShell remoting communication data which are encoded in a
2016-03-30 14:20:52 -07:00
// special format.
// In the following, we check for this server condition.
// If it is the server, then we redirect all std io handles for the native command.
if ( NativeCommandProcessor . IsServerSide )
{
redirectInput = true ;
redirectOutput = true ;
redirectError = true ;
}
2017-08-10 13:32:57 -07:00
else if ( Platform . IsWindowsDesktop && IsConsoleApplication ( this . Path ))
2016-03-30 14:20:52 -07:00
{
2017-08-10 13:32:57 -07:00
// On Windows desktops, if the command to run is a console application,
// then allocate a console if there isn't one attached already...
2016-03-30 14:20:52 -07:00
ConsoleVisibility . AllocateHiddenConsole ();
if ( ConsoleVisibility . AlwaysCaptureApplicationIO )
{
redirectOutput = true ;
redirectError = true ;
}
}
2017-08-10 13:32:57 -07:00
_runStandAlone = ! redirectInput && ! redirectOutput && ! redirectError ;
2017-09-28 17:15:37 -07:00
if ( _runStandAlone )
{
2018-06-26 02:37:09 +05:00
if ( s_supportScreenScrape == null )
2017-09-28 17:15:37 -07:00
{
try
{
_startPosition = this . Command . Context . EngineHostInterface . UI . RawUI . CursorPosition ;
Host . BufferCell [,] bufferContents = this . Command . Context . EngineHostInterface . UI . RawUI . GetBufferContents (
new Host . Rectangle ( _startPosition , _startPosition ));
s_supportScreenScrape = true ;
}
catch ( Exception )
{
s_supportScreenScrape = false ;
}
}
// if screen scraping isn't supported, we enable redirection so that the output is still transcribed
// as redirected output is always transcribed
if ( _isTranscribing && ( false == s_supportScreenScrape ))
{
redirectOutput = true ;
redirectError = true ;
_runStandAlone = false ;
}
}
2016-03-30 14:20:52 -07:00
}
2018-09-14 08:03:56 +05:00
// On Windows, check the extension list and see if we should try to execute this directly.
// Otherwise, use the platform library to check executability
private bool IsExecutable ( string path )
2016-03-30 14:20:52 -07:00
{
2018-09-14 08:03:56 +05:00
#if UNIX
return Platform . NonWindowsIsExecutable ( this . Path );
#else
2016-03-30 14:20:52 -07:00
string myExtension = System . IO . Path . GetExtension ( path );
2018-09-14 08:03:56 +05:00
var pathext = Environment . GetEnvironmentVariable ( "PATHEXT" );
2016-03-30 14:20:52 -07:00
string [] extensionList ;
2018-09-14 08:03:56 +05:00
if ( string . IsNullOrEmpty ( pathext ))
2016-03-30 14:20:52 -07:00
{
extensionList = new string [] { ".exe" , ".com" , ".bat" , ".cmd" };
}
else
{
extensionList = pathext . Split ( Utils . Separators . Semicolon );
}
2018-09-14 08:03:56 +05:00
2016-03-30 14:20:52 -07:00
foreach ( string extension in extensionList )
{
2018-09-14 08:03:56 +05:00
if ( string . Equals ( extension , myExtension , StringComparison . OrdinalIgnoreCase ))
2016-03-30 14:20:52 -07:00
{
return true ;
}
}
2018-09-14 08:03:56 +05:00
2016-03-30 14:20:52 -07:00
return false ;
2018-09-14 08:03:56 +05:00
#endif
2016-03-30 14:20:52 -07:00
}
#region Interop for FindExecutable ...
// Constant used to determine the buffer size for a path
// when looking for an executable. MAX_PATH is defined as 260
// so this is much larger than what should be permitted
private const int MaxExecutablePath = 1024 ;
// The FindExecutable API is defined in shellapi.h as
// SHSTDAPI_(HINSTANCE) FindExecutableW(LPCWSTR lpFile, LPCWSTR lpDirectory, __out_ecount(MAX_PATH) LPWSTR lpResult);
// HINSTANCE is void* so we need to use IntPtr as API return value.
[DllImport("shell32.dll", EntryPoint = "FindExecutable")]
[SuppressMessage("Microsoft.Globalization", "CA2101:SpecifyMarshalingForPInvokeStringArguments", MessageId = "0")]
[SuppressMessage("Microsoft.Globalization", "CA2101:SpecifyMarshalingForPInvokeStringArguments", MessageId = "1")]
[SuppressMessage("Microsoft.Globalization", "CA2101:SpecifyMarshalingForPInvokeStringArguments", MessageId = "2")]
private static extern IntPtr FindExecutableW (
string fileName , string directoryPath , StringBuilder pathFound );
2016-04-28 13:41:25 -07:00
[ArchitectureSensitive]
2016-03-30 14:20:52 -07:00
private static string FindExecutable ( string filename )
{
// Preallocate a
2016-07-29 13:02:49 -07:00
StringBuilder objResultBuffer = new StringBuilder ( MaxExecutablePath );
2016-03-30 14:20:52 -07:00
IntPtr resultCode = ( IntPtr ) 0 ;
try
{
resultCode = FindExecutableW ( filename , string . Empty , objResultBuffer );
}
catch ( System . IndexOutOfRangeException e )
{
// If we got an index-out-of-range exception here, it's because
// of a buffer overrun error so we fail fast instead of
// continuing to run in an possibly unstable environment....
2016-11-01 15:24:07 -07:00
Environment . FailFast ( e . Message , e );
2016-03-30 14:20:52 -07:00
}
// If FindExecutable returns a result >= 32, then it succeeded
// and we return the string that was found, otherwise we
// return null.
if (( long ) resultCode >= 32 )
{
return objResultBuffer . ToString ();
}
return null ;
}
#endregion
#region Interop for SHGetFileInfo
private const int SCS_32BIT_BINARY = 0 ; // A 32-bit Windows-based application
2016-07-29 13:02:49 -07:00
private const int SCS_DOS_BINARY = 1 ; // An MS-DOS - based application
private const int SCS_WOW_BINARY = 2 ; // A 16-bit Windows-based application
private const int SCS_PIF_BINARY = 3 ; // A PIF file that executes an MS-DOS - based application
2016-03-30 14:20:52 -07:00
private const int SCS_POSIX_BINARY = 4 ; // A POSIX - based application
private const int SCS_OS216_BINARY = 5 ; // A 16-bit OS/2-based application
private const int SCS_64BIT_BINARY = 6 ; // A 64-bit Windows-based application.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct SHFILEINFO
{
public IntPtr hIcon ;
public int iIcon ;
public uint dwAttributes ;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szDisplayName ;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string szTypeName ;
};
private const uint SHGFI_EXETYPE = 0x000002000 ; // flag used to ask to return exe type
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
2016-07-29 13:02:49 -07:00
private static extern IntPtr SHGetFileInfo ( string pszPath , uint dwFileAttributes ,
2016-03-30 14:20:52 -07:00
ref SHFILEINFO psfi , uint cbSizeFileInfo , uint uFlags );
#endregion
#region Minishell Interop
2016-07-29 13:02:49 -07:00
private bool _isMiniShell = false ;
2016-03-30 14:20:52 -07:00
/// <summary>
/// Returns true if native command being invoked is mini-shell.
/// </summary>
/// <returns></returns>
/// <remarks>
2017-01-16 13:31:14 -08:00
/// If any of the argument supplied to native command is script block,
2016-03-30 14:20:52 -07:00
/// we assume it is minishell.
/// </remarks>
private bool IsMiniShell ()
{
for ( int i = 0 ; i < arguments . Count ; i ++)
{
CommandParameterInternal arg = arguments [ i ];
if (! arg . ParameterNameSpecified )
{
if ( arg . ArgumentValue is ScriptBlock )
{
return true ;
}
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return false ;
}
#endregion Minishell Interop
2016-08-02 13:29:09 -07:00
internal static bool IsServerSide { get ; set ; }
2016-03-30 14:20:52 -07:00
}
2016-11-11 13:20:47 -08:00
internal class ProcessOutputHandler
{
2016-11-11 13:43:50 -08:00
internal const string XmlCliTag = "#< CLIXML" ;
2016-11-11 13:20:47 -08:00
private int _refCount ;
private BlockingCollection < ProcessOutputObject > _queue ;
private bool _isFirstOutput ;
private bool _isFirstError ;
private bool _isXmlCliOutput ;
private bool _isXmlCliError ;
private string _processFileName ;
public ProcessOutputHandler ( Process process , BlockingCollection < ProcessOutputObject > queue )
{
Debug . Assert ( process . StartInfo . RedirectStandardOutput || process . StartInfo . RedirectStandardError , "Caller should redirect at least one stream" );
_refCount = 0 ;
_processFileName = process . StartInfo . FileName ;
_queue = queue ;
// we incrementing refCount on the same thread and before running any processing
// so it's safe to do it without Interlocked.
if ( process . StartInfo . RedirectStandardOutput ) { _refCount ++; }
2018-12-23 22:20:06 -08:00
2016-11-11 13:20:47 -08:00
if ( process . StartInfo . RedirectStandardError ) { _refCount ++; }
// once we have _refCount, we can start processing
if ( process . StartInfo . RedirectStandardOutput )
{
_isFirstOutput = true ;
_isXmlCliOutput = false ;
process . OutputDataReceived += new DataReceivedEventHandler ( OutputHandler );
process . BeginOutputReadLine ();
}
if ( process . StartInfo . RedirectStandardError )
{
_isFirstError = true ;
_isXmlCliError = false ;
process . ErrorDataReceived += new DataReceivedEventHandler ( ErrorHandler );
process . BeginErrorReadLine ();
}
}
private void decrementRefCount ()
{
Debug . Assert ( _refCount > 0 , "RefCount should always be positive, when we are trying to decrement it" );
if ( Interlocked . Decrement ( ref _refCount ) == 0 )
{
_queue . CompleteAdding ();
}
}
private void OutputHandler ( object sender , DataReceivedEventArgs outputReceived )
{
if ( outputReceived . Data != null )
{
if ( _isFirstOutput )
{
_isFirstOutput = false ;
if ( string . Equals ( outputReceived . Data , XmlCliTag , StringComparison . Ordinal ))
{
_isXmlCliOutput = true ;
return ;
}
}
if ( _isXmlCliOutput )
{
foreach ( var record in DeserializeCliXmlObject ( outputReceived . Data , true ))
{
_queue . Add ( record );
}
}
else
{
_queue . Add ( new ProcessOutputObject ( outputReceived . Data , MinishellStream . Output ));
}
}
else
{
decrementRefCount ();
}
}
private void ErrorHandler ( object sender , DataReceivedEventArgs errorReceived )
{
if ( errorReceived . Data != null )
{
if ( string . Equals ( errorReceived . Data , XmlCliTag , StringComparison . Ordinal ))
{
_isXmlCliError = true ;
return ;
}
if ( _isXmlCliError )
{
foreach ( var record in DeserializeCliXmlObject ( errorReceived . Data , false ))
{
_queue . Add ( record );
}
}
else
{
ErrorRecord errorRecord ;
if ( _isFirstError )
{
_isFirstError = false ;
// Produce a regular error record for the first line of the output
errorRecord = new ErrorRecord ( new RemoteException ( errorReceived . Data ), "NativeCommandError" , ErrorCategory . NotSpecified , errorReceived . Data );
}
else
{
// Wrap the rest of the output in ErrorRecords with the "NativeCommandErrorMessage" error ID
errorRecord = new ErrorRecord ( new RemoteException ( errorReceived . Data ), "NativeCommandErrorMessage" , ErrorCategory . NotSpecified , null );
}
_queue . Add ( new ProcessOutputObject ( errorRecord , MinishellStream . Error ));
}
}
else
{
decrementRefCount ();
}
}
private List < ProcessOutputObject > DeserializeCliXmlObject ( string xml , bool isOutput )
{
var result = new List < ProcessOutputObject >();
try
{
using ( var streamReader = new MemoryStream ( Encoding . UTF8 . GetBytes ( xml )))
{
XmlReader xmlReader = XmlReader . Create ( streamReader , InternalDeserializer . XmlReaderSettingsForCliXml );
Deserializer des = new Deserializer ( xmlReader );
while (! des . Done ())
{
string streamName ;
object obj = des . Deserialize ( out streamName );
//Decide the stream to which data belongs
MinishellStream stream = MinishellStream . Unknown ;
if ( streamName != null )
{
stream = StringToMinishellStreamConverter . ToMinishellStream ( streamName );
}
2018-12-23 22:20:06 -08:00
2016-11-11 13:20:47 -08:00
if ( stream == MinishellStream . Unknown )
{
stream = isOutput ? MinishellStream . Output : MinishellStream . Error ;
}
//Null is allowed only in output stream
if ( stream != MinishellStream . Output && obj == null )
{
continue ;
}
if ( stream == MinishellStream . Error )
{
if ( obj is PSObject )
{
obj = ErrorRecord . FromPSObjectForRemoting ( PSObject . AsPSObject ( obj ));
}
else
{
string errorMessage = null ;
try
{
errorMessage = ( string ) LanguagePrimitives . ConvertTo ( obj , typeof ( string ), CultureInfo . InvariantCulture );
}
catch ( PSInvalidCastException )
{
continue ;
}
2018-12-23 22:20:06 -08:00
2016-11-11 13:20:47 -08:00
obj = new ErrorRecord ( new RemoteException ( errorMessage ),
"NativeCommandError" , ErrorCategory . NotSpecified , errorMessage );
}
}
else if ( stream == MinishellStream . Information )
{
if ( obj is PSObject )
{
obj = InformationRecord . FromPSObjectForRemoting ( PSObject . AsPSObject ( obj ));
}
else
{
string messageData = null ;
try
{
messageData = ( string ) LanguagePrimitives . ConvertTo ( obj , typeof ( string ), CultureInfo . InvariantCulture );
}
catch ( PSInvalidCastException )
{
continue ;
}
obj = new InformationRecord ( messageData , null );
}
}
else if ( stream == MinishellStream . Debug ||
stream == MinishellStream . Verbose ||
stream == MinishellStream . Warning )
{
//Convert to string
try
{
obj = LanguagePrimitives . ConvertTo ( obj , typeof ( string ), CultureInfo . InvariantCulture );
}
catch ( PSInvalidCastException )
{
continue ;
}
}
2018-12-23 22:20:06 -08:00
2016-11-11 13:20:47 -08:00
result . Add ( new ProcessOutputObject ( obj , stream ));
}
}
}
catch ( XmlException originalException )
{
string template = NativeCP . CliXmlError ;
string message = string . Format (
null ,
template ,
isOutput ? MinishellStream . Output : MinishellStream . Error ,
_processFileName ,
originalException . Message );
XmlException newException = new XmlException (
message ,
originalException );
ErrorRecord error = new ErrorRecord (
newException ,
"ProcessStreamReader_CliXmlError" ,
ErrorCategory . SyntaxError ,
_processFileName );
result . Add ( new ProcessOutputObject ( error , MinishellStream . Error ));
}
return result ;
}
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Helper class to handle writing input to a process.
/// </summary>
internal class ProcessInputWriter
{
2017-01-16 13:31:14 -08:00
#region constructor
2016-07-29 13:02:49 -07:00
private InternalCommand _command ;
2016-03-30 14:20:52 -07:00
/// <summary>
2017-01-16 13:31:14 -08:00
/// Creates an instance of ProcessInputWriter
2016-03-30 14:20:52 -07:00
/// </summary>
internal ProcessInputWriter ( InternalCommand command )
{
Dbg . Assert ( command != null , "Caller should validate the parameter" );
2016-07-29 13:02:49 -07:00
_command = command ;
2016-03-30 14:20:52 -07:00
}
#endregion constructor
2016-10-10 18:57:39 -07:00
private SteppablePipeline _pipeline ;
2016-11-11 13:43:50 -08:00
private Serializer _xmlSerializer ;
2016-10-10 18:57:39 -07:00
2016-03-30 14:20:52 -07:00
/// <summary>
/// Add an object to write to process
/// </summary>
/// <param name="input"></param>
internal void Add ( object input )
{
2016-10-12 19:12:54 -07:00
if ( _stopping || _streamWriter == null )
{
2016-11-15 15:14:09 -08:00
// if _streamWriter is already null, then we already called Dispose()
2016-10-12 19:12:54 -07:00
// so we should just discard the input.
return ;
}
2016-11-11 13:43:50 -08:00
if ( _inputFormat == NativeCommandIOFormat . Text )
{
AddTextInput ( input );
}
else // Xml
{
AddXmlInput ( input );
}
}
private void AddTextInput ( object input )
{
2016-11-11 14:23:29 -08:00
AddTextInputFromFormattedArray ( _pipeline . Process ( input ));
}
private void AddTextInputFromFormattedArray ( Array formattedObjects )
{
2016-10-10 18:57:39 -07:00
foreach ( var item in formattedObjects )
2016-03-30 14:20:52 -07:00
{
2016-10-10 18:57:39 -07:00
string line = PSObject . ToStringParser ( _command . Context , item );
2016-10-21 18:45:29 -07:00
// if process is already finished and we are trying to write something to it,
// we will get IOException
try
{
_streamWriter . WriteLine ( line );
}
catch ( IOException )
{
// we are assuming that process is already finished
// we should just stop processing at this point
2016-11-15 15:14:09 -08:00
this . Dispose ();
2016-10-21 18:45:29 -07:00
// stop foreach execution
break ;
}
2016-03-30 14:20:52 -07:00
}
}
2016-11-11 13:43:50 -08:00
private void AddXmlInput ( object input )
{
try
{
_xmlSerializer . Serialize ( input );
}
catch ( IOException )
{
// we are assuming that process is already finished
// we should just stop processing at this point
2016-11-15 15:14:09 -08:00
this . Dispose ();
2016-11-11 13:43:50 -08:00
}
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Stream to which input is written
/// </summary>
2016-07-29 13:02:49 -07:00
private StreamWriter _streamWriter ;
2016-03-30 14:20:52 -07:00
/// <summary>
/// Format of input.
/// </summary>
2016-07-29 13:02:49 -07:00
private NativeCommandIOFormat _inputFormat ;
2016-03-30 14:20:52 -07:00
/// <summary>
/// Start writing input to process
/// </summary>
/// <param name="process">
/// process to which input is written
/// </param>
/// <param name="inputFormat">
/// </param>
internal void Start ( Process process , NativeCommandIOFormat inputFormat )
{
2016-10-10 18:57:39 -07:00
Dbg . Assert ( process != null , "caller should validate the paramter" );
2016-03-30 14:20:52 -07:00
2017-01-16 13:31:14 -08:00
//Get the encoding for writing to native command. Note we get the Encoding
2016-03-30 14:20:52 -07:00
//from the current scope so a script or function can use a different encoding
//than global value.
2016-08-02 22:25:13 -07:00
Encoding pipeEncoding = _command . Context . GetVariableValue ( SpecialVariables . OutputEncodingVarPath ) as System . Text . Encoding ??
2017-11-07 14:49:14 -08:00
Utils . utf8NoBom ;
2016-03-30 14:20:52 -07:00
2016-10-12 19:12:54 -07:00
_streamWriter = new StreamWriter ( process . StandardInput . BaseStream , pipeEncoding );
_streamWriter . AutoFlush = true ;
2016-07-29 13:02:49 -07:00
_inputFormat = inputFormat ;
2016-11-11 13:43:50 -08:00
if ( _inputFormat == NativeCommandIOFormat . Xml )
{
_streamWriter . WriteLine ( ProcessOutputHandler . XmlCliTag );
_xmlSerializer = new Serializer ( XmlWriter . Create ( _streamWriter ));
}
else // Text
{
_pipeline = ScriptBlock . Create ( "Out-String -Stream" ). GetSteppablePipeline ();
_pipeline . Begin ( true );
}
2016-03-30 14:20:52 -07:00
}
2016-10-10 18:57:39 -07:00
bool _stopping = false ;
2016-03-30 14:20:52 -07:00
/// <summary>
/// Stop writing input to process
/// </summary>
internal void Stop ()
{
2016-07-29 13:02:49 -07:00
_stopping = true ;
2016-03-30 14:20:52 -07:00
}
2016-11-15 15:14:09 -08:00
internal void Dispose ()
2016-03-30 14:20:52 -07:00
{
2016-11-15 15:14:09 -08:00
// we allow call Dispose() multiply times.
2016-11-07 18:38:25 -08:00
// For example one time from ProcessRecord() code path,
2016-10-21 18:45:29 -07:00
// when we detect that process already finished
2016-11-15 15:14:09 -08:00
// and once from Done() code path.
// Even though Dispose() could be called multiple times,
// the calls are on the same thread, so there is no race condition
2016-10-21 18:45:29 -07:00
if ( _pipeline != null )
{
_pipeline . Dispose ();
_pipeline = null ;
}
2016-03-30 14:20:52 -07:00
2016-11-11 13:43:50 -08:00
if ( _xmlSerializer != null )
{
_xmlSerializer = null ;
}
2016-11-15 15:14:09 -08:00
// streamWriter can be null if we didn't call Start method
2016-10-10 18:57:39 -07:00
if ( _streamWriter != null )
2016-03-30 14:20:52 -07:00
{
2016-10-21 18:45:29 -07:00
try
{
_streamWriter . Dispose ();
}
catch ( IOException )
{
// on unix, if process is already finished attempt to dispose it will
// lead to "Broken pipe" exception.
// we are ignoring it here
}
2018-12-23 22:20:06 -08:00
2016-10-12 19:12:54 -07:00
_streamWriter = null ;
2016-03-30 14:20:52 -07:00
}
}
2016-11-15 15:14:09 -08:00
internal void Done ()
{
if ( _inputFormat == NativeCommandIOFormat . Xml )
{
if ( _xmlSerializer != null )
{
_xmlSerializer . Done ();
}
}
else // Text
{
// if _pipeline == null, we already called Dispose(),
// for example, because downstream process finished
if ( _pipeline != null )
{
var finalResults = _pipeline . End ();
AddTextInputFromFormattedArray ( finalResults );
}
}
Dispose ();
}
2016-03-30 14:20:52 -07:00
}
2017-01-16 13:31:14 -08:00
2016-03-30 14:20:52 -07:00
/// <summary>
/// Static class that allows you to show and hide the console window
/// associated with this process.
/// </summary>
internal static class ConsoleVisibility
{
/// <summary>
/// If set to true, then native commands will always be run redirected...
/// </summary>
2016-08-02 13:29:09 -07:00
public static bool AlwaysCaptureApplicationIO { get ; set ; }
2016-03-30 14:20:52 -07:00
[DllImport("Kernel32.dll")]
internal static extern IntPtr GetConsoleWindow ();
internal const int SW_HIDE = 0 ;
internal const int SW_SHOWNORMAL = 1 ;
internal const int SW_NORMAL = 1 ;
internal const int SW_SHOWMINIMIZED = 2 ;
internal const int SW_SHOWMAXIMIZED = 3 ;
internal const int SW_MAXIMIZE = 3 ;
internal const int SW_SHOWNOACTIVATE = 4 ;
internal const int SW_SHOW = 5 ;
internal const int SW_MINIMIZE = 6 ;
internal const int SW_SHOWMINNOACTIVE = 7 ;
internal const int SW_SHOWNA = 8 ;
internal const int SW_RESTORE = 9 ;
internal const int SW_SHOWDEFAULT = 10 ;
internal const int SW_FORCEMINIMIZE = 11 ;
internal const int SW_MAX = 11 ;
/// <summary>
/// Code to control the display properties of the a window...
/// </summary>
/// <param name="hWnd">The window to show...</param>
/// <param name="nCmdShow">The command to do</param>
2018-12-25 21:43:03 +05:00
/// <returns>True it it was successful.</returns>
2016-03-30 14:20:52 -07:00
[DllImport("user32.dll")]
2018-12-21 23:33:10 -08:00
internal static extern bool ShowWindow ( IntPtr hWnd , int nCmdShow );
2016-03-30 14:20:52 -07:00
/// <summary>
/// Code to allocate a console...
/// </summary>
2018-12-25 21:43:03 +05:00
/// <returns>True if a console was created...</returns>
2016-03-30 14:20:52 -07:00
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool AllocConsole ();
/// <summary>
/// Called to save the foreground window before allocating a hidden console window
/// </summary>
2018-12-25 21:43:03 +05:00
/// <returns>A handle to the foreground window.</returns>
2016-03-30 14:20:52 -07:00
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow ();
/// <summary>
/// Called to restore the foreground window after allocating a hidden console window
/// </summary>
/// <param name="hWnd">A handle to the window that should be activated and brought to the foreground.</param>
2018-12-25 21:43:03 +05:00
/// <returns>True if the window was brought to the foreground.</returns>
2016-03-30 14:20:52 -07:00
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
2016-07-29 13:02:49 -07:00
private static extern bool SetForegroundWindow ( IntPtr hWnd );
2016-03-30 14:20:52 -07:00
/// <summary>
/// If no console window is attached to this process, then allocate one,
/// hide it and return true. If there's already a console window attached, then
/// just return false.
/// </summary>
/// <returns></returns>
internal static bool AllocateHiddenConsole ()
{
// See if there is already a console attached.
IntPtr hwnd = ConsoleVisibility . GetConsoleWindow ();
if ( hwnd != IntPtr . Zero )
{
return false ;
}
// save the foreground window since allocating a console window might remove focus from it
IntPtr savedForeground = ConsoleVisibility . GetForegroundWindow ();
// Since there is no console window, allocate and then hide it...
// Suppress the PreFAST warning about not using Marshal.GetLastWin32Error() to
// get the error code.
#pragma warning disable 56523
ConsoleVisibility . AllocConsole ();
hwnd = ConsoleVisibility . GetConsoleWindow ();
bool returnValue ;
if ( hwnd == IntPtr . Zero )
{
returnValue = false ;
}
else
{
returnValue = true ;
ConsoleVisibility . ShowWindow ( hwnd , ConsoleVisibility . SW_HIDE );
AlwaysCaptureApplicationIO = true ;
}
if ( savedForeground != IntPtr . Zero && ConsoleVisibility . GetForegroundWindow () != savedForeground )
{
ConsoleVisibility . SetForegroundWindow ( savedForeground );
}
return returnValue ;
}
/// <summary>
/// If there is a console attached, then make it visible
/// and allow interactive console applications to be run.
/// </summary>
public static void Show ()
{
IntPtr hwnd = GetConsoleWindow ();
if ( hwnd != IntPtr . Zero )
{
ShowWindow ( hwnd , SW_SHOW );
AlwaysCaptureApplicationIO = false ;
}
else
{
throw PSTraceSource . NewInvalidOperationException ();
}
}
/// <summary>
/// If there is a console attached, then hide it and always capture
/// output from the child process.
/// </summary>
public static void Hide ()
{
IntPtr hwnd = GetConsoleWindow ();
if ( hwnd != IntPtr . Zero )
{
ShowWindow ( hwnd , SW_HIDE );
AlwaysCaptureApplicationIO = true ;
}
else
{
throw PSTraceSource . NewInvalidOperationException ();
}
}
}
/// <summary>
2017-01-16 13:31:14 -08:00
/// Exception used to wrap the error coming from
2016-03-30 14:20:52 -07:00
/// remote instance of Msh.
2017-01-16 13:31:14 -08:00
/// </summary>
2016-03-30 14:20:52 -07:00
/// <remarks>
2017-01-16 13:31:14 -08:00
/// This remote instance of Msh can be in a separate process,
2016-03-30 14:20:52 -07:00
/// appdomain or machine.
/// </remarks>
[Serializable]
[SuppressMessage("Microsoft.Usage", "CA2240:ImplementISerializableCorrectly")]
2016-07-29 13:02:49 -07:00
public class RemoteException : RuntimeException
2016-03-30 14:20:52 -07:00
{
/// <summary>
/// Initializes a new instance of RemoteException
/// </summary>
public RemoteException ()
: base ()
{
}
/// <summary>
2017-01-16 13:31:14 -08:00
/// Initializes a new instance of RemoteException with a specified error message.
2016-03-30 14:20:52 -07:00
/// </summary>
/// <param name="message">
2017-01-16 13:31:14 -08:00
/// The message that describes the error.
2016-03-30 14:20:52 -07:00
/// </param>
public RemoteException ( string message )
: base ( message )
{
}
/// <summary>
2017-01-16 13:31:14 -08:00
/// Initializes a new instance of the RemoteException class
/// with a specified error message and a reference to the inner exception
/// that is the cause of this exception.
2016-03-30 14:20:52 -07:00
/// </summary>
/// <param name="message">
2017-01-16 13:31:14 -08:00
/// The message that describes the error.
2016-03-30 14:20:52 -07:00
/// </param>
/// <param name="innerException">
/// The exception that is the cause of the current exception.
/// </param>
public RemoteException ( string message , Exception innerException )
: base ( message , innerException )
{
}
/// <summary>
2017-01-16 13:31:14 -08:00
/// Initializes a new instance of the RemoteException
/// with a specified error message, serialized Exception and
2016-03-30 14:20:52 -07:00
/// serialized InvocationInfo
/// </summary>
2018-12-28 11:05:14 +05:00
/// <param name="message">The message that describes the error.</param>
2016-03-30 14:20:52 -07:00
/// <param name="serializedRemoteException">
/// serialized exception from remote msh
/// </param>
/// <param name="serializedRemoteInvocationInfo">
/// serialized invocation info from remote msh
/// </param>
internal RemoteException
(
string message ,
PSObject serializedRemoteException ,
PSObject serializedRemoteInvocationInfo
)
: base ( message )
{
_serializedRemoteException = serializedRemoteException ;
_serializedRemoteInvocationInfo = serializedRemoteInvocationInfo ;
}
#region ISerializable Members
/// <summary>
/// Initializes a new instance of the <see cref="RemoteException"/>
/// class with serialized data.
/// </summary>
/// <param name="info">
2017-01-16 13:31:14 -08:00
/// The <see cref="SerializationInfo"/> that holds the serialized object
2016-03-30 14:20:52 -07:00
/// data about the exception being thrown.
/// </param>
/// <param name="context">
2017-01-16 13:31:14 -08:00
/// The <see cref="StreamingContext"/> that contains contextual information
2016-03-30 14:20:52 -07:00
/// about the source or destination.
/// </param>
protected RemoteException ( SerializationInfo info , StreamingContext context )
: base ( info , context )
{
}
#endregion
[NonSerialized]
private PSObject _serializedRemoteException ;
[NonSerialized]
private PSObject _serializedRemoteInvocationInfo ;
/// <summary>
/// Original Serialized Exception from remote msh
/// </summary>
/// <remarks>This is the exception which was thrown in remote.
/// </remarks>
public PSObject SerializedRemoteException
{
get
{
return _serializedRemoteException ;
}
}
/// <summary>
/// InvocationInfo, if any, associated with the SerializedRemoteException.
/// </summary>
/// <remarks>
/// This is the serialized InvocationInfo from the remote msh.
/// </remarks>
public PSObject SerializedRemoteInvocationInfo
{
get
{
return _serializedRemoteInvocationInfo ;
}
}
private ErrorRecord _remoteErrorRecord ;
/// <summary>
/// Sets the remote error record associated with this exception
/// </summary>
/// <param name="remoteError"></param>
internal void SetRemoteErrorRecord ( ErrorRecord remoteError )
{
_remoteErrorRecord = remoteError ;
}
/// <summary>
/// ErrorRecord associated with the exception
/// </summary>
public override ErrorRecord ErrorRecord
{
get
{
if ( _remoteErrorRecord != null )
return _remoteErrorRecord ;
return base . ErrorRecord ;
}
}
}
}