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);
}
}
}
}