using ClashDotNetFramework.Models.Enums; using ClashDotNetFramework.Utils; using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Timers; namespace ClashDotNetFramework.Controllers { public abstract class Guard { /// /// 保存Buffer计时器 /// private static readonly Timer SaveBufferTimer = new Timer(300) { AutoReset = true }; /// /// 日志Buffer /// private readonly StringBuilder _logBuffer = new StringBuilder(); /// /// 日志文件(重定向输出文件) /// private string _logPath; /// /// 成功启动关键词 /// protected virtual IEnumerable StartedKeywords { get; } = null; /// /// 启动失败关键词 /// protected virtual IEnumerable StoppedKeywords { get; } = null; /// /// 主程序名称 /// public virtual string Name { get; } /// /// 主程序文件名 /// public virtual string MainFile { get; protected set; } /// /// 实例状态 /// protected State State { get; set; } = State.Waiting; /// /// 重定向输出 /// protected bool RedirectStd { get; set; } = false; /// /// 程序输出的编码 /// protected Encoding InstanceOutputEncoding { get; set; } = Encoding.GetEncoding("utf-8"); /// /// 进程实例 /// public Process Instance { get; private set; } /// /// 停止进程 /// public abstract void Stop(); /// /// 初始化实例 /// /// 进程参数 protected virtual void InitInstance(string argument) { Instance = new Process { StartInfo = { FileName = Path.GetFullPath($"bin\\{MainFile}"), WorkingDirectory = $"{Global.ClashDotNetFrameworkDir}\\bin", Arguments = argument, CreateNoWindow = true, UseShellExecute = !RedirectStd, RedirectStandardOutput = RedirectStd, StandardOutputEncoding = RedirectStd ? InstanceOutputEncoding : null, RedirectStandardError = RedirectStd, StandardErrorEncoding = RedirectStd ? InstanceOutputEncoding : null, WindowStyle = ProcessWindowStyle.Hidden } }; } /// /// 停止实例 /// protected void StopInstance() { try { if (Instance == null || Instance.HasExited) return; Instance.Kill(); Instance.WaitForExit(); } catch (Win32Exception e) { Logging.Error($"停止 {MainFile} 错误:\n" + e); } catch { } } /// /// 启动实例 /// /// 进程参数 /// 进程优先级 protected void StartInstanceAuto(string argument, ProcessPriorityClass priority = ProcessPriorityClass.Normal) { InitInstance(argument); Instance.EnableRaisingEvents = true; if (RedirectStd) { // 清理日志 _logPath = Path.Combine(Utils.Utils.GetClashLogsDir(), $"{Name}.log"); if (File.Exists(_logPath)) File.Delete(_logPath); Instance.OutputDataReceived += OnOutputDataReceived; Instance.ErrorDataReceived += OnOutputDataReceived; } Instance.Exited += OnExited; Instance.Start(); if (priority != ProcessPriorityClass.Normal) { Instance.PriorityClass = priority; } if (!RedirectStd) return; Instance.BeginOutputReadLine(); Instance.BeginErrorReadLine(); SaveBufferTimer.Elapsed += SaveBufferTimerEvent; SaveBufferTimer.Enabled = true; if (!(StartedKeywords?.Any() ?? false)) { State = State.Started; return; } } /// /// 退出时执行的操作 /// /// /// private void OnExited(object sender, EventArgs e) { if (RedirectStd) SaveBufferTimer.Enabled = false; SaveBufferTimerEvent(null, null); State = State.Stopped; } /// /// 写入日志文件缓冲 /// /// /// 转码后的字符串 private void Write(string info) { _logBuffer.Append(info + Global.EOF); } /// /// 接收输出数据 /// /// 发送者 /// 数据 protected void OnOutputDataReceived(object sender, DataReceivedEventArgs e) { // 程序结束, 接收到 null if (e.Data == null) return; Write(e.Data); // 检查启动 if (State == State.Starting) { if (StartedKeywords.Any(s => e.Data.Contains(s))) State = State.Started; else if (StoppedKeywords.Any(s => e.Data.Contains(s))) State = State.Stopped; } } /// /// 计时器存储日志 /// /// 发送者 /// 数据 private void SaveBufferTimerEvent(object sender, EventArgs e) { try { if (_logPath != null && _logBuffer != null) { File.AppendAllText(_logPath, _logBuffer.ToString()); _logBuffer.Clear(); } } catch (Exception exception) { Logging.Warning($"写入 {Name} 日志错误:\n" + exception.Message); } } } }