using System.Diagnostics; namespace Neta.Tray; public sealed class BackendProcessManager { public static ProcessStartInfo BuildBackendStartInfo(string backendExePath, string traySecret) { var info = new ProcessStartInfo(backendExePath) { UseShellExecute = false, CreateNoWindow = true, WorkingDirectory = Path.GetDirectoryName(backendExePath)! }; info.ArgumentList.Add("--tray-secret"); info.ArgumentList.Add(traySecret); info.ArgumentList.Add("--no-browser"); return info; } public Process Start(string backendExePath, string traySecret) { return Process.Start(BuildBackendStartInfo(backendExePath, traySecret)) ?? throw new InvalidOperationException("backend.exe 启动失败"); } public bool IsBackendProcessAlive(int pid) { try { var process = Process.GetProcessById(pid); return !process.HasExited; } catch { return false; } } public void WaitForExit(int pid, TimeSpan timeout) { var deadline = DateTime.UtcNow + timeout; while (DateTime.UtcNow < deadline) { if (!IsBackendProcessAlive(pid)) return; Thread.Sleep(200); } } public void KillProcess(int pid) { try { var process = Process.GetProcessById(pid); if (!process.HasExited) { process.Kill(true); process.WaitForExit(5000); } } catch { } } public void KillInstalledBackendProcesses(string backendExePath) { var normalizedPath = Path.GetFullPath(backendExePath); foreach (var process in Process.GetProcessesByName("backend")) { try { var fileName = process.MainModule?.FileName; if (string.IsNullOrWhiteSpace(fileName)) continue; if (!string.Equals(Path.GetFullPath(fileName), normalizedPath, StringComparison.OrdinalIgnoreCase)) continue; if (!process.HasExited) { process.Kill(true); process.WaitForExit(5000); } } catch { } } } }