Browse Source

Add files via upload

Machtergreifung 4 years ago
parent
commit
733bd498a2

+ 43 - 0
ClashDotNetFramework/Controllers/ClashController.cs

@@ -0,0 +1,43 @@
+using ClashDotNetFramework.Utils;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ClashDotNetFramework.Controllers
+{
+    public class ClashController : Guard
+    { 
+        public ClashController()
+        {
+            RedirectStd = true;
+        }
+
+        public override string MainFile { get; protected set; } = "Clash.exe";
+
+        public override string Name { get; } = "Clash";
+
+        public void Start()
+        {
+            int port = PortHelper.GetAvailablePort(PortType.TCP);
+            Global.ClashControllerPort = port;
+            string arguments = $"-ext-ctl 127.0.0.1:{port}";
+            StartInstanceAuto(arguments);
+        }
+
+        public void Start(int port)
+        {
+            PortHelper.CheckPort(Convert.ToUInt16(port), PortType.TCP);
+            Global.ClashControllerPort = port;
+            string arguments = $"-ext-ctl 127.0.0.1:{port}";
+            StartInstanceAuto(arguments);
+        }
+
+        public override void Stop()
+        {
+            StopInstance();
+        }
+    }
+}

+ 51 - 0
ClashDotNetFramework/Controllers/FireWallController.cs

@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using WindowsFirewallHelper;
+
+namespace ClashDotNetFramework.Controllers
+{
+    public static class FireWallController
+    {
+        public static string ClashDotNetFramework = "ClashDotNetFramework";
+
+        public static void AddFireWallRules()
+        {
+            if (!FirewallWAS.IsSupported)
+            {
+                return;
+            }
+
+            var rule = FirewallManager.Instance.Rules.FirstOrDefault(r => r.Name == ClashDotNetFramework);
+            if (rule != null)
+            {
+                if (rule.ApplicationName.StartsWith(Global.ClashDotNetFrameworkDir))
+                    return;
+            }
+
+            try
+            {
+                Process proc = new Process
+                {
+                    StartInfo =
+                    {
+                        FileName = Path.GetFullPath($"bin\\FireWallHelper.exe"),
+                        WorkingDirectory = $"{Global.ClashDotNetFrameworkDir}\\bin",
+                        CreateNoWindow = true,
+                        WindowStyle = ProcessWindowStyle.Hidden,
+                        UseShellExecute = true,
+                        Verb = "runas"
+                    }
+                };
+                proc.Start();
+            }
+            catch
+            {
+            }
+        }
+    }
+}

+ 233 - 0
ClashDotNetFramework/Controllers/Guard.cs

@@ -0,0 +1,233 @@
+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);
+            }
+        }
+    }
+}

+ 209 - 0
ClashDotNetFramework/Controllers/NFController.cs

@@ -0,0 +1,209 @@
+using ClashDotNetFramework.Utils;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.ServiceProcess;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ClashDotNetFramework.Controllers
+{
+    public class NFController : Guard
+    {
+        private readonly string BinDriver = string.Empty;
+        private readonly string SystemDriver = $"{Environment.SystemDirectory}\\drivers\\netfilter2.sys";
+
+        public NFController()
+        {
+            string fileName;
+            switch ($"{Environment.OSVersion.Version.Major}.{Environment.OSVersion.Version.Minor}")
+            {
+                case "10.0": // Win 10
+                case "6.3":  // Win 8
+                case "6.2":  // Win 8
+                case "6.1":  // Win 7
+                case "6.0":  // Win 7
+                    if (SystemHelper.Is64BitOperatingSystem())
+                        fileName = "netfilter2-amd64.sys";
+                    else
+                        fileName = "netfilter2-i386.sys";
+                    break;
+                default:
+                    Logging.Error($"不支持的系统版本:{Environment.OSVersion.Version}");
+                    return;
+            }
+
+            BinDriver = Path.Combine(Global.ClashDotNetFrameworkDir, $"bin\\{fileName}");
+            RedirectStd = true;
+        }
+
+        public override string MainFile { get; protected set; } = "Redirector.exe";
+
+        public override string Name { get; } = "Redirector";
+
+        #region NetFilter Controller
+
+        public void Start()
+        {
+            if (!CheckDriver())
+                return;
+            try
+            {
+                _ = Task.Run(FireWallController.AddFireWallRules);
+                string arguments = $"-m {string.Join(",", Global.Settings.RedirectTraffic)} -l {Global.Settings.ProcessMode} -b {string.Join(",", Global.Settings.BypassType)} -p {string.Join(",", Global.Settings.Processes)} -r 127.0.0.1:{Global.ClashMixedPort}";
+                StartInstanceAuto(arguments);
+            }
+            catch
+            {
+            }
+        }
+
+        public override void Stop()
+        {
+            StopInstance();
+        }
+
+        #endregion
+
+        #region NetFilter Driver
+
+        /// <summary>
+        /// 检查 NF 驱动
+        /// </summary>
+        private bool CheckDriver()
+        {
+            var binFileVersion = Utils.Utils.GetFileVersion(BinDriver);
+            var systemFileVersion = Utils.Utils.GetFileVersion(SystemDriver);
+
+            Logging.Info("内置驱动版本: " + binFileVersion);
+            Logging.Info("系统驱动版本: " + systemFileVersion);
+
+            if (!File.Exists(BinDriver))
+            {
+                Logging.Warning("内置驱动不存在");
+                if (File.Exists(SystemDriver))
+                {
+                    Logging.Warning("使用系统驱动");
+                    return true;
+                }
+
+                Logging.Error("未安装驱动");
+                return false;
+            }
+
+            if (!File.Exists(SystemDriver))
+            {
+                return InstallDriver();
+            }
+
+            var reinstallFlag = false;
+            if (Version.TryParse(binFileVersion, out var binResult) && Version.TryParse(systemFileVersion, out var systemResult))
+            {
+                if (binResult.CompareTo(systemResult) > 0)
+                    // Bin greater than Installed
+                    reinstallFlag = true;
+                else if (systemResult.Major != binResult.Major)
+                    // Installed greater than Bin but Major Version Difference (has breaking changes), do downgrade
+                    reinstallFlag = true;
+            }
+            else
+            {
+                if (!systemFileVersion.Equals(binFileVersion))
+                    reinstallFlag = true;
+            }
+
+            if (!reinstallFlag)
+                return true;
+
+            return ReinstallDriver();
+        }
+
+        /// <summary>
+        /// 安装 NF 驱动
+        /// </summary>
+        /// <returns>驱动是否安装成功</returns>
+        public bool InstallDriver()
+        {
+            Logging.Info("安装 NF 驱动");
+
+            if (!File.Exists(BinDriver))
+                throw new Exception("builtin driver files missing, can't install NF driver");
+
+            Process proc = new Process
+            {
+                StartInfo =
+                {
+                    FileName = Path.GetFullPath($"bin\\NetFilterHelper.exe"),
+                    WorkingDirectory = $"{Global.ClashDotNetFrameworkDir}\\bin",
+                    Arguments = "install",
+                    CreateNoWindow = true,
+                    WindowStyle = ProcessWindowStyle.Hidden,
+                    UseShellExecute = true,
+                    Verb = "runas"
+                }
+            };
+            proc.Start();
+            proc.WaitForExit();
+
+            return proc.ExitCode == 0;
+        }
+
+        // <summary>
+        /// 卸载 NF 驱动
+        /// </summary>
+        /// <returns>是否成功卸载</returns>
+        public bool UninstallDriver()
+        {
+            Logging.Info("卸载 NF 驱动");
+
+            Process proc = new Process
+            {
+                StartInfo =
+                {
+                    FileName = Path.GetFullPath($"bin\\NetFilterHelper.exe"),
+                    WorkingDirectory = $"{Global.ClashDotNetFrameworkDir}\\bin",
+                    Arguments = "uninstall",
+                    CreateNoWindow = true,
+                    WindowStyle = ProcessWindowStyle.Hidden,
+                    UseShellExecute = true,
+                    Verb = "runas"
+                }
+            };
+            proc.Start();
+            proc.WaitForExit();
+
+            return proc.ExitCode == 0;
+        }
+
+        /// <summary>
+        /// 更新 NF 驱动
+        /// </summary>
+        /// <returns>是否更新成功</returns>
+        public bool ReinstallDriver()
+        {
+            Logging.Info("更新 NF 驱动");
+
+            Process proc = new Process
+            {
+                StartInfo =
+                {
+                    FileName = Path.GetFullPath($"bin\\NetFilterHelper.exe"),
+                    WorkingDirectory = $"{Global.ClashDotNetFrameworkDir}\\bin",
+                    Arguments = "reinstall",
+                    CreateNoWindow = true,
+                    WindowStyle = ProcessWindowStyle.Hidden,
+                    UseShellExecute = true,
+                    Verb = "runas"
+                }
+            };
+            proc.Start();
+            proc.WaitForExit();
+
+            return proc.ExitCode == 0;
+        }
+
+        #endregion
+    }
+}

+ 393 - 0
ClashDotNetFramework/Controllers/NotifyIconController.cs

@@ -0,0 +1,393 @@
+using Clash.SDK.Models.Enums;
+using ClashDotNetFramework.Properties;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Forms;
+using WindowsProxy;
+
+using Application = System.Windows.Application;
+using Clipboard = System.Windows.Clipboard;
+
+namespace ClashDotNetFramework.Controllers
+{
+    public class NotifyIconController
+    {
+        public NotifyIcon Icon;
+        public ContextMenu Strip;
+        public MenuItem proxyModeMenu;
+
+        #region Public Control Function
+
+        public void Start()
+        {
+            // 初始化菜单
+            Strip = new ContextMenu();
+            Strip.MenuItems.Add("Dashboard");                                                   // 0
+            Strip.MenuItems.Add("-");                                                           // 1
+            proxyModeMenu = new MenuItem("Proxy Mode");
+            proxyModeMenu.MenuItems.Add("Direct");
+            proxyModeMenu.MenuItems.Add("Rule");
+            proxyModeMenu.MenuItems.Add("Global");
+            Strip.MenuItems.Add(proxyModeMenu);                                                 // 2
+            Strip.MenuItems.Add("System Proxy");                                                // 3
+            Strip.MenuItems.Add("Process Proxy");                                               // 4
+            Strip.MenuItems.Add("-");                                                           // 5
+            MenuItem openTerminalMenu = new MenuItem("Open Terminal With Proxy Set Up");
+            openTerminalMenu.MenuItems.Add("Cmd");
+            openTerminalMenu.MenuItems.Add("PowerShell");
+            openTerminalMenu.MenuItems.Add("Windows Terminal");
+            openTerminalMenu.MenuItems.Add("Fluent Terminal");
+            Strip.MenuItems.Add(openTerminalMenu);                                              // 6
+            MenuItem copyCommandMenu = new MenuItem("Copy Proxy Setting Commands");
+            copyCommandMenu.MenuItems.Add("Cmd");
+            copyCommandMenu.MenuItems.Add("PowerShell");
+            Strip.MenuItems.Add(copyCommandMenu);                                               // 7
+            MenuItem setTelegramProxyMenu = new MenuItem("Set Telegram Proxy");
+            setTelegramProxyMenu.MenuItems.Add("Set Socks5 Proxy To Telegram");
+            Strip.MenuItems.Add(setTelegramProxyMenu);                                          // 8
+            Strip.MenuItems.Add("-");                                                           // 9
+            MenuItem netFilterMenu = new MenuItem("NetFilter Driver");
+            netFilterMenu.MenuItems.Add("Install NetFilter Driver");
+            netFilterMenu.MenuItems.Add("Uninstall NetFilter Driver");
+            Strip.MenuItems.Add(netFilterMenu);                                                 // 10
+            Strip.MenuItems.Add("Enable UWP Loopback");                                         // 11
+            Strip.MenuItems.Add("-");                                                           // 12
+            Strip.MenuItems.Add("Quit");                                                        // 13
+
+            // 设置属性并分配事件
+            Strip.MenuItems[0].Click += OnDashboard_Click;
+
+            proxyModeMenu.MenuItems[0].RadioCheck = true;
+            proxyModeMenu.MenuItems[1].RadioCheck = true;
+            proxyModeMenu.MenuItems[2].RadioCheck = true;
+
+            proxyModeMenu.MenuItems[0].Click += OnProxyMode_Click;
+            proxyModeMenu.MenuItems[1].Click += OnProxyMode_Click;
+            proxyModeMenu.MenuItems[2].Click += OnProxyMode_Click;
+
+            Strip.MenuItems[3].Click += OnSystemProxy_Click;
+
+            Strip.MenuItems[4].Click += OnProcessProxy_Click;
+
+            openTerminalMenu.MenuItems[0].Click += OpenCmdWithProxySetUp_Click;
+            openTerminalMenu.MenuItems[1].Click += OpenPowerShellWithProxySetUp_Click;
+            openTerminalMenu.MenuItems[2].Click += OpenWindowsTerminalWithProxySetUp_Click;
+            openTerminalMenu.MenuItems[3].Click += OpenFluentTerminalWithProxySetUp_Click;
+
+            copyCommandMenu.MenuItems[0].Click += CopyCmdProxySettingCommands_Click;
+            copyCommandMenu.MenuItems[1].Click += CopyPowerShellProxySettingCommands_Click;
+
+            setTelegramProxyMenu.MenuItems[0].Click += SetSocks5ProxyToTelegram_Click;
+
+            netFilterMenu.MenuItems[0].Click += InstallNetFilterDriver_Click;
+            netFilterMenu.MenuItems[1].Click += UninstallNetFilterDriver_Click;
+
+            Strip.MenuItems[11].Click += EnableUWPLoopback_Click;
+
+            Strip.MenuItems[13].Click += QuitItem_Click;
+
+            // 初始化图标
+            Icon = new NotifyIcon
+            {
+                Icon = Resources.Normal,
+                Visible = false,
+                Text = "Clash .NET Framework",
+                ContextMenu = Strip
+            };
+
+            Icon.MouseDown += Icon_MouseDown;
+            Icon.Visible = true;
+        }
+
+        public void Stop()
+        {
+            Icon.MouseDown -= Icon_MouseDown;
+            Icon.Visible = false;
+        }
+
+        public void SetIcon()
+        {
+            if (Global.Settings.ProcessProxy || Global.Settings.SystemProxy)
+            {
+                Icon.Icon = Resources.Proxy;
+            }
+            else
+            {
+                Icon.Icon = Resources.Normal;
+            }
+        }
+
+        #endregion
+
+        #region TrayIcon Event
+
+        private async void Icon_MouseDown(object sender, MouseEventArgs e)
+        {
+            if (e.Button == MouseButtons.Left)
+            {
+                var window = Application.Current.MainWindow;
+                window.Show();
+            }
+            else if (e.Button == MouseButtons.Right)
+            {
+                try
+                {
+                    await InitStatus();
+                }
+                catch
+                {
+                }
+            }
+        }
+
+        #endregion
+
+        #region Internal Function
+
+        private async Task InitStatus()
+        {
+            var config = await Global.clashClient.GetClashConfigs();
+            switch (config.Mode)
+            {
+                case ModeType.Direct:
+                    proxyModeMenu.MenuItems[0].Checked = true;
+                    proxyModeMenu.MenuItems[1].Checked = false;
+                    proxyModeMenu.MenuItems[2].Checked = false;
+                    break;
+                case ModeType.Rule:
+                    proxyModeMenu.MenuItems[0].Checked = false;
+                    proxyModeMenu.MenuItems[1].Checked = true;
+                    proxyModeMenu.MenuItems[2].Checked = false;
+                    break;
+                case ModeType.Global:
+                    proxyModeMenu.MenuItems[0].Checked = false;
+                    proxyModeMenu.MenuItems[1].Checked = false;
+                    proxyModeMenu.MenuItems[2].Checked =true;
+                    break;
+                default:
+                    break;
+            }
+            Strip.MenuItems[3].Checked = Global.Settings.SystemProxy;
+            Strip.MenuItems[4].Checked = Global.Settings.ProcessProxy;
+        }
+
+        #endregion
+
+        #region MenuItem Click
+
+        private void OnDashboard_Click(object sender, EventArgs e)
+        {
+            var window = Application.Current.MainWindow;
+            window.Show();
+        }
+
+        private async void OnProxyMode_Click(object sender, EventArgs e)
+        {
+            Dictionary<string, dynamic> dict = new Dictionary<string, dynamic>();
+            if (sender == proxyModeMenu.MenuItems[0] && !proxyModeMenu.MenuItems[0].Checked)
+            {
+                dict.Add("mode", "Direct");
+                await Global.clashClient.ChangeClashConfigs(dict);
+                proxyModeMenu.MenuItems[0].Checked = true;
+                proxyModeMenu.MenuItems[1].Checked = false;
+                proxyModeMenu.MenuItems[2].Checked = false;
+            }
+            else if (sender == proxyModeMenu.MenuItems[1] && !proxyModeMenu.MenuItems[1].Checked)
+            {
+                dict.Add("mode", "Rule");
+                await Global.clashClient.ChangeClashConfigs(dict);
+                proxyModeMenu.MenuItems[0].Checked = false;
+                proxyModeMenu.MenuItems[1].Checked = true;
+                proxyModeMenu.MenuItems[2].Checked = false;
+            }
+            else if (sender == proxyModeMenu.MenuItems[2] && !proxyModeMenu.MenuItems[2].Checked)
+            {
+                dict.Add("mode", "Global");
+                await Global.clashClient.ChangeClashConfigs(dict);
+                proxyModeMenu.MenuItems[0].Checked = false;
+                proxyModeMenu.MenuItems[1].Checked = false;
+                proxyModeMenu.MenuItems[2].Checked = true;
+            }
+            else
+            {
+                // 啥也别干
+            }
+            Global.Refresh = true;
+        }
+
+        private void OnSystemProxy_Click(object sender, EventArgs e)
+        {
+            if (Strip.MenuItems[3].Checked)
+            {
+                using var service = new ProxyService();
+                service.Direct();
+                Global.Settings.SystemProxy = false;
+            }
+            else
+            {
+                using var service = new ProxyService
+                {
+                    Server = $"127.0.0.1:{Global.ClashMixedPort}",
+                    Bypass = string.Join(@";", ProxyService.LanIp)
+                };
+                service.Global();
+                Global.Settings.SystemProxy = true;
+            }
+            Strip.MenuItems[3].Checked = !Strip.MenuItems[3].Checked;
+            SetIcon();
+        }
+
+        private void OnProcessProxy_Click(object sender, EventArgs e)
+        {
+            if (Strip.MenuItems[4].Checked)
+            {
+                Global.nfController.Stop();
+                Global.Settings.ProcessProxy = false;
+            }
+            else
+            {
+                Global.nfController.Start();
+                Global.Settings.ProcessProxy = true;
+            }
+            Strip.MenuItems[4].Checked = !Strip.MenuItems[4].Checked;
+            SetIcon();
+        }
+
+        #endregion
+
+        #region Open Terminal With Proxy Set Up
+
+        private void OpenProcessWithProxySetUp(string name)
+        {
+            try
+            {
+                var startInfo = new ProcessStartInfo();
+
+                // 设置环境变量
+                startInfo.EnvironmentVariables["http_proxy"] = $"http://127.0.0.1:{Global.ClashMixedPort}";
+                startInfo.EnvironmentVariables["https_proxy"] = $"http://127.0.0.1:{Global.ClashMixedPort}";
+
+                // 否则环境变量无法被设置
+                startInfo.UseShellExecute = false;
+
+                // 设置工作路径
+                startInfo.WorkingDirectory = Utils.Utils.GetUserDir();
+
+                // 文件名
+                startInfo.FileName = name;
+
+                // 开启进程
+                Process.Start(startInfo);
+            }
+            catch
+            {
+
+            }
+        }
+
+        private void OpenCmdWithProxySetUp_Click(object sender, EventArgs e)
+        {
+            OpenProcessWithProxySetUp("cmd.exe");
+        }
+
+        private void OpenPowerShellWithProxySetUp_Click(object sender, EventArgs e)
+        {
+            OpenProcessWithProxySetUp("powershell.exe");
+        }
+
+        private void OpenWindowsTerminalWithProxySetUp_Click(object sender, EventArgs e)
+        {
+            OpenProcessWithProxySetUp("wt.exe");
+        }
+
+        private void OpenFluentTerminalWithProxySetUp_Click(object sender, EventArgs e)
+        {
+            OpenProcessWithProxySetUp("flute.exe");
+        }
+
+        #endregion
+
+        #region Copy Proxy Setting Commands
+
+        private void CopyCmdProxySettingCommands_Click(object sender, EventArgs e)
+        {
+            Clipboard.SetText($"set http_proxy=http://127.0.0.1:{Global.ClashMixedPort} & set https_proxy=http://127.0.0.1:{Global.ClashMixedPort}");
+        }
+
+        private void CopyPowerShellProxySettingCommands_Click(object sender, EventArgs e)
+        {
+            Clipboard.SetText($"$Env:http_proxy=\"http://127.0.0.1:{Global.ClashMixedPort}\";$Env:https_proxy=\"http://127.0.0.1:{Global.ClashMixedPort}\"");
+        }
+
+        #endregion
+
+        #region Set Telegram Proxy
+
+        private void SetSocks5ProxyToTelegram_Click(object sender, EventArgs e)
+        {
+            Process.Start($"tg://socks?server=127.0.0.1&port={Global.ClashMixedPort}");
+        }
+
+        #endregion
+
+        #region NetFilter Driver
+
+        private void InstallNetFilterDriver_Click(object sender, EventArgs e)
+        {
+            try
+            {
+                Global.nfController.InstallDriver();
+            }
+            catch
+            {
+
+            }
+        }
+
+        private void UninstallNetFilterDriver_Click(object sender, EventArgs e)
+        {
+            try
+            {
+                Global.nfController.UninstallDriver();
+            }
+            catch
+            {
+
+            }
+        }
+
+        #endregion
+
+        #region Enable UWP Loopback
+
+        private void EnableUWPLoopback_Click(object sender, EventArgs e)
+        {
+            try
+            {
+                Process.Start(Path.Combine(Global.ClashDotNetFrameworkDir, "bin\\EnableLoopback.exe"));
+            }
+            catch
+            {
+            }
+        }
+
+        #endregion
+
+        #region Quit
+
+        private void QuitItem_Click(object sender, EventArgs e)
+        {
+            Application.Current.Shutdown();
+        }
+
+        #endregion
+    }
+}

+ 25 - 0
ClashDotNetFramework/Controllers/PcapController.cs

@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ClashDotNetFramework.Controllers
+{
+    public class PcapController : Guard
+    {
+        public override string MainFile { get; protected set; } = "Pcap2socks.exe";
+
+        public override string Name { get; } = "Pcap2socks";
+
+        public void Start()
+        {
+
+        }
+
+        public override void Stop()
+        {
+            StopInstance();
+        }
+    }
+}

+ 75 - 0
ClashDotNetFramework/Controllers/UpdateChecker.cs

@@ -0,0 +1,75 @@
+using ClashDotNetFramework.Models.GitHubRelease;
+using ClashDotNetFramework.Utils;
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace ClashDotNetFramework.Controllers
+{
+    public static class UpdateChecker
+    {
+        public const string Owner = @"ClashDotNetFramework";
+        public const string Repo = @"ClashDotNetFramework";
+
+        public const string Name = @"Clash .NET Framework";
+        public const string Copyright = @"Copyright © 2018 - 2021 Coel Wu";
+
+        public const string AssemblyVersion = @"0.0.8";
+
+        public static readonly string Version = $"{AssemblyVersion}";
+
+        public static string LatestVersionNumber;
+        public static string LatestVersionUrl;
+        public static Release LatestRelease;
+
+        public static event EventHandler NewVersionFound;
+
+        public static event EventHandler NewVersionFoundFailed;
+
+        public static event EventHandler NewVersionNotFound;
+
+
+        public static async void Check(bool isPreRelease)
+        {
+            try
+            {
+                var updater = new GitHubRelease(Owner, Repo);
+                var url = updater.AllReleaseUrl;
+
+                var json = await WebUtil.DownloadStringAsync(WebUtil.CreateRequest(url));
+
+                var releases = JsonConvert.DeserializeObject<List<Release>>(json);
+                LatestRelease = VersionUtil.GetLatestRelease(releases, isPreRelease);
+                LatestVersionNumber = LatestRelease.tag_name;
+                LatestVersionUrl = LatestRelease.html_url;
+                Logging.Info($"Github 最新发布版本: {LatestRelease.tag_name}");
+                if (VersionUtil.CompareVersion(LatestRelease.tag_name, Version) > 0)
+                {
+                    Logging.Info("发现新版本");
+                    NewVersionFound?.Invoke(null, new EventArgs());
+                }
+                else
+                {
+                    Logging.Info("目前是最新版本");
+                    NewVersionNotFound?.Invoke(null, new EventArgs());
+                }
+            }
+            catch (Exception e)
+            {
+                if (e is WebException)
+                    Logging.Warning($"获取新版本失败: {e.Message}");
+                else
+                    Logging.Warning(e.ToString());
+
+                NewVersionFoundFailed?.Invoke(null, new EventArgs());
+            }
+        }
+    }
+}