child/ControlProcess.cs

#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace DeskPilot.Child
{
    internal sealed class ControlProcess : IDisposable
    {
        private readonly Process? _process;
        private readonly OwnedProcess? _owned;

        internal ControlProcess(ProcessStartInfo start, OwnedProcess? owner)
        {
            if (owner == null)
            {
                _process = new Process { StartInfo = start };
                _process.Start();
                Input = _process.StandardInput.BaseStream;
                Output = _process.StandardOutput.BaseStream;
                Error = _process.StandardError.BaseStream;
            }
            else
            {
                var environment = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
                foreach (KeyValuePair<string, string?> entry in start.Environment)
                {
                    if (entry.Value != null) { environment.Add(entry.Key, entry.Value); }
                }
                _owned = owner.CreateSibling(start.FileName, new List<string>(start.ArgumentList).ToArray(),
                    start.WorkingDirectory, environment);
                try { _owned.Resume(); }
                catch { _owned.Dispose(); throw; }
                Input = _owned.Input;
                Output = _owned.Output;
                Error = _owned.Error;
            }
        }

        internal Stream Input { get; }
        internal Stream Output { get; }
        internal Stream Error { get; }
        internal bool HasExited => _owned?.HasExited ?? _process!.HasExited;
        internal int ExitCode => _owned?.ExitCode ?? _process!.ExitCode;
        internal Task WaitForExitAsync(CancellationToken cancellationToken) =>
            _owned?.WaitForExitAsync(cancellationToken) ?? _process!.WaitForExitAsync(cancellationToken);

        internal void Kill(bool descendants)
        {
            if (_owned != null) { _owned.Stop(); }
            else if (!_process!.HasExited) { _process.Kill(descendants); }
        }

        public void Dispose()
        {
            if (_owned != null) { _owned.Dispose(); }
            else
            {
                try { if (!_process!.HasExited) { _process.Kill(true); } }
                finally { _process!.Dispose(); }
            }
        }
    }
}