using System.Diagnostics; namespace SinMaiLauncher.ChildProcessHolder; internal abstract class ChildProcessStateBag(ChildProcessKind kind, IProcessStartInfo startInfo) { public ChildProcessKind Kind { get; } = kind; public event EventHandler StatusUpdated; private Process? process; public bool IsAlive => process?.HasExited == false; public int? Pid => process?.Id; public ChildProcessStatus Status { get; protected set { field = value; StatusUpdated?.Invoke(this, EventArgs.Empty); } } public async Task StartAsync() { if (process != null) throw new InvalidOperationException("Already started"); Status = ChildProcessStatus.PreLaunching; if (false == Directory.Exists(startInfo.WorkingDir)) { Status = ChildProcessStatus.MissingWorkingDir; return; } if (false == File.Exists(startInfo.Exe)) { Status = ChildProcessStatus.MissingExeFile; return; } Status = ChildProcessStatus.Launching; process = Program.CreateConsoleProcess(startInfo); process.Exited += async delegate { process = null; Status = ChildProcessStatus.Stopped; }; process.Start(); if (IsAlive) { if (await CheckReadyAsync()) { Status = ChildProcessStatus.Ready; } else { await StopAsync(TimeSpan.Zero); } } } protected virtual async Task CheckReadyAsync() => true; public virtual async Task StopAsync(TimeSpan timeout) { if (IsAlive) process!.Kill(); Status = ChildProcessStatus.Stopped; } protected Task WaitForExitAsync() => IsAlive ? process!.WaitForExitAsync() : Task.CompletedTask; protected Task WaitForInputIdleAsync(TimeSpan timeout) => IsAlive ? Task.Run(() => process!.WaitForInputIdle(timeout)) : Task.FromResult(false); protected nint? MainWindowHWnd => IsAlive ? process!.MainWindowHandle : null; protected bool CloseMainForm() => IsAlive && process!.CloseMainWindow(); protected void RefreshProcess() => process?.Refresh(); }