internal AttachedCommand( Process process, bool throwOnError, TimeSpan timeout, CancellationToken cancellationToken, bool disposeOnExit) { this.process = process; this.disposeOnExit = disposeOnExit; var processMonitoringTask = CreateProcessMonitoringTask(process); var processTask = ProcessHelper.CreateProcessTask(this.process, processMonitoringTask, throwOnError, timeout, cancellationToken); this.commandResultTask = processTask.ContinueWith( continuedTask => { if (disposeOnExit) { this.process.Dispose(); } return(new CommandResult(continuedTask.Result, this)); }, TaskContinuationOptions.ExecuteSynchronously ); this.processes = new Lazy <ReadOnlyCollection <Process> >(() => new ReadOnlyCollection <Process>(new[] { this.process })); }
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); }