using System; using System.Diagnostics; using System.IO; using System.Threading; using XP.Common.Logging.Interfaces; using XP.Hardware.RaySource.Config; namespace XP.Hardware.RaySource.Implementations { /// /// Host 进程生命周期管理器 /// 负责启动、监控和关闭 .NET Framework 4.8 Host 子进程 /// public class CometHostManager : IDisposable { private readonly ILoggerService _logger; private readonly RaySourceConfig _config; private Process _hostProcess; private bool _isDisposed; /// /// Host 可执行文件名 /// private const string HostExeName = "XP.Hardware.RaySource.Comet.Host.exe"; /// /// 命令管道名称(客户端写 → Host 读) /// private const string CmdPipeName = "XP.Hardware.RaySource.Comet.Cmd"; /// /// 响应管道名称(Host 写 → 客户端读) /// private const string RspPipeName = "XP.Hardware.RaySource.Comet.Rsp"; /// /// 等待管道就绪的超时时间(毫秒) /// private const int PipeReadyTimeoutMs = 10000; /// /// 等待 Host 进程退出的超时时间(毫秒) /// private const int ShutdownTimeoutMs = 5000; /// /// 构造函数 /// /// 日志服务 /// 射线源配置 public CometHostManager(ILoggerService loggerService, RaySourceConfig config) { _logger = loggerService?.ForModule() ?? throw new ArgumentNullException(nameof(loggerService)); _config = config ?? throw new ArgumentNullException(nameof(config)); } /// /// Host 进程是否正在运行 /// public bool IsRunning => _hostProcess != null && !_hostProcess.HasExited; /// /// 确保 Host 进程正在运行 /// 优先检查是否已有同路径的 Host 进程在运行,有则直接关联;否则启动新进程 /// public void EnsureRunning() { if (IsRunning) { _logger.Debug("Host 进程已在运行,PID={Pid}", _hostProcess.Id); return; } var hostExePath = GetHostExePath(); // 检查是否已有 Host 进程在运行(例如上次异常退出后残留的进程),杀掉后重新启动 var hostProcessName = Path.GetFileNameWithoutExtension(HostExeName); var existingProcesses = Process.GetProcessesByName(hostProcessName); foreach (var proc in existingProcesses) { try { _logger.Info("发现残留的 Host 进程,正在终止,PID={Pid}", proc.Id); proc.Kill(); proc.WaitForExit(1500); } catch (Exception ex) { _logger.Warn("终止残留 Host 进程失败,PID={Pid},{Message}", proc.Id, ex.Message); } } _logger.Info("启动 Host 进程:{Path}", hostExePath); if (!File.Exists(hostExePath)) { throw new FileNotFoundException($"Host 可执行文件未找到:{hostExePath}", hostExePath); } var startInfo = new ProcessStartInfo { FileName = hostExePath, UseShellExecute = false, CreateNoWindow = true, RedirectStandardError = true }; _hostProcess = new Process { StartInfo = startInfo, EnableRaisingEvents = true }; _hostProcess.Exited += OnHostProcessExited; _hostProcess.ErrorDataReceived += OnHostErrorDataReceived; _hostProcess.Start(); _hostProcess.BeginErrorReadLine(); _logger.Info("Host 进程已启动,PID={Pid}", _hostProcess.Id); // 等待 NamedPipe 就绪 WaitForPipeReady(); } /// /// 安全关闭 Host 进程 /// 等待进程退出,超时则强制终止(DisconnectCommand 已由 CometIpcClient 发送) /// public void Shutdown() { if (!IsRunning) { _logger.Debug("Host 进程未运行,无需关闭"); return; } _logger.Info("正在关闭 Host 进程,PID={Pid}", _hostProcess.Id); try { // 等待进程退出(DisconnectCommand 已由 CometIpcClient 发送) if (!_hostProcess.WaitForExit(ShutdownTimeoutMs)) { _logger.Warn("Host 进程未在超时时间内退出,强制终止"); _hostProcess.Kill(); _hostProcess.WaitForExit(1000); } _logger.Info("Host 进程已关闭"); } catch (Exception ex) { _logger.Warn("关闭 Host 进程时发生异常:{Message}", ex.Message); } finally { CleanupProcess(); } } /// /// 获取 Host 可执行文件路径 /// 优先使用配置的路径,为空时默认在主程序输出目录下查找 /// private string GetHostExePath() { // 优先使用配置的路径 if (!string.IsNullOrWhiteSpace(_config.HostExePath)) { return _config.HostExePath; } // 默认在主程序输出目录的 Host 子目录下查找 var baseDir = AppDomain.CurrentDomain.BaseDirectory; return Path.Combine(baseDir, "Host", HostExeName); } /// /// 等待 NamedPipe 就绪 /// 通过短暂延迟和进程存活检查确认 Host 进程已启动并创建了管道 /// 使用临时 NamedPipeClientStream 尝试连接来检测管道是否就绪 /// private void WaitForPipeReady() { var startTime = DateTime.UtcNow; while ((DateTime.UtcNow - startTime).TotalMilliseconds < PipeReadyTimeoutMs) { if (_hostProcess.HasExited) { throw new InvalidOperationException($"Host 进程启动后立即退出,退出码={_hostProcess.ExitCode}"); } // 短暂等待,让 Host 进程有时间创建管道 Thread.Sleep(200); // 进程仍在运行,认为管道即将就绪或已就绪 if (!_hostProcess.HasExited) { _logger.Info("Host 进程正在运行,管道应已就绪"); return; } } // 超时但进程仍在运行,假设管道已就绪 if (!_hostProcess.HasExited) { _logger.Warn("等待管道就绪超时,但 Host 进程仍在运行,继续"); return; } throw new TimeoutException($"等待 Host 进程 NamedPipe 就绪超时({PipeReadyTimeoutMs}ms)"); } /// /// Host 进程退出事件处理 /// private void OnHostProcessExited(object sender, EventArgs e) { if (_hostProcess != null) { _logger.Warn("Host 进程已退出,退出码={ExitCode}", _hostProcess.ExitCode); } } /// /// Host 进程 stderr 输出事件处理,转发到主进程日志 /// private void OnHostErrorDataReceived(object sender, DataReceivedEventArgs e) { if (!string.IsNullOrEmpty(e.Data)) { _logger.Debug("[Host stderr] {Message}", e.Data); } } /// /// 清理进程资源 /// private void CleanupProcess() { if (_hostProcess != null) { _hostProcess.Exited -= OnHostProcessExited; _hostProcess.ErrorDataReceived -= OnHostErrorDataReceived; _hostProcess.Dispose(); _hostProcess = null; } } /// /// 释放资源 /// public void Dispose() { if (!_isDisposed) { Shutdown(); _isDisposed = true; } } } }