internal ProcessCommand(
            ProcessStartInfo startInfo,
            bool throwOnError,
            bool disposeOnExit,
            TimeSpan timeout,
            CancellationToken cancellationToken,
            Encoding standardInputEncoding)
        {
            this.disposeOnExit = disposeOnExit;
            this.fileName      = startInfo.FileName;
            this.arguments     = startInfo.Arguments;
            this.process       = new Process {
                StartInfo = startInfo, EnableRaisingEvents = true
            };

            var processMonitoringTask = CreateProcessMonitoringTask(this.process);

            this.process.SafeStart(out var processStandardInput, out var processStandardOutput, out var processStandardError);

            var ioTasks = new List <Task>(capacity: 2);

            if (startInfo.RedirectStandardOutput)
            {
                this.standardOutputReader = new InternalProcessStreamReader(processStandardOutput);
                ioTasks.Add(this.standardOutputReader.Task);
            }
            if (startInfo.RedirectStandardError)
            {
                this.standardErrorReader = new InternalProcessStreamReader(processStandardError);
                ioTasks.Add(this.standardErrorReader.Task);
            }
            if (startInfo.RedirectStandardInput)
            {
                // unfortunately, changing the encoding can't be done via ProcessStartInfo so we have to do it manually here.
                // See https://github.com/dotnet/corefx/issues/20497

                var wrappedStream = PlatformCompatibilityHelper.WrapStandardInputStreamIfNeeded(processStandardInput.BaseStream);
                var standardInputEncodingToUse = standardInputEncoding ?? processStandardInput.Encoding;
                var streamWriter = wrappedStream == processStandardInput.BaseStream && Equals(standardInputEncodingToUse, processStandardInput.Encoding)
                    ? processStandardInput
                    : new StreamWriter(wrappedStream, standardInputEncodingToUse);
                this.standardInput = new ProcessStreamWriter(streamWriter);
            }

            // according to https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.process.id?view=netcore-1.1#System_Diagnostics_Process_Id,
            // this can throw PlatformNotSupportedException on some older windows systems in some StartInfo configurations. To be as
            // robust as possible, we thus make this a best-effort attempt
            try { this.processIdOrExceptionDispatchInfo = this.process.Id; }
            catch (PlatformNotSupportedException processIdException)
            {
                this.processIdOrExceptionDispatchInfo = ExceptionDispatchInfo.Capture(processIdException);
            }

            // we only set up timeout and cancellation AFTER starting the process. This prevents a race
            // condition where we immediately try to kill the process before having started it and then proceed to start it.
            // While we could avoid starting at all in such cases, that would leave the command in a weird state (no PID, no streams, etc)
            var processTask = ProcessHelper.CreateProcessTask(this.process, processMonitoringTask, throwOnError, timeout, cancellationToken);

            this.task = this.CreateCombinedTask(processTask, ioTasks);
        }
Example #2
0
 /// <summary>
 /// Restores all settings to the default value
 /// </summary>
 public Options RestoreDefaults()
 {
     this.StartInfoInitializers    = new List <Action <ProcessStartInfo> >();
     this.CommandInitializers      = new List <Func <Command, Command> >();
     this.CommandLineSyntax        = PlatformCompatibilityHelper.GetDefaultCommandLineSyntax();
     this.ThrowExceptionOnError    = false;
     this.DisposeProcessOnExit     = true;
     this.ProcessTimeout           = System.Threading.Timeout.InfiniteTimeSpan;
     this.ProcessStreamEncoding    = null;
     this.ProcessCancellationToken = System.Threading.CancellationToken.None;
     return(this);
 }