Files
NebulaLauncher/Nebula.Launcher/ProcessHelper/ProcessRunHandler.cs

155 lines
3.8 KiB
C#
Raw Normal View History

2025-06-17 21:07:32 +03:00
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Nebula.Shared.Services;
using Nebula.Shared.Services.Logging;
namespace Nebula.Launcher.ProcessHelper;
2025-12-11 21:47:54 +03:00
public class ProcessRunHandler : IDisposable
2025-06-17 21:07:32 +03:00
{
private ProcessStartInfo? _processInfo;
private Task<ProcessStartInfo>? _processInfoTask;
private Process? _process;
2025-12-11 21:47:54 +03:00
private readonly IProcessLogConsumer _logConsumer;
2025-06-17 21:07:32 +03:00
private string _lastError = string.Empty;
2025-12-11 21:47:54 +03:00
private readonly IProcessStartInfoProvider _currentProcessStartInfoProvider;
2025-06-17 21:07:32 +03:00
2025-12-11 21:47:54 +03:00
public IProcessStartInfoProvider GetCurrentProcessStartInfo() => _currentProcessStartInfoProvider;
2025-06-17 21:07:32 +03:00
public bool IsRunning => _processInfo is not null;
2025-12-11 21:47:54 +03:00
public Action<ProcessRunHandler>? OnProcessExited;
2025-06-17 21:07:32 +03:00
2025-12-11 21:47:54 +03:00
public bool Disposed { get; private set; }
public ProcessRunHandler(IProcessStartInfoProvider processStartInfoProvider, IProcessLogConsumer logConsumer)
2025-06-17 21:07:32 +03:00
{
_currentProcessStartInfoProvider = processStartInfoProvider;
2025-12-11 21:47:54 +03:00
_logConsumer = logConsumer;
2025-06-17 21:07:32 +03:00
_processInfoTask = _currentProcessStartInfoProvider.GetProcessStartInfo();
_processInfoTask.GetAwaiter().OnCompleted(OnInfoProvided);
}
private void OnInfoProvided()
{
if (_processInfoTask == null)
return;
_processInfo = _processInfoTask.GetAwaiter().GetResult();
_processInfoTask = null;
}
2025-12-11 21:47:54 +03:00
private void CheckIfDisposed()
{
if (!Disposed) return;
throw new ObjectDisposedException(nameof(ProcessRunHandler));
}
2025-06-17 21:07:32 +03:00
public void Start()
{
2025-12-11 21:47:54 +03:00
CheckIfDisposed();
if(_process is not null)
throw new InvalidOperationException("Already running");
2025-06-17 21:07:32 +03:00
if (_processInfoTask != null)
{
_processInfoTask.Wait();
}
_process = Process.Start(_processInfo!);
if (_process is null) return;
_process.EnableRaisingEvents = true;
_process.BeginOutputReadLine();
_process.BeginErrorReadLine();
_process.OutputDataReceived += OnOutputDataReceived;
_process.ErrorDataReceived += OnErrorDataReceived;
_process.Exited += OnExited;
}
public void Stop()
{
2025-12-11 21:47:54 +03:00
CheckIfDisposed();
Dispose();
2025-06-17 21:07:32 +03:00
}
private void OnExited(object? sender, EventArgs e)
{
if (_process is null) return;
_process.OutputDataReceived -= OnOutputDataReceived;
_process.ErrorDataReceived -= OnErrorDataReceived;
_process.Exited -= OnExited;
if (_process.ExitCode != 0)
2025-12-11 21:47:54 +03:00
_logConsumer.Fatal(_lastError);
2025-06-17 21:07:32 +03:00
_process.Dispose();
_process = null;
OnProcessExited?.Invoke(this);
2025-12-11 21:47:54 +03:00
Dispose();
2025-06-17 21:07:32 +03:00
}
private void OnErrorDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
_lastError = e.Data;
2025-12-11 21:47:54 +03:00
_logConsumer.Error(e.Data);
2025-06-17 21:07:32 +03:00
}
}
private void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
2025-12-11 21:47:54 +03:00
_logConsumer.Out(e.Data);
2025-06-17 21:07:32 +03:00
}
}
public void Dispose()
{
2025-12-11 21:47:54 +03:00
if (_process is not null)
{
_process.CloseMainWindow();
return;
}
CheckIfDisposed();
2025-06-17 21:07:32 +03:00
_processInfoTask?.Dispose();
2025-12-11 21:47:54 +03:00
Disposed = true;
2025-06-17 21:07:32 +03:00
}
}
public sealed class DebugLoggerBridge : IProcessLogConsumer
{
private ILogger _logger;
public DebugLoggerBridge(ILogger logger)
{
_logger = logger;
}
public void Out(string text)
{
_logger.Log(LoggerCategory.Log, text);
}
public void Error(string text)
{
_logger.Log(LoggerCategory.Error, text);
}
public void Fatal(string text)
{
_logger.Log(LoggerCategory.Error, text);
}
}