| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 | 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    {        /// <summary>        /// 保存Buffer计时器        /// </summary>        private static readonly Timer SaveBufferTimer = new Timer(300) { AutoReset = true };        /// <summary>        /// 日志Buffer        /// </summary>        private readonly StringBuilder _logBuffer = new StringBuilder();        /// <summary>        /// 日志文件(重定向输出文件)        /// </summary>        private string _logPath;        /// <summary>        /// 成功启动关键词        /// </summary>        protected virtual IEnumerable<string> StartedKeywords { get; } = null;        /// <summary>        /// 启动失败关键词        /// </summary>        protected virtual IEnumerable<string> StoppedKeywords { get; } = null;        /// <summary>        /// 主程序名称        /// </summary>        public virtual string Name { get; }        /// <summary>        /// 主程序文件名        /// </summary>        public virtual string MainFile { get; protected set; }        /// <summary>        /// 实例状态        /// </summary>        protected State State { get; set; } = State.Waiting;        /// <summary>        /// 重定向输出        /// </summary>        protected bool RedirectStd { get; set; } = false;        /// <summary>        /// 程序输出的编码        /// </summary>        protected Encoding InstanceOutputEncoding { get; set; } = Encoding.GetEncoding("utf-8");        /// <summary>        /// 进程实例        /// </summary>        public Process Instance { get; private set; }        /// <summary>        /// 停止进程        /// </summary>        public abstract void Stop();        /// <summary>        /// 初始化实例        /// </summary>        /// <param name="argument">进程参数</param>        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                }            };        }        /// <summary>        /// 停止实例        /// </summary>        protected void StopInstance()        {            try            {                if (Instance == null || Instance.HasExited)                    return;                Instance.Kill();                Instance.WaitForExit();            }            catch (Win32Exception e)            {                Logging.Error($"停止 {MainFile} 错误:\n" + e);            }            catch            {            }        }        /// <summary>        /// 启动实例        /// </summary>        /// <param name="argument">进程参数</param>        /// <param name="priority">进程优先级</param>        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;            }        }        /// <summary>        /// 退出时执行的操作        /// </summary>        /// <param name="sender"></param>        /// <param name="e"></param>        private void OnExited(object sender, EventArgs e)        {            if (RedirectStd)                SaveBufferTimer.Enabled = false;            SaveBufferTimerEvent(null, null);            State = State.Stopped;        }        /// <summary>        /// 写入日志文件缓冲        /// </summary>        /// <param name="info"></param>        /// <returns>转码后的字符串</returns>        private void Write(string info)        {            _logBuffer.Append(info + Global.EOF);        }        /// <summary>        /// 接收输出数据        /// </summary>        /// <param name="sender">发送者</param>        /// <param name="e">数据</param>        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;            }        }        /// <summary>        /// 计时器存储日志        /// </summary>        /// <param name="sender">发送者</param>        /// <param name="e">数据</param>        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);            }        }    }}
 |