ChildProcessStateBag.cs 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. using System.Diagnostics;
  2. namespace SinMaiLauncher.ChildProcessHolder;
  3. internal abstract class ChildProcessStateBag(ChildProcessKind kind, IProcessStartInfo startInfo)
  4. {
  5. public ChildProcessKind Kind { get; } = kind;
  6. public event EventHandler<EventArgs> StatusUpdated;
  7. private Process? process;
  8. public bool IsAlive => process?.HasExited == false;
  9. public int? Pid => process?.Id;
  10. public ChildProcessStatus Status
  11. {
  12. get;
  13. protected set
  14. {
  15. field = value;
  16. StatusUpdated?.Invoke(this, EventArgs.Empty);
  17. }
  18. }
  19. public async Task StartAsync()
  20. {
  21. if (process != null) throw new InvalidOperationException("Already started");
  22. Status = ChildProcessStatus.PreLaunching;
  23. if (false == Directory.Exists(startInfo.WorkingDir))
  24. {
  25. Status = ChildProcessStatus.MissingWorkingDir;
  26. return;
  27. }
  28. if (false == File.Exists(startInfo.Exe))
  29. {
  30. Status = ChildProcessStatus.MissingExeFile;
  31. return;
  32. }
  33. Status = ChildProcessStatus.Launching;
  34. process = Program.CreateConsoleProcess(startInfo);
  35. process.Exited += async delegate
  36. {
  37. process = null;
  38. Status = ChildProcessStatus.Stopped;
  39. };
  40. process.Start();
  41. if (IsAlive)
  42. {
  43. if (await CheckReadyAsync())
  44. {
  45. Status = ChildProcessStatus.Ready;
  46. }
  47. else
  48. {
  49. await StopAsync(TimeSpan.Zero);
  50. }
  51. }
  52. }
  53. protected virtual async Task<bool> CheckReadyAsync() => true;
  54. public virtual async Task StopAsync(TimeSpan timeout)
  55. {
  56. if (IsAlive) process!.Kill();
  57. Status = ChildProcessStatus.Stopped;
  58. }
  59. protected Task WaitForExitAsync() => IsAlive ? process!.WaitForExitAsync() : Task.CompletedTask;
  60. protected Task<bool> WaitForInputIdleAsync(TimeSpan timeout) => IsAlive ? Task.Run(() => process!.WaitForInputIdle(timeout)) : Task.FromResult(false);
  61. protected nint? MainWindowHWnd => IsAlive ? process!.MainWindowHandle : null;
  62. protected bool CloseMainForm() => IsAlive && process!.CloseMainWindow();
  63. protected void RefreshProcess() => process?.Refresh();
  64. }