├── .gitignore
├── LICENSE
├── README.md
└── clashN
├── clashN.sln
├── clashN
├── App.xaml
├── App.xaml.cs
├── AssemblyInfo.cs
├── Base
│ ├── HttpClientHelper.cs
│ └── StringEx.cs
├── Converters
│ ├── DelayColorConverter.cs
│ └── MaterialDesignFonts.cs
├── FodyWeavers.xml
├── FodyWeavers.xsd
├── Global.cs
├── Handler
│ ├── ConfigProc.cs
│ ├── CoreConfigHandler.cs
│ ├── CoreHandler.cs
│ ├── DownloadHandle.cs
│ ├── LazyConfig.cs
│ ├── MainFormHandler.cs
│ ├── NoticeHandler.cs
│ ├── ProxySetting.cs
│ ├── QRCodeHelper.cs
│ ├── SpeedtestHandler.cs
│ ├── StatisticsHandler.cs
│ ├── SysProxyHandle.cs
│ └── UpdateHandle.cs
├── Mode
│ ├── ClashConnections.cs
│ ├── ClashProviders.cs
│ ├── ClashProxies.cs
│ ├── ComboItem.cs
│ ├── Config.cs
│ ├── ConnectionModel.cs
│ ├── CoreInfo.cs
│ ├── CoreKind.cs
│ ├── EProfileColName.cs
│ ├── ERuleMode.cs
│ ├── ESpeedActionType.cs
│ ├── GlobalHotkeyAction.cs
│ ├── KeyShortcut.cs
│ ├── MovementTarget.cs
│ ├── ProfileItemModel.cs
│ ├── ProxyModel.cs
│ ├── ServerStatistics.cs
│ ├── ServerTestItem.cs
│ └── SysProxyType.cs
├── Properties
│ ├── Resources.Designer.cs
│ └── Resources.resx
├── Resources
│ ├── NotifyIcon1.ico
│ ├── NotifyIcon2.ico
│ ├── NotifyIcon3.ico
│ ├── sysproxy.exe.gz
│ └── sysproxy64.exe.gz
├── Resx
│ ├── ResUI.Designer.cs
│ ├── ResUI.fa-IR.resx
│ ├── ResUI.resx
│ └── ResUI.zh-Hans.resx
├── Sample
│ ├── SampleMixin.yaml
│ └── SampleTun.yaml
├── Tool
│ ├── FileManager.cs
│ ├── Job.cs
│ ├── Logging.cs
│ ├── QueryableExtension.cs
│ ├── UI.cs
│ └── Utils.cs
├── ViewModels
│ ├── ConnectionsViewModel.cs
│ ├── DashboardViewModel.cs
│ ├── HelpViewModel.cs
│ ├── LogsViewModel.cs
│ ├── MainWindowViewModel.cs
│ ├── ProfileEditViewModel.cs
│ ├── ProfilesViewModel.cs
│ ├── PromotionViewModel.cs
│ ├── ProxiesViewModel.cs
│ └── SettingsViewModel.cs
├── Views
│ ├── ConnectionsView.xaml
│ ├── ConnectionsView.xaml.cs
│ ├── DashboardView.xaml
│ ├── DashboardView.xaml.cs
│ ├── GlobalHotkeySettingWindow.xaml
│ ├── GlobalHotkeySettingWindow.xaml.cs
│ ├── HelpView.xaml
│ ├── HelpView.xaml.cs
│ ├── LogsView.xaml
│ ├── LogsView.xaml.cs
│ ├── MainWindow.xaml
│ ├── MainWindow.xaml.cs
│ ├── MessageSampleDialog.xaml
│ ├── MessageSampleDialog.xaml.cs
│ ├── MsgView.xaml
│ ├── MsgView.xaml.cs
│ ├── PorfileEditWindow.xaml
│ ├── PorfileEditWindow.xaml.cs
│ ├── ProfileQrcodeView.xaml
│ ├── ProfileQrcodeView.xaml.cs
│ ├── ProfilesView.xaml
│ ├── ProfilesView.xaml.cs
│ ├── PromotionView.xaml
│ ├── PromotionView.xaml.cs
│ ├── ProxiesView.xaml
│ ├── ProxiesView.xaml.cs
│ ├── SettingsView.xaml
│ └── SettingsView.xaml.cs
├── app.manifest
├── clashN.csproj
└── clashN.ico
└── clashUpgrade
├── MainForm.Designer.cs
├── MainForm.cs
├── MainForm.resx
├── Program.cs
├── Properties
├── Resources.Designer.cs
├── Resources.resx
├── Settings.Designer.cs
└── Settings.settings
└── clashUpgrade.csproj
/.gitignore:
--------------------------------------------------------------------------------
1 | ################################################################################
2 | # 此 .gitignore 文件已由 Microsoft(R) Visual Studio 自动创建。
3 | ################################################################################
4 | `
5 | /clashN/.vs/
6 | /clashN/clashN/bin/Debug/app.publish
7 | /clashN/clashN/bin/Debug
8 | /clashN/clashN/bin/Release
9 | /clashN/clashN/obj/
10 | /clashN/.vs/clashN/DesignTimeBuild
11 | /clashN/packages
12 | .vs/ProjectSettings.json
13 | .vs/slnx.sqlite
14 | .vs/VSWorkspaceState.json
15 | /clashN/clashUpgrade/bin/Debug
16 | /clashN/clashUpgrade/obj/Debug
17 | /clashN/clashUpgrade/bin/Release
18 | /clashN/clashUpgrade/obj/Release
19 | /clashN/clashUpgrade/obj
20 | /clashN/clashUpgrade/Properties/PublishProfiles
21 | /clashN/clashN/Properties/PublishProfiles
22 | *.user
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## [本项目功能将合并到v2rayN项目](https://github.com/2dust/clashN/issues/358)
2 |
3 |
4 | # ClashN
5 | A clash client for Windows, supports [Mihomo core](https://github.com/MetaCubeX/Mihomo)
6 |
7 | [](https://github.com/2dust/clashn/commits/master)
8 | [](https://github.com/2dust/clashn/releases)
9 |
10 | ### How to use
11 | 1. Download `clashN.zip` from [releases](https://github.com/2dust/clashN/releases)
12 | 2. Unzip to any folder you want
13 | 3. Run ClashN.exe
14 |
15 |
16 |
17 | ### Tips
18 |
19 | - You can also add `v2ray` subscription to `ClashN`, just enable `Subcription conversion` when you add a profile.
20 |
21 |
22 |
23 | ### Requirements
24 | - Microsoft [.NET 8.0 Desktop Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/8.0/runtime)
25 | - Mihomo core [https://github.com/MetaCubeX/Mihomo/releases](https://github.com/MetaCubeX/Mihomo/releases)
26 |
27 |
28 |
29 | ### Telegram Channel
30 | [github_2dust](https://t.me/github_2dust)
31 |
32 |
33 |
34 | ### Contribute
35 |
36 | To build this project, you should clone both [ClashN](https://github.com/2dust/clashN) and [v2rayN](https://github.com/2dust/v2rayN) to the same folder
37 | ```
38 | Example folder
39 | ├─ ClashN
40 | | ├─ clashN
41 | | | ├─ clashN.sln
42 | | | └─ ...
43 | | ├─ README.md
44 | | └─ ...
45 | ├─ v2rayN
46 | | ├─ v2rayN
47 | | | ├─ v2rayN.sln
48 | | | └─ ...
49 | | ├─ README.md
50 | | └─ ...
51 | └─ ...
52 | ```
53 |
--------------------------------------------------------------------------------
/clashN/clashN.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.3.32811.315
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClashN", "clashN\ClashN.csproj", "{6DE127CA-1763-4236-B297-D2EF9CB2EC9B}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClashUpgrade", "clashUpgrade\ClashUpgrade.csproj", "{59E0AF2B-9915-47F2-9F5F-9008F47DAE5F}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PacLib", "..\..\v2rayN\v2rayN\PacLib\PacLib.csproj", "{E27F1C24-9A0A-47D5-8723-B17D00F264FB}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Release|Any CPU = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {6DE127CA-1763-4236-B297-D2EF9CB2EC9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {6DE127CA-1763-4236-B297-D2EF9CB2EC9B}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {6DE127CA-1763-4236-B297-D2EF9CB2EC9B}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {6DE127CA-1763-4236-B297-D2EF9CB2EC9B}.Release|Any CPU.Build.0 = Release|Any CPU
22 | {59E0AF2B-9915-47F2-9F5F-9008F47DAE5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {59E0AF2B-9915-47F2-9F5F-9008F47DAE5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {59E0AF2B-9915-47F2-9F5F-9008F47DAE5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {59E0AF2B-9915-47F2-9F5F-9008F47DAE5F}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {E27F1C24-9A0A-47D5-8723-B17D00F264FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {E27F1C24-9A0A-47D5-8723-B17D00F264FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {E27F1C24-9A0A-47D5-8723-B17D00F264FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {E27F1C24-9A0A-47D5-8723-B17D00F264FB}.Release|Any CPU.Build.0 = Release|Any CPU
30 | EndGlobalSection
31 | GlobalSection(SolutionProperties) = preSolution
32 | HideSolutionNode = FALSE
33 | EndGlobalSection
34 | GlobalSection(ExtensibilityGlobals) = postSolution
35 | SolutionGuid = {43E06CBD-3DA9-40A3-8E4D-F0943CB0DD32}
36 | EndGlobalSection
37 | EndGlobal
38 |
--------------------------------------------------------------------------------
/clashN/clashN/App.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
16 |
17 |
18 | 11
19 | 12
20 | 13
21 | 14
22 |
28 |
34 |
40 |
46 |
52 |
58 |
64 |
67 |
84 |
90 |
93 |
100 |
107 |
113 |
119 |
125 |
131 |
132 |
133 |
--------------------------------------------------------------------------------
/clashN/clashN/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using ClashN.Handler;
2 | using ClashN.Mode;
3 | using ClashN.Tool;
4 | using System.Diagnostics;
5 | using System.Windows;
6 | using System.Windows.Threading;
7 |
8 | namespace ClashN
9 | {
10 | ///
11 | /// Interaction logic for App.xaml
12 | ///
13 | public partial class App : Application
14 | {
15 | public static EventWaitHandle? ProgramStarted;
16 | private Config? _config;
17 |
18 | static App()
19 | {
20 | ProgramStarted = new EventWaitHandle(false, EventResetMode.AutoReset, "ProgramStartedEvent", out bool bCreatedNew);
21 | if (!bCreatedNew)
22 | {
23 | ProgramStarted.Set();
24 | Environment.Exit(-1);
25 | return;
26 | }
27 | }
28 |
29 | public App()
30 | {
31 | // Locator.CurrentMutable.RegisterViewsForViewModels(Assembly.GetCallingAssembly());
32 |
33 | this.DispatcherUnhandledException += App_DispatcherUnhandledException;
34 | AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
35 | TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
36 | }
37 |
38 | ///
39 | /// 只打开一个进程
40 | ///
41 | ///
42 | protected override void OnStartup(StartupEventArgs e)
43 | {
44 | foreach (string arg in e.Args)
45 | {
46 | Utils.SetClipboardData(arg);
47 | }
48 |
49 | Global.processJob = new Job();
50 |
51 | Logging.Setup();
52 | Utils.SaveLog($"ClashN start up | {Utils.GetVersion()} | {Utils.GetExePath()}");
53 | Logging.ClearLogs();
54 |
55 | Init();
56 |
57 | string lang = Utils.RegReadValue(Global.MyRegPath, Global.MyRegKeyLanguage, Global.Languages[0]);
58 | Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(lang);
59 |
60 | base.OnStartup(e);
61 | }
62 |
63 | private void Init()
64 | {
65 | if (ConfigProc.LoadConfig(ref _config) != 0)
66 | {
67 | UI.ShowWarning($"Loading GUI configuration file is abnormal,please restart the application{Environment.NewLine}加载GUI配置文件异常,请重启应用");
68 | Environment.Exit(0);
69 | return;
70 | }
71 | }
72 |
73 | private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
74 | {
75 | Utils.SaveLog("App_DispatcherUnhandledException", e.Exception);
76 | e.Handled = true;
77 | }
78 |
79 | private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
80 | {
81 | if (e.ExceptionObject != null)
82 | {
83 | Utils.SaveLog("CurrentDomain_UnhandledException", (Exception)e.ExceptionObject!);
84 | }
85 | }
86 |
87 | private void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e)
88 | {
89 | Utils.SaveLog("TaskScheduler_UnobservedTaskException", e.Exception);
90 | }
91 | protected override void OnExit(ExitEventArgs e)
92 | {
93 | Utils.SaveLog("OnExit");
94 | base.OnExit(e);
95 | Process.GetCurrentProcess().Kill();
96 | }
97 | }
98 | }
--------------------------------------------------------------------------------
/clashN/clashN/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | [assembly: ThemeInfo(
4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
5 | //(used if a resource is not found in the page,
6 | // or application resource dictionaries)
7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
8 | //(used if a resource is not found in the page,
9 | // app, or any theme specific resource dictionaries)
10 | )]
--------------------------------------------------------------------------------
/clashN/clashN/Base/HttpClientHelper.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Net.Http;
3 | using System.Net.Http.Headers;
4 |
5 | namespace ClashN.Base
6 | {
7 | ///
8 | ///
9 | public class HttpClientHelper
10 | {
11 | private static HttpClientHelper httpClientHelper = null;
12 | private HttpClient httpClient;
13 | private int progressPercentage = -1;
14 |
15 | ///
16 | ///
17 | private HttpClientHelper()
18 | {
19 | httpClient = new HttpClient(new HttpClientHandler()
20 | {
21 | UseCookies = false
22 | });
23 | }
24 |
25 | ///
26 | ///
27 | ///
28 | public static HttpClientHelper GetInstance()
29 | {
30 | if (httpClientHelper != null)
31 | {
32 | return httpClientHelper;
33 | }
34 | else
35 | {
36 | HttpClientHelper httpClientHelper = new HttpClientHelper();
37 |
38 | HttpClientHandler handler = new HttpClientHandler() { UseCookies = false };
39 | httpClientHelper.httpClient = new HttpClient(handler);
40 | return httpClientHelper;
41 | }
42 | }
43 |
44 | public async Task TryGetAsync(string url)
45 | {
46 | if (string.IsNullOrEmpty(url))
47 | return null;
48 |
49 | try
50 | {
51 | HttpResponseMessage response = await httpClient.GetAsync(url);
52 | return await response.Content.ReadAsStringAsync();
53 | }
54 | catch
55 | {
56 | return null;
57 | }
58 | }
59 |
60 | public async Task<(string, HttpResponseHeaders)> GetAsync(HttpClient client, string url, CancellationToken token)
61 | {
62 | if (string.IsNullOrEmpty(url))
63 | {
64 | return (null, null);
65 | }
66 | HttpResponseMessage response = await client.GetAsync(url, token);
67 | if (!response.IsSuccessStatusCode)
68 | {
69 | throw new Exception(string.Format("The request returned with HTTP status code {0}", response.StatusCode));
70 | }
71 | return (await response.Content.ReadAsStringAsync(), response.Headers);
72 | }
73 |
74 | public async Task PutAsync(string url, Dictionary headers)
75 | {
76 | var myContent = Utils.ToJson(headers);
77 | var buffer = System.Text.Encoding.UTF8.GetBytes(myContent);
78 | var byteContent = new ByteArrayContent(buffer);
79 | byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
80 |
81 | await httpClient.PutAsync(url, byteContent);
82 | }
83 |
84 | public async Task PatchAsync(string url, Dictionary headers)
85 | {
86 | var myContent = Utils.ToJson(headers);
87 | var buffer = System.Text.Encoding.UTF8.GetBytes(myContent);
88 | var byteContent = new ByteArrayContent(buffer);
89 | byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
90 |
91 | await httpClient.PatchAsync(url, byteContent);
92 | }
93 |
94 | public async Task DeleteAsync(string url)
95 | {
96 | //var myContent = Utils.ToJson(headers);
97 | //var buffer = System.Text.Encoding.UTF8.GetBytes(myContent);
98 | //var byteContent = new ByteArrayContent(buffer);
99 | //byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
100 |
101 | await httpClient.DeleteAsync(url);
102 | }
103 |
104 | public async Task DownloadFileAsync(HttpClient client, string url, string fileName, IProgress progress, CancellationToken token)
105 | {
106 | if (string.IsNullOrEmpty(url))
107 | throw new ArgumentNullException(nameof(url));
108 | if (string.IsNullOrEmpty(fileName))
109 | throw new ArgumentNullException(nameof(fileName));
110 |
111 | HttpResponseMessage response =
112 | await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);
113 |
114 | if (!response.IsSuccessStatusCode)
115 | throw new Exception(string.Format("The request returned with HTTP status code {0}", response.StatusCode));
116 |
117 | var total = response.Content.Headers.ContentLength.HasValue ? response.Content.Headers.ContentLength.Value : -1L;
118 | var canReportProgress = total != -1 && progress != null;
119 |
120 | using var stream = await response.Content.ReadAsStreamAsync();
121 | using var file = File.Create(fileName);
122 |
123 | var totalRead = 0L;
124 | var buffer = new byte[1024 * 1024];
125 | var isMoreToRead = true;
126 | progressPercentage = -1;
127 |
128 | do
129 | {
130 | token.ThrowIfCancellationRequested();
131 |
132 | var read = await stream.ReadAsync(buffer, 0, buffer.Length, token);
133 |
134 | if (read == 0)
135 | {
136 | isMoreToRead = false;
137 | }
138 | else
139 | {
140 | var data = new byte[read];
141 | buffer.ToList().CopyTo(0, data, 0, read);
142 |
143 | // TODO: put here the code to write the file to disk
144 | file.Write(data, 0, read);
145 |
146 | totalRead += read;
147 |
148 | if (canReportProgress)
149 | {
150 | var percent = Convert.ToInt32((totalRead * 1d) / (total * 1d) * 100);
151 | if (progressPercentage != percent && percent % 10 == 0)
152 | {
153 | progressPercentage = percent;
154 | progress?.Report(percent);
155 | }
156 | }
157 | }
158 | } while (isMoreToRead);
159 |
160 | if (canReportProgress)
161 | progress?.Report(101);
162 | }
163 | }
164 | }
--------------------------------------------------------------------------------
/clashN/clashN/Base/StringEx.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace ClashN.Base
4 | {
5 | internal static class StringEx
6 | {
7 | public static bool BeginWithAny(this string? s, IEnumerable chars)
8 | {
9 | if (string.IsNullOrEmpty(s))
10 | return false;
11 |
12 | return chars.Contains(s[0]);
13 | }
14 |
15 | public static IEnumerable NonWhiteSpaceLines(this TextReader reader)
16 | {
17 | string? line;
18 | while ((line = reader.ReadLine()) != null)
19 | {
20 | if (string.IsNullOrWhiteSpace(line))
21 | continue;
22 | yield return line;
23 | }
24 | }
25 |
26 | public static string TrimEx(this string? value)
27 | {
28 | return value == null ? string.Empty : value.Trim();
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/clashN/clashN/Converters/DelayColorConverter.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Data;
2 | using System.Windows.Media;
3 |
4 | namespace ClashN.Converters
5 | {
6 | public class DelayColorConverter : IValueConverter
7 | {
8 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
9 | {
10 | var temp = (int)value;
11 |
12 | if (temp <= 200)
13 | return new SolidColorBrush(Colors.Green);
14 | else
15 | return new SolidColorBrush(Colors.IndianRed);
16 | }
17 |
18 | public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
19 | {
20 | return null;
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/clashN/clashN/Converters/MaterialDesignFonts.cs:
--------------------------------------------------------------------------------
1 | using ClashN.Handler;
2 | using System.Windows.Media;
3 |
4 | namespace ClashN.Converters
5 | {
6 | public class MaterialDesignFonts
7 | {
8 | public static FontFamily MyFont { get; }
9 |
10 | static MaterialDesignFonts()
11 | {
12 | try
13 | {
14 | var fontFamily = LazyConfig.Instance.Config.UiItem.currentFontFamily;
15 | if (!string.IsNullOrEmpty(fontFamily))
16 | {
17 | var fontPath = Utils.GetFontsPath();
18 | MyFont = new FontFamily(new Uri(@$"file:///{fontPath}\"), $"./#{fontFamily}");
19 | }
20 | }
21 | catch
22 | {
23 | }
24 | if (MyFont is null)
25 | {
26 | MyFont = new FontFamily("Microsoft YaHei");
27 | }
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/clashN/clashN/FodyWeavers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/clashN/clashN/FodyWeavers.xsd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
12 |
13 |
14 |
15 |
16 | A comma-separated list of error codes that can be safely ignored in assembly verification.
17 |
18 |
19 |
20 |
21 | 'false' to turn off automatic generation of the XML Schema file.
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/clashN/clashN/Global.cs:
--------------------------------------------------------------------------------
1 | namespace ClashN
2 | {
3 | internal class Global
4 | {
5 | #region 常量
6 |
7 | public const string AboutUrl = @"https://github.com/2dust/clashN";
8 | public const string UpdateUrl = AboutUrl + @"/releases";
9 | public const string NUrl = @"https://github.com/2dust/clashN/releases";
10 | public const string clashCoreUrl = "https://github.com/Dreamacro/clash/releases";
11 | public const string clashMetaCoreUrl = "https://github.com/MetaCubeX/Clash.Meta/releases";
12 | public const string mihomoCoreUrl = "https://github.com/MetaCubeX/mihomo/releases";
13 | public const string geoUrl = "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/{0}.dat";
14 |
15 | ///
16 | /// SpeedTestUrl
17 | ///
18 | public const string SpeedTestUrl = @"http://cachefly.cachefly.net/10mb.test";
19 |
20 | public const string SpeedPingTestUrl = @"https://www.google.com/generate_204";
21 |
22 | public static readonly List SubConvertUrls = new List {
23 | @"https://sub.xeton.dev/sub?target=clash&url={0}",
24 | @"https://api.dler.io/sub?target=clash&url={0}",
25 | @"http://127.0.0.1:25500/sub?target=clash&url={0}",
26 | ""
27 | };
28 |
29 | public static readonly List SubConvertConfig = new List {
30 | @"https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online.ini"
31 | };
32 |
33 | ///
34 | /// PromotionUrl
35 | ///
36 | public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw=";
37 |
38 | ///
39 | /// 本软件配置文件名
40 | ///
41 | public const string ConfigFileName = "guiNConfig.json";
42 |
43 | ///
44 | /// 配置文件名
45 | ///
46 | public const string coreConfigFileName = "config.yaml";
47 |
48 | public const string mixinConfigFileName = "Mixin.yaml";
49 |
50 | public const string SampleMixin = "ClashN.Sample.SampleMixin.yaml";
51 | public const string SampleTun = "ClashN.Sample.SampleTun.yaml";
52 |
53 | public const string InboundSocks = "socks";
54 | public const string InboundHttp = "http";
55 | public const string Loopback = "127.0.0.1";
56 |
57 | ///
58 | /// http
59 | ///
60 | public const string httpProtocol = "http://";
61 |
62 | ///
63 | /// https
64 | ///
65 | public const string httpsProtocol = "https://";
66 |
67 | public const string clashProtocol = "clash://";
68 |
69 | ///
70 | /// MyRegPath
71 | ///
72 | public const string MyRegPath = "Software\\clashNGUI";
73 |
74 | ///
75 | /// Language
76 | ///
77 | public const string MyRegKeyLanguage = "CurrentLanguage";
78 |
79 | ///
80 | /// Font
81 | ///
82 | public const string MyRegKeyFont = "CurrentFont";
83 |
84 | ///
85 | /// URL Schemes
86 | ///
87 | public const string MyRegPathClasses = "SOFTWARE\\Classes\\clash";
88 |
89 | ///
90 | /// Icon
91 | ///
92 | public const string CustomIconName = "ClashN.ico";
93 |
94 | public const int MinFontSize = 10;
95 |
96 | public const string StatisticLogOverall = "StatisticLogOverall.json";
97 |
98 | public const string IEProxyExceptions = "localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;192.168.*";
99 |
100 | public static readonly List IEProxyProtocols = new List {
101 | "{ip}:{http_port}",
102 | "socks={ip}:{socks_port}",
103 | "http={ip}:{http_port};https={ip}:{http_port};ftp={ip}:{http_port};socks={ip}:{socks_port}",
104 | "http=http://{ip}:{http_port};https=http://{ip}:{http_port}",
105 | ""
106 | };
107 |
108 | public static readonly List coreTypes = new List { "Clash", "ClashPremium", "ClashMeta", "Mihomo" };
109 |
110 | public static readonly List allowSelectType = new List { "selector", "urltest", "loadbalance", "fallback" };
111 |
112 | public static readonly List notAllowTestType = new List { "selector", "urltest", "direct", "reject", "compatible", "pass", "loadbalance", "fallback" };
113 |
114 | public static readonly List proxyVehicleType = new List { "file", "http" };
115 |
116 | public static readonly List Languages = new List { "zh-Hans", "en", "fa-IR" };
117 |
118 | public static readonly List LogLevel = new List { "debug", "info", "warning", "error", "silent" };
119 |
120 | #endregion 常量
121 |
122 | #region 全局变量
123 |
124 | ///
125 | /// 是否需要重启服务
126 | ///
127 | public static bool reloadCore
128 | {
129 | get; set;
130 | }
131 |
132 | public static Job processJob
133 | {
134 | get; set;
135 | }
136 |
137 | public static bool ShowInTaskbar
138 | {
139 | get; set;
140 | }
141 |
142 | #endregion 全局变量
143 | }
144 | }
--------------------------------------------------------------------------------
/clashN/clashN/Handler/DownloadHandle.cs:
--------------------------------------------------------------------------------
1 | using ClashN.Base;
2 | using ClashN.Resx;
3 | using System.IO;
4 | using System.Net;
5 | using System.Net.Http;
6 | using System.Net.Http.Headers;
7 | using System.Net.Sockets;
8 |
9 | namespace ClashN.Handler
10 | {
11 | ///
12 | ///Download
13 | ///
14 | internal class DownloadHandle
15 | {
16 | public event EventHandler? UpdateCompleted;
17 |
18 | public event ErrorEventHandler? Error;
19 |
20 | public class ResultEventArgs : EventArgs
21 | {
22 | public bool Success;
23 | public string Msg;
24 |
25 | public ResultEventArgs(bool success, string msg)
26 | {
27 | this.Success = success;
28 | this.Msg = msg;
29 | }
30 | }
31 |
32 | public void DownloadFileAsync(string url, bool blProxy, int downloadTimeout)
33 | {
34 | try
35 | {
36 | Utils.SetSecurityProtocol(LazyConfig.Instance.Config.EnableSecurityProtocolTls13);
37 | UpdateCompleted?.Invoke(this, new ResultEventArgs(false, ResUI.Downloading));
38 |
39 | var client = new HttpClient(new SocketsHttpHandler()
40 | {
41 | Proxy = GetWebProxy(blProxy)
42 | });
43 |
44 | var progress = new Progress();
45 | progress.ProgressChanged += (sender, value) =>
46 | {
47 | if (UpdateCompleted != null)
48 | {
49 | string msg = string.Format("...{0}%", value);
50 | UpdateCompleted(this, new ResultEventArgs(value > 100 ? true : false, msg));
51 | }
52 | };
53 |
54 | var cancellationToken = new CancellationTokenSource();
55 | _ = HttpClientHelper.GetInstance().DownloadFileAsync(client,
56 | url,
57 | Utils.GetTempPath(Utils.GetDownloadFileName(url)),
58 | progress,
59 | cancellationToken.Token);
60 | }
61 | catch (Exception ex)
62 | {
63 | Utils.SaveLog(ex.Message, ex);
64 |
65 | Error?.Invoke(this, new ErrorEventArgs(ex));
66 | }
67 | }
68 |
69 | ///
70 | /// DownloadString
71 | ///
72 | ///
73 | public async Task<(string, HttpResponseHeaders)?> DownloadStringAsync(string url, bool blProxy, string userAgent)
74 | {
75 | try
76 | {
77 | Utils.SetSecurityProtocol(LazyConfig.Instance.Config.EnableSecurityProtocolTls13);
78 | var webProxy = GetWebProxy(blProxy);
79 | var client = new HttpClient(new SocketsHttpHandler()
80 | {
81 | Proxy = webProxy,
82 | UseProxy = webProxy != null
83 | });
84 |
85 | if (string.IsNullOrEmpty(userAgent))
86 | {
87 | userAgent = $"{Utils.GetVersion(false)}";
88 | }
89 | client.DefaultRequestHeaders.UserAgent.TryParseAdd(userAgent);
90 |
91 | Uri uri = new Uri(url);
92 | //Authorization Header
93 | if (!string.IsNullOrEmpty(uri.UserInfo))
94 | {
95 | client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Utils.Base64Encode(uri.UserInfo));
96 | }
97 |
98 | var cts = new CancellationTokenSource();
99 | cts.CancelAfter(1000 * 30);
100 |
101 | var result = await HttpClientHelper.GetInstance().GetAsync(client, url, cts.Token);
102 | return result;
103 | }
104 | catch (Exception ex)
105 | {
106 | Utils.SaveLog(ex.Message, ex);
107 | Error?.Invoke(this, new ErrorEventArgs(ex));
108 | if (ex.InnerException != null)
109 | {
110 | Error?.Invoke(this, new ErrorEventArgs(ex.InnerException));
111 | }
112 | }
113 |
114 | return null;
115 | }
116 |
117 | public async Task UrlRedirectAsync(string url, bool blProxy)
118 | {
119 | Utils.SetSecurityProtocol(LazyConfig.Instance.Config.EnableSecurityProtocolTls13);
120 | SocketsHttpHandler webRequestHandler = new SocketsHttpHandler
121 | {
122 | AllowAutoRedirect = false,
123 | Proxy = GetWebProxy(blProxy)
124 | };
125 | HttpClient client = new HttpClient(webRequestHandler);
126 |
127 | HttpResponseMessage response = await client.GetAsync(url);
128 | if (response.StatusCode == HttpStatusCode.Found || response.StatusCode == HttpStatusCode.Redirect)
129 | {
130 | return response.Headers.Location?.ToString();
131 | }
132 | else
133 | {
134 | Utils.SaveLog("StatusCode error: " + url);
135 | return null;
136 | }
137 | }
138 |
139 | private WebProxy GetWebProxy(bool blProxy)
140 | {
141 | if (!blProxy)
142 | {
143 | return null;
144 | }
145 | var socksPort = LazyConfig.Instance.Config.SocksPort;
146 | if (!SocketCheck(Global.Loopback, socksPort))
147 | {
148 | return null;
149 | }
150 |
151 | return new WebProxy($"socks5://{Global.Loopback}:{socksPort}");
152 | }
153 |
154 | private bool SocketCheck(string ip, int port)
155 | {
156 | Socket sock = null;
157 | try
158 | {
159 | IPAddress ipa = IPAddress.Parse(ip);
160 | IPEndPoint point = new IPEndPoint(ipa, port);
161 | sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
162 | sock.Connect(point);
163 | return true;
164 | }
165 | catch { }
166 | finally
167 | {
168 | if (sock != null)
169 | {
170 | sock.Close();
171 | sock.Dispose();
172 | }
173 | }
174 | return false;
175 | }
176 | }
177 | }
--------------------------------------------------------------------------------
/clashN/clashN/Handler/LazyConfig.cs:
--------------------------------------------------------------------------------
1 | using ClashN.Mode;
2 | using System.Runtime.Intrinsics.X86;
3 | using static ClashN.Mode.ClashProxies;
4 |
5 | namespace ClashN.Handler
6 | {
7 | public sealed class LazyConfig
8 | {
9 | private static readonly Lazy _instance = new Lazy(() => new LazyConfig());
10 | private Config _config;
11 | private List coreInfos;
12 | private Dictionary _proxies;
13 |
14 | public static LazyConfig Instance
15 | {
16 | get { return _instance.Value; }
17 | }
18 |
19 | public void SetConfig(Config config)
20 | {
21 | _config = config;
22 | }
23 |
24 | public Config Config => _config;
25 |
26 | public void SetProxies(Dictionary proxies)
27 | {
28 | _proxies = proxies;
29 | }
30 |
31 | public Dictionary GetProxies()
32 | {
33 | return _proxies;
34 | }
35 |
36 | public Dictionary ProfileContent { get; set; }
37 |
38 | public CoreKind GetCoreType(ProfileItem profileItem)
39 | {
40 | if (profileItem != null && profileItem.coreType != null)
41 | {
42 | return (CoreKind)profileItem.coreType;
43 | }
44 | return CoreKind.Clash;
45 | }
46 |
47 | public CoreInfo GetCoreInfo(CoreKind coreType)
48 | {
49 | if (coreInfos == null)
50 | {
51 | InitCoreInfo();
52 | }
53 | return coreInfos.Where(t => t.coreType == coreType).FirstOrDefault();
54 | }
55 |
56 | private void InitCoreInfo()
57 | {
58 | coreInfos = new List();
59 |
60 | coreInfos.Add(new CoreInfo
61 | {
62 | coreType = CoreKind.ClashN,
63 | coreUrl = Global.NUrl,
64 | coreLatestUrl = Global.NUrl + "/latest",
65 | coreDownloadUrl32 = Global.NUrl + "/download/{0}/clashN-32.zip",
66 | coreDownloadUrl64 = Global.NUrl + "/download/{0}/clashN.zip",
67 | });
68 |
69 | coreInfos.Add(new CoreInfo
70 | {
71 | coreType = CoreKind.Clash,
72 | coreExes = new List { "clash-windows-amd64-v3", "clash-windows-amd64", "clash-windows-386", "clash" },
73 | arguments = "-f config.yaml",
74 | coreUrl = Global.clashCoreUrl,
75 | coreLatestUrl = Global.clashCoreUrl + "/latest",
76 | coreDownloadUrl32 = Global.clashCoreUrl + "/download/{0}/clash-windows-386-{0}.zip",
77 | coreDownloadUrl64 = Global.clashCoreUrl + "/download/{0}/clash-windows-amd64-{0}.zip",
78 | match = "Clash"
79 | });
80 |
81 | coreInfos.Add(new CoreInfo
82 | {
83 | coreType = CoreKind.ClashMeta,
84 | coreExes = new List { $"Clash.Meta-windows-amd64{(Avx2.X64.IsSupported ? "" : "-compatible")}", "Clash.Meta-windows-amd64-compatible", "Clash.Meta-windows-amd64", "Clash.Meta-windows-386", "Clash.Meta", "clash" },
85 | arguments = "-f config.yaml",
86 | coreUrl = Global.clashMetaCoreUrl,
87 | coreLatestUrl = Global.clashMetaCoreUrl + "/latest",
88 | coreDownloadUrl32 = Global.clashMetaCoreUrl + "/download/{0}/Clash.Meta-windows-386-{0}.zip",
89 | coreDownloadUrl64 = Global.clashMetaCoreUrl + "/download/{0}/Clash.Meta-windows-amd64" + (Avx2.X64.IsSupported ? "" : "-compatible") + "-{0}.zip",
90 | match = "Clash Meta"
91 | });
92 |
93 | coreInfos.Add(new CoreInfo
94 | {
95 | coreType = CoreKind.Mihomo,
96 | coreExes = new List { $"mihomo-windows-amd64{(Avx2.X64.IsSupported ? "" : "-compatible")}", "mihomo-windows-amd64-compatible", "mihomo-windows-amd64", "mihomo-windows-386", "mihomo", "clash" },
97 | arguments = "-f config.yaml",
98 | coreUrl = Global.mihomoCoreUrl,
99 | coreLatestUrl = Global.mihomoCoreUrl + "/latest",
100 | coreDownloadUrl32 = Global.mihomoCoreUrl + "/download/{0}/mihomo-windows-386-{0}.zip",
101 | coreDownloadUrl64 = Global.mihomoCoreUrl + "/download/{0}/mihomo-windows-amd64" + (Avx2.X64.IsSupported ? "" : "-compatible") + "-{0}.zip",
102 | match = "Mihomo"
103 | });
104 |
105 | coreInfos.Add(new CoreInfo
106 | {
107 | coreType = CoreKind.ClashPremium,
108 | coreExes = new List { "clash-windows-amd64-v3", "clash-windows-amd64", "clash-windows-386", "clash" },
109 | arguments = "-f config.yaml",
110 | coreUrl = Global.clashCoreUrl,
111 | coreLatestUrl = Global.clashCoreUrl + "/latest",
112 | coreDownloadUrl32 = Global.clashCoreUrl + "/download/{0}/clash-windows-386-{0}.zip",
113 | coreDownloadUrl64 = Global.clashCoreUrl + "/download/{0}/clash-windows-amd64-{0}.zip",
114 | match = "Clash"
115 | });
116 | }
117 | }
118 | }
--------------------------------------------------------------------------------
/clashN/clashN/Handler/NoticeHandler.cs:
--------------------------------------------------------------------------------
1 | using MaterialDesignThemes.Wpf;
2 | using ReactiveUI;
3 |
4 | namespace ClashN.Handler
5 | {
6 | public class NoticeHandler
7 | {
8 | private readonly ISnackbarMessageQueue _snackbarMessageQueue;
9 |
10 | public NoticeHandler(ISnackbarMessageQueue snackbarMessageQueue)
11 | {
12 | _snackbarMessageQueue = snackbarMessageQueue ?? throw new ArgumentNullException(nameof(snackbarMessageQueue));
13 |
14 | //_snackbarMessageQueue = snackbarMessageQueue;
15 | }
16 |
17 | public void Enqueue(object content)
18 | {
19 | _snackbarMessageQueue?.Enqueue(content);
20 | }
21 |
22 | public void SendMessage(string msg)
23 | {
24 | MessageBus.Current.SendMessage(msg, "MsgView");
25 | }
26 |
27 | public void SendMessage(string msg, bool time)
28 | {
29 | msg = $"{DateTime.Now} {msg}";
30 | MessageBus.Current.SendMessage(msg, "MsgView");
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/clashN/clashN/Handler/QRCodeHelper.cs:
--------------------------------------------------------------------------------
1 | using QRCoder;
2 | using QRCoder.Xaml;
3 | using System.Windows.Media;
4 |
5 | namespace ClashN.Handler
6 | {
7 | ///
8 | /// 含有QR码的描述类和包装编码和渲染
9 | ///
10 | public class QRCodeHelper
11 | {
12 | public static DrawingImage GetQRCode(string strContent)
13 | {
14 | try
15 | {
16 | QRCodeGenerator qrGenerator = new QRCodeGenerator();
17 | QRCodeData qrCodeData = qrGenerator.CreateQrCode(strContent, QRCodeGenerator.ECCLevel.H);
18 | XamlQRCode qrCode = new XamlQRCode(qrCodeData);
19 | DrawingImage qrCodeAsXaml = qrCode.GetGraphic(40);
20 | return qrCodeAsXaml;
21 | }
22 | catch
23 | {
24 | return null;
25 | }
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/clashN/clashN/Handler/SpeedtestHandler.cs:
--------------------------------------------------------------------------------
1 | using ClashN.Mode;
2 | using System.Diagnostics;
3 | using System.Net;
4 | using System.Net.Sockets;
5 |
6 | namespace ClashN.Handler
7 | {
8 | internal class SpeedtestHandler
9 | {
10 | private Config _config;
11 | private CoreHandler _coreHandler;
12 | private List _selecteds;
13 | private Action _updateFunc;
14 |
15 | public SpeedtestHandler(ref Config config)
16 | {
17 | _config = config;
18 | }
19 |
20 | public SpeedtestHandler(ref Config config, CoreHandler coreHandler, List selecteds, ESpeedActionType actionType, Action update)
21 | {
22 | _config = config;
23 | _coreHandler = coreHandler;
24 | //_selecteds = Utils.DeepCopy(selecteds);
25 | _updateFunc = update;
26 |
27 | _selecteds = new List();
28 | foreach (var it in selecteds)
29 | {
30 | _selecteds.Add(new ServerTestItem()
31 | {
32 | IndexId = it.indexId,
33 | Address = it.address
34 | });
35 | }
36 |
37 | if (actionType == ESpeedActionType.Ping)
38 | {
39 | Task.Run(() => RunPing());
40 | }
41 | else if (actionType == ESpeedActionType.Tcping)
42 | {
43 | Task.Run(() => RunTcping());
44 | }
45 | }
46 |
47 | private void RunPingSub(Action updateFun)
48 | {
49 | try
50 | {
51 | foreach (var it in _selecteds)
52 | {
53 | try
54 | {
55 | updateFun(it);
56 | }
57 | catch (Exception ex)
58 | {
59 | Utils.SaveLog(ex.Message, ex);
60 | }
61 | }
62 |
63 | Thread.Sleep(10);
64 | }
65 | catch (Exception ex)
66 | {
67 | Utils.SaveLog(ex.Message, ex);
68 | }
69 | }
70 |
71 | private void RunPing()
72 | {
73 | RunPingSub((ServerTestItem it) =>
74 | {
75 | long time = Utils.Ping(it.Address);
76 |
77 | _updateFunc(it.IndexId, FormatOut(time, "ms"));
78 | });
79 | }
80 |
81 | private void RunTcping()
82 | {
83 | RunPingSub((ServerTestItem it) =>
84 | {
85 | int time = GetTcpingTime(it.Address, it.Port);
86 |
87 | _updateFunc(it.IndexId, FormatOut(time, "ms"));
88 | });
89 | }
90 |
91 | public int RunAvailabilityCheck() // alias: isLive
92 | {
93 | try
94 | {
95 | int httpPort = _config.HttpPort;
96 |
97 | Task t = Task.Run(() =>
98 | {
99 | try
100 | {
101 | WebProxy webProxy = new WebProxy(Global.Loopback, httpPort);
102 | int responseTime = -1;
103 | string status = GetRealPingTime(LazyConfig.Instance.Config.ConstItem.speedPingTestUrl, webProxy, out responseTime);
104 | bool noError = string.IsNullOrEmpty(status);
105 | return noError ? responseTime : -1;
106 | }
107 | catch (Exception ex)
108 | {
109 | Utils.SaveLog(ex.Message, ex);
110 | return -1;
111 | }
112 | });
113 | return t.Result;
114 | }
115 | catch (Exception ex)
116 | {
117 | Utils.SaveLog(ex.Message, ex);
118 | return -1;
119 | }
120 | }
121 |
122 | private int GetTcpingTime(string url, int port)
123 | {
124 | int responseTime = -1;
125 |
126 | try
127 | {
128 | if (!IPAddress.TryParse(url, out IPAddress ipAddress))
129 | {
130 | IPHostEntry ipHostInfo = System.Net.Dns.GetHostEntry(url);
131 | ipAddress = ipHostInfo.AddressList[0];
132 | }
133 |
134 | Stopwatch timer = new Stopwatch();
135 | timer.Start();
136 |
137 | IPEndPoint endPoint = new IPEndPoint(ipAddress, port);
138 | Socket clientSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
139 |
140 | IAsyncResult result = clientSocket.BeginConnect(endPoint, null, null);
141 | if (!result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(5)))
142 | throw new TimeoutException("connect timeout (5s): " + url);
143 | clientSocket.EndConnect(result);
144 |
145 | timer.Stop();
146 | responseTime = timer.Elapsed.Milliseconds;
147 | clientSocket.Close();
148 | }
149 | catch (Exception ex)
150 | {
151 | Utils.SaveLog(ex.Message, ex);
152 | }
153 | return responseTime;
154 | }
155 |
156 | private string GetRealPingTime(string url, WebProxy webProxy, out int responseTime)
157 | {
158 | string msg = string.Empty;
159 | responseTime = -1;
160 | try
161 | {
162 | HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(url);
163 | myHttpWebRequest.Timeout = 5000;
164 | myHttpWebRequest.Proxy = webProxy;//new WebProxy(Global.Loopback, Global.httpPort);
165 |
166 | Stopwatch timer = new Stopwatch();
167 | timer.Start();
168 |
169 | HttpWebResponse myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
170 | if (myHttpWebResponse.StatusCode != HttpStatusCode.OK
171 | && myHttpWebResponse.StatusCode != HttpStatusCode.NoContent)
172 | {
173 | msg = myHttpWebResponse.StatusDescription;
174 | }
175 | timer.Stop();
176 | responseTime = timer.Elapsed.Milliseconds;
177 |
178 | myHttpWebResponse.Close();
179 | }
180 | catch (Exception ex)
181 | {
182 | Utils.SaveLog(ex.Message, ex);
183 | msg = ex.Message;
184 | }
185 | return msg;
186 | }
187 |
188 | private string FormatOut(object time, string unit)
189 | {
190 | if (time.ToString().Equals("-1"))
191 | {
192 | return "Timeout";
193 | }
194 | return string.Format("{0}{1}", time, unit).PadLeft(8, ' ');
195 | }
196 | }
197 | }
--------------------------------------------------------------------------------
/clashN/clashN/Handler/StatisticsHandler.cs:
--------------------------------------------------------------------------------
1 | using ClashN.Mode;
2 | using System.Net.WebSockets;
3 | using System.Text;
4 |
5 | namespace ClashN.Handler
6 | {
7 | internal class StatisticsHandler
8 | {
9 | private Config config_;
10 |
11 | //private ServerStatistics serverStatistics_;
12 | private bool exitFlag_;
13 |
14 | private ClientWebSocket webSocket = null;
15 | private string url = string.Empty;
16 |
17 | private Action updateFunc_;
18 |
19 | private bool Enable
20 | {
21 | get; set;
22 | }
23 |
24 | //private List Statistic
25 | //{
26 | // get
27 | // {
28 | // return serverStatistics_.profileStat;
29 | // }
30 | //}
31 |
32 | public StatisticsHandler(Config config, Action update)
33 | {
34 | config_ = config;
35 | Enable = config.EnableStatistics;
36 | updateFunc_ = update;
37 | exitFlag_ = false;
38 |
39 | //LoadFromFile();
40 |
41 | Task.Run(() => Run());
42 | }
43 |
44 | private async void Init()
45 | {
46 | Thread.Sleep(5000);
47 |
48 | try
49 | {
50 | url = $"ws://{Global.Loopback}:{config_.ApiPort}/traffic";
51 |
52 | if (webSocket == null)
53 | {
54 | webSocket = new ClientWebSocket();
55 | await webSocket.ConnectAsync(new Uri(url), CancellationToken.None);
56 | }
57 | }
58 | catch { }
59 | }
60 |
61 | public void Close()
62 | {
63 | try
64 | {
65 | exitFlag_ = true;
66 | if (webSocket != null)
67 | {
68 | webSocket.Abort();
69 | webSocket = null;
70 | }
71 | }
72 | catch (Exception ex)
73 | {
74 | Utils.SaveLog(ex.Message, ex);
75 | }
76 | }
77 |
78 | public async void Run()
79 | {
80 | Init();
81 |
82 | while (!exitFlag_)
83 | {
84 | try
85 | {
86 | if (Enable)
87 | {
88 | if (webSocket.State == WebSocketState.Aborted
89 | || webSocket.State == WebSocketState.Closed)
90 | {
91 | webSocket.Abort();
92 | webSocket = null;
93 | Init();
94 | }
95 |
96 | if (webSocket.State != WebSocketState.Open)
97 | {
98 | continue;
99 | }
100 |
101 | var buffer = new byte[1024];
102 | var res = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None);
103 | while (!res.CloseStatus.HasValue)
104 | {
105 | var result = Encoding.UTF8.GetString(buffer, 0, res.Count);
106 | if (!string.IsNullOrEmpty(result))
107 | {
108 | var serverStatItem = config_.GetProfileItem(config_.IndexId);
109 |
110 | ParseOutput(result, out ulong up, out ulong down);
111 | if (serverStatItem != null && (up + down) > 0)
112 | {
113 | serverStatItem.uploadRemote += up;
114 | serverStatItem.downloadRemote += down;
115 | }
116 | updateFunc_(up, down);
117 | }
118 | res = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None);
119 | }
120 | }
121 | }
122 | catch
123 | {
124 | }
125 | finally
126 | {
127 | Thread.Sleep(1000);
128 | }
129 | }
130 | }
131 |
132 | //public void LoadFromFile()
133 | //{
134 | // try
135 | // {
136 | // string result = Utils.LoadResource(Utils.GetConfigPath(Global.StatisticLogOverall));
137 | // if (!string.IsNullOrEmpty(result))
138 | // {
139 | // serverStatistics_ = Utils.FromJson(result);
140 | // }
141 |
142 | // if (serverStatistics_ == null)
143 | // {
144 | // serverStatistics_ = new ServerStatistics();
145 | // }
146 | // if (serverStatistics_.profileStat == null)
147 | // {
148 | // serverStatistics_.profileStat = new List();
149 | // }
150 |
151 | // long ticks = DateTime.Now.Date.Ticks;
152 | // foreach (ProfileStatItem item in serverStatistics_.profileStat)
153 | // {
154 | // if (item.dateNow != ticks)
155 | // {
156 | // item.todayUp = 0;
157 | // item.todayDown = 0;
158 | // item.dateNow = ticks;
159 | // }
160 | // }
161 | // }
162 | // catch (Exception ex)
163 | // {
164 | // Utils.SaveLog(ex.Message, ex);
165 | // }
166 | //}
167 |
168 | //public void SaveToFile()
169 | //{
170 | // try
171 | // {
172 | // Utils.ToJsonFile(serverStatistics_, Utils.GetConfigPath(Global.StatisticLogOverall));
173 | // }
174 | // catch (Exception ex)
175 | // {
176 | // Utils.SaveLog(ex.Message, ex);
177 | // }
178 | //}
179 |
180 | //public void ClearAllServerStatistics()
181 | //{
182 | // if (serverStatistics_ != null)
183 | // {
184 | // foreach (var item in serverStatistics_.profileStat)
185 | // {
186 | // item.todayUp = 0;
187 | // item.todayDown = 0;
188 | // item.totalUp = 0;
189 | // item.totalDown = 0;
190 | // // update ui display to zero
191 | // updateFunc_(0, 0);
192 | // }
193 |
194 | // // update statistic json file
195 | // //SaveToFile();
196 | // }
197 | //}
198 |
199 | //public List GetStatistic()
200 | //{
201 | // return Statistic;
202 | //}
203 |
204 | //private ProfileStatItem GetServerStatItem(string itemId)
205 | //{
206 | // long ticks = DateTime.Now.Date.Ticks;
207 | // int cur = Statistic.FindIndex(item => item.indexId == itemId);
208 | // if (cur < 0)
209 | // {
210 | // Statistic.Add(new ProfileStatItem
211 | // {
212 | // indexId = itemId,
213 | // totalUp = 0,
214 | // totalDown = 0,
215 | // todayUp = 0,
216 | // todayDown = 0,
217 | // dateNow = ticks
218 | // });
219 | // cur = Statistic.Count - 1;
220 | // }
221 | // if (Statistic[cur].dateNow != ticks)
222 | // {
223 | // Statistic[cur].todayUp = 0;
224 | // Statistic[cur].todayDown = 0;
225 | // Statistic[cur].dateNow = ticks;
226 | // }
227 | // return Statistic[cur];
228 | //}
229 |
230 | private void ParseOutput(string source, out ulong up, out ulong down)
231 | {
232 | up = 0; down = 0;
233 | try
234 | {
235 | var trafficItem = Utils.FromJson(source);
236 | if (trafficItem != null)
237 | {
238 | up = trafficItem.up;
239 | down = trafficItem.down;
240 | }
241 | }
242 | catch (Exception)
243 | {
244 | //Utils.SaveLog(ex.Message, ex);
245 | }
246 | }
247 | }
248 | }
--------------------------------------------------------------------------------
/clashN/clashN/Mode/ClashConnections.cs:
--------------------------------------------------------------------------------
1 | namespace ClashN.Mode
2 | {
3 | public class ClashConnections
4 | {
5 | public ulong DownloadTotal { get; set; }
6 | public ulong UploadTotal { get; set; }
7 | public List Connections { get; } = new List();
8 | }
9 |
10 | public class ConnectionItem
11 | {
12 | public string Id { get; set; } = string.Empty;
13 | public MetadataItem metadata { get; set; }
14 | public ulong upload { get; set; }
15 | public ulong download { get; set; }
16 | public DateTime start { get; set; }
17 | public List Chains { get; } = new List();
18 | public string rule { get; set; }
19 | public string rulePayload { get; set; }
20 | }
21 |
22 | public class MetadataItem
23 | {
24 | public string Network { get; set; }
25 | public string Type { get; set; }
26 | public string SourceIP { get; set; }
27 | public string DestinationIP { get; set; }
28 | public string SourcePort { get; set; }
29 | public string DestinationPort { get; set; }
30 | public string Host { get; set; }
31 | public string DnsMode { get; set; }
32 | public object Uid { get; set; }
33 | public string Process { get; set; }
34 | public string ProcessPath { get; set; }
35 | public string RemoteDestination { get; set; }
36 | }
37 | }
--------------------------------------------------------------------------------
/clashN/clashN/Mode/ClashProviders.cs:
--------------------------------------------------------------------------------
1 | using static ClashN.Mode.ClashProxies;
2 |
3 | namespace ClashN.Mode
4 | {
5 | public class ClashProviders
6 | {
7 | public Dictionary providers { get; set; }
8 |
9 | public class ProvidersItem
10 | {
11 | public string name { get; set; }
12 | public ProxiesItem[] proxies { get; set; }
13 | public string type { get; set; }
14 | public string vehicleType { get; set; }
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/clashN/clashN/Mode/ClashProxies.cs:
--------------------------------------------------------------------------------
1 | namespace ClashN.Mode
2 | {
3 | public class ClashProxies
4 | {
5 | public Dictionary proxies { get; set; }
6 |
7 | public class ProxiesItem
8 | {
9 | public string[] all { get; set; }
10 | public List history { get; set; }
11 | public string name { get; set; }
12 | public string type { get; set; }
13 | public bool udp { get; set; }
14 | public string now { get; set; }
15 | public int delay { get; set; }
16 | }
17 |
18 | public class HistoryItem
19 | {
20 | public string time { get; set; }
21 | public int delay { get; set; }
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/clashN/clashN/Mode/ComboItem.cs:
--------------------------------------------------------------------------------
1 | namespace ClashN.Mode
2 | {
3 | internal class ComboItem
4 | {
5 | public int ID { get; set; }
6 | public string Text { get; set; }
7 | }
8 | }
--------------------------------------------------------------------------------
/clashN/clashN/Mode/Config.cs:
--------------------------------------------------------------------------------
1 | using System.Drawing;
2 |
3 | namespace ClashN.Mode
4 | {
5 | ///
6 | /// 本软件配置文件实体类
7 | ///
8 | [Serializable]
9 | public class Config
10 | {
11 | #region property
12 |
13 | public int MixedPort { get; set; } = 7888;
14 |
15 | public int HttpPort { get; set; } = 7890;
16 |
17 | public int SocksPort { get; set; } = 7891;
18 |
19 | public int ApiPort { get; set; } = 9090;
20 |
21 | public string LogLevel { get; set; }
22 |
23 | public bool EnableIpv6 { get; set; }
24 |
25 | public string IndexId { get; set; }
26 |
27 | public SysProxyType SysProxyType { get; set; }
28 |
29 | public ERuleMode ruleMode { get; set; }
30 |
31 | public bool AllowLANConn { get; set; }
32 |
33 | public bool AutoRun { get; set; }
34 |
35 | public bool EnableStatistics { get; set; }
36 |
37 | public string SystemProxyExceptions { get; set; }
38 | public string SystemProxyAdvancedProtocol { get; set; }
39 |
40 | public int AutoUpdateSubInterval { get; set; } = 10;
41 | public int AutoDelayTestInterval { get; set; } = 10;
42 |
43 | public bool EnableSecurityProtocolTls13 { get; set; }
44 |
45 | public bool EnableMixinContent { get; set; }
46 |
47 | public int PacPort { get; set; }
48 |
49 | public bool AutoHideStartup { get; set; }
50 |
51 | public bool EnableTun { get; set; }
52 |
53 | #endregion property
54 |
55 | #region other entities
56 |
57 | public List ProfileItems { get; } = new List();
58 |
59 | public UIItem? UiItem { get; set; }
60 |
61 | public ConstItem? ConstItem { get; set; }
62 |
63 | public List globalHotkeys { get; } = new List();
64 |
65 | #endregion other entities
66 |
67 | #region function
68 |
69 | public int FindIndexId(string id)
70 | {
71 | if (string.IsNullOrEmpty(id))
72 | {
73 | return -1;
74 | }
75 | return ProfileItems.FindIndex(it => it.indexId == id);
76 | }
77 |
78 | public ProfileItem? GetProfileItem(string id)
79 | {
80 | if (string.IsNullOrEmpty(id))
81 | return null;
82 |
83 | return ProfileItems.FirstOrDefault(it => it.indexId == id);
84 | }
85 |
86 | public bool IsActiveNode(ProfileItem item)
87 | {
88 | if (!string.IsNullOrEmpty(item.indexId) && item.indexId == IndexId)
89 | {
90 | return true;
91 | }
92 |
93 | return false;
94 | }
95 |
96 | #endregion function
97 | }
98 |
99 | [Serializable]
100 | public class ProfileItem
101 | {
102 | public ProfileItem()
103 | {
104 | indexId = string.Empty;
105 | sort = 0;
106 | url = string.Empty;
107 | enabled = true;
108 | address = string.Empty;
109 | remarks = string.Empty;
110 | testResult = string.Empty;
111 | groupId = string.Empty;
112 | enableConvert = false;
113 | }
114 |
115 | #region function
116 |
117 | public string GetSummary()
118 | {
119 | string summary = string.Format("{0}", remarks);
120 | return summary;
121 | }
122 |
123 | #endregion function
124 |
125 | public string indexId { get; set; }
126 |
127 | public int sort { get; set; }
128 |
129 | public string address { get; set; }
130 |
131 | public string remarks { get; set; }
132 |
133 | public string testResult { get; set; }
134 |
135 | public string groupId { get; set; } = string.Empty;
136 | public CoreKind? coreType { get; set; }
137 |
138 | public string url { get; set; }
139 |
140 | public bool enabled { get; set; } = true;
141 |
142 | public string userAgent { get; set; } = string.Empty;
143 |
144 | public bool enableConvert { get; set; }
145 |
146 | public long updateTime { get; set; }
147 | public ulong uploadRemote { get; set; }
148 | public ulong downloadRemote { get; set; }
149 | public ulong totalRemote { get; set; }
150 | public long expireRemote { get; set; }
151 | }
152 |
153 | [Serializable]
154 | public class UIItem
155 | {
156 | public Point mainLocation { get; set; }
157 |
158 | public double mainWidth { get; set; }
159 | public double mainHeight { get; set; }
160 |
161 | public bool colorModeDark { get; set; }
162 | public string? colorPrimaryName { get; set; }
163 | public string currentFontFamily { get; set; } = string.Empty;
164 | public int currentFontSize { get; set; }
165 |
166 | public int proxiesSorting { get; set; }
167 | public bool proxiesAutoRefresh { get; set; }
168 |
169 | public int connectionsSorting { get; set; }
170 | public bool connectionsAutoRefresh { get; set; }
171 | }
172 |
173 | [Serializable]
174 | public class ConstItem
175 | {
176 | public string subConvertUrl { get; set; } = string.Empty;
177 | public string speedTestUrl { get; set; } = string.Empty;
178 | public string speedPingTestUrl { get; set; } = string.Empty;
179 | public string defIEProxyExceptions { get; set; } = string.Empty;
180 | }
181 | }
--------------------------------------------------------------------------------
/clashN/clashN/Mode/ConnectionModel.cs:
--------------------------------------------------------------------------------
1 | namespace ClashN.Mode
2 | {
3 | public class ConnectionModel
4 | {
5 | public string id { get; set; }
6 | public string network { get; set; }
7 | public string type { get; set; }
8 | public string host { get; set; }
9 | public ulong upload { get; set; }
10 | public ulong download { get; set; }
11 | public string uploadTraffic { get; set; }
12 | public string downloadTraffic { get; set; }
13 | public double time { get; set; }
14 | public string elapsed { get; set; }
15 | public string chain { get; set; }
16 | }
17 | }
--------------------------------------------------------------------------------
/clashN/clashN/Mode/CoreInfo.cs:
--------------------------------------------------------------------------------
1 | namespace ClashN.Mode
2 | {
3 | [Serializable]
4 | public class CoreInfo
5 | {
6 | public CoreKind coreType { get; set; }
7 |
8 | public List coreExes { get; set; }
9 |
10 | public string arguments { get; set; }
11 |
12 | public string coreUrl { get; set; }
13 |
14 | public string coreLatestUrl { get; set; }
15 |
16 | public string coreDownloadUrl32 { get; set; }
17 |
18 | public string coreDownloadUrl64 { get; set; }
19 |
20 | public string match { get; set; }
21 | }
22 | }
--------------------------------------------------------------------------------
/clashN/clashN/Mode/CoreKind.cs:
--------------------------------------------------------------------------------
1 | namespace ClashN.Mode
2 | {
3 | public enum CoreKind
4 | {
5 | Clash = 1,
6 | ClashMeta = 2,
7 | ClashPremium = 3,
8 | Mihomo = 4,
9 | ClashN = 99
10 | }
11 | }
--------------------------------------------------------------------------------
/clashN/clashN/Mode/EProfileColName.cs:
--------------------------------------------------------------------------------
1 | namespace ClashN.Mode
2 | {
3 | public enum EProfileColName
4 | {
5 | def = 0,
6 | remarks,
7 | url,
8 | address,
9 | enableUpdateSub,
10 | testResult,
11 | updateTime,
12 |
13 | todayDown,
14 | todayUp,
15 | totalDown,
16 | totalUp
17 | }
18 | }
--------------------------------------------------------------------------------
/clashN/clashN/Mode/ERuleMode.cs:
--------------------------------------------------------------------------------
1 | namespace ClashN.Mode
2 | {
3 | public enum ERuleMode
4 | {
5 | Rule = 0,
6 | Global = 1,
7 | Direct = 2,
8 | Unchanged = 3
9 | }
10 | }
--------------------------------------------------------------------------------
/clashN/clashN/Mode/ESpeedActionType.cs:
--------------------------------------------------------------------------------
1 | namespace ClashN.Mode
2 | {
3 | public enum ESpeedActionType
4 | {
5 | Ping,
6 | Tcping,
7 | Realping,
8 | Speedtest
9 | }
10 | }
--------------------------------------------------------------------------------
/clashN/clashN/Mode/GlobalHotkeyAction.cs:
--------------------------------------------------------------------------------
1 | namespace ClashN.Mode
2 | {
3 | public enum GlobalHotkeyAction
4 | {
5 | ShowForm = 0,
6 | SystemProxyClear = 1,
7 | SystemProxySet = 2,
8 | SystemProxyUnchanged = 3,
9 | SystemProxyPac = 4,
10 | }
11 | }
--------------------------------------------------------------------------------
/clashN/clashN/Mode/KeyShortcut.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Forms;
2 |
3 | namespace ClashN.Mode
4 | {
5 | [Serializable]
6 | public struct KeyShortcut
7 | {
8 | public GlobalHotkeyAction GlobalHotkey { get; set; }
9 |
10 | public bool Alt { get; set; }
11 |
12 | public bool Control { get; set; }
13 |
14 | public bool Shift { get; set; }
15 |
16 | public Keys? KeyCode { get; set; }
17 | }
18 | }
--------------------------------------------------------------------------------
/clashN/clashN/Mode/MovementTarget.cs:
--------------------------------------------------------------------------------
1 | namespace ClashN.Mode
2 | {
3 | public enum MovementTarget
4 | {
5 | Top = 1,
6 | Up = 2,
7 | Down = 3,
8 | Bottom = 4,
9 | Position = 5
10 | }
11 | }
--------------------------------------------------------------------------------
/clashN/clashN/Mode/ProfileItemModel.cs:
--------------------------------------------------------------------------------
1 | namespace ClashN.Mode
2 | {
3 | public class ProfileItemModel : ProfileItem
4 | {
5 | public bool IsActive { get; set; }
6 | public bool HasUrl => !string.IsNullOrEmpty(url);
7 | public bool HasAddress => !string.IsNullOrEmpty(address);
8 |
9 | public string StrUpdateTime
10 | {
11 | get
12 | {
13 | if (updateTime <= 0)
14 | {
15 | return String.Empty;
16 | }
17 | var dateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
18 | return dateTime.AddSeconds(updateTime).ToLocalTime().ToString("MM-dd HH:mm");
19 | }
20 | }
21 |
22 | public string TrafficUsed => Utils.HumanFy(uploadRemote + downloadRemote);
23 | public string TrafficTotal => totalRemote <= 0 ? "∞" : Utils.HumanFy(totalRemote);
24 |
25 | public string StrExpireTime
26 | {
27 | get
28 | {
29 | if (expireRemote <= 0)
30 | {
31 | return String.Empty;
32 | }
33 | var dateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
34 | return dateTime.AddSeconds(expireRemote).ToLocalTime().ToString("yyyy-MM-dd");
35 | }
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/clashN/clashN/Mode/ProxyModel.cs:
--------------------------------------------------------------------------------
1 | using ReactiveUI.Fody.Helpers;
2 |
3 | namespace ClashN.Mode
4 | {
5 | [Serializable]
6 | public class ProxyModel
7 | {
8 | [Reactive]
9 | public string name { get; set; }
10 |
11 | [Reactive]
12 | public string type { get; set; }
13 |
14 | [Reactive]
15 | public string now { get; set; }
16 |
17 | [Reactive]
18 | public int delay { get; set; }
19 |
20 | [Reactive]
21 | public string delayName { get; set; }
22 |
23 | [Reactive]
24 | public bool isActive { get; set; }
25 | }
26 | }
--------------------------------------------------------------------------------
/clashN/clashN/Mode/ServerStatistics.cs:
--------------------------------------------------------------------------------
1 | namespace ClashN.Mode
2 | {
3 | [Serializable]
4 | public class ServerStatistics
5 | {
6 | public List profileStat
7 | {
8 | get; set;
9 | }
10 | }
11 |
12 | [Serializable]
13 | public class ProfileStatItem
14 | {
15 | public string indexId
16 | {
17 | get; set;
18 | }
19 |
20 | public ulong totalUp
21 | {
22 | get; set;
23 | }
24 |
25 | public ulong totalDown
26 | {
27 | get; set;
28 | }
29 |
30 | public ulong todayUp
31 | {
32 | get; set;
33 | }
34 |
35 | public ulong todayDown
36 | {
37 | get; set;
38 | }
39 |
40 | public long dateNow
41 | {
42 | get; set;
43 | }
44 | }
45 |
46 | [Serializable]
47 | public class TrafficItem
48 | {
49 | public ulong up
50 | {
51 | get; set;
52 | }
53 |
54 | public ulong down
55 | {
56 | get; set;
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/clashN/clashN/Mode/ServerTestItem.cs:
--------------------------------------------------------------------------------
1 | namespace ClashN.Mode
2 | {
3 | [Serializable]
4 | internal class ServerTestItem
5 | {
6 | public string IndexId { get; set; }
7 | public string Address { get; set; }
8 | public int Port { get; set; }
9 | public bool AllowTest { get; set; }
10 | }
11 | }
--------------------------------------------------------------------------------
/clashN/clashN/Mode/SysProxyType.cs:
--------------------------------------------------------------------------------
1 | namespace ClashN.Mode
2 | {
3 | public enum SysProxyType
4 | {
5 | ForcedClear = 0,
6 | ForcedChange = 1,
7 | Unchanged = 2,
8 | Pac = 3
9 | }
10 | }
--------------------------------------------------------------------------------
/clashN/clashN/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | ..\resources\sysproxy.exe.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
123 |
124 |
125 | ..\resources\sysproxy64.exe.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
126 |
127 |
128 | ..\Resources\NotifyIcon1.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
129 |
130 |
131 | ..\Resources\NotifyIcon2.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
132 |
133 |
134 | ..\Resources\NotifyIcon3.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
135 |
136 |
--------------------------------------------------------------------------------
/clashN/clashN/Resources/NotifyIcon1.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2dust/clashN/0b498424d3bc1ef3a9fa1b9a6c05b4b216303984/clashN/clashN/Resources/NotifyIcon1.ico
--------------------------------------------------------------------------------
/clashN/clashN/Resources/NotifyIcon2.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2dust/clashN/0b498424d3bc1ef3a9fa1b9a6c05b4b216303984/clashN/clashN/Resources/NotifyIcon2.ico
--------------------------------------------------------------------------------
/clashN/clashN/Resources/NotifyIcon3.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2dust/clashN/0b498424d3bc1ef3a9fa1b9a6c05b4b216303984/clashN/clashN/Resources/NotifyIcon3.ico
--------------------------------------------------------------------------------
/clashN/clashN/Resources/sysproxy.exe.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2dust/clashN/0b498424d3bc1ef3a9fa1b9a6c05b4b216303984/clashN/clashN/Resources/sysproxy.exe.gz
--------------------------------------------------------------------------------
/clashN/clashN/Resources/sysproxy64.exe.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2dust/clashN/0b498424d3bc1ef3a9fa1b9a6c05b4b216303984/clashN/clashN/Resources/sysproxy64.exe.gz
--------------------------------------------------------------------------------
/clashN/clashN/Sample/SampleMixin.yaml:
--------------------------------------------------------------------------------
1 | #
2 | # 配置文件内容不会被修改,混合行为只会发生在内存中
3 | #
4 | # 注意下面缩进,请用支持yaml显示的编辑器打开
5 | #
6 | # 使用clash配置文件关键字则覆盖原配置
7 | #
8 | # removed-rules 循环匹配rules数组每行,符合则移除当前行 (此规则请放最前面)
9 | #
10 | # append-rules 数组合并至原配置rules数组后
11 | # prepend-rules 数组合并至原配置rules数组前
12 | # append-proxies 数组合并至原配置proxies数组后
13 | # prepend-proxies 数组合并至原配置proxies数组前
14 | # append-proxy-groups 数组合并至原配置proxy-groups数组后
15 | # prepend-proxy-groups 数组合并至原配置proxy-groups数组前
16 | # append-rule-providers 数组合并至原配置rule-providers数组后
17 | # prepend-rule-providers 数组合并至原配置rule-providers数组前
18 | #
19 |
20 | dns:
21 | enable: true
22 | enhanced-mode: fake-ip
23 | nameserver:
24 | - 114.114.114.114
25 | - 223.5.5.5
26 | - 8.8.8.8
27 | fallback: []
28 | fake-ip-filter:
29 | - +.stun.*.*
30 | - +.stun.*.*.*
31 | - +.stun.*.*.*.*
32 | - +.stun.*.*.*.*.*
33 | - "*.n.n.srv.nintendo.net"
34 | - +.stun.playstation.net
35 | - xbox.*.*.microsoft.com
36 | - "*.*.xboxlive.com"
37 | - "*.msftncsi.com"
38 | - "*.msftconnecttest.com"
39 | - WORKGROUP
--------------------------------------------------------------------------------
/clashN/clashN/Sample/SampleTun.yaml:
--------------------------------------------------------------------------------
1 | tun:
2 | enable: true
3 | stack: gvisor
4 | dns-hijack:
5 | - 0.0.0.0:53
6 | auto-route: true
7 | auto-detect-interface: true
8 |
--------------------------------------------------------------------------------
/clashN/clashN/Tool/FileManager.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.IO.Compression;
3 | using System.Text;
4 |
5 | namespace ClashN.Tool
6 | {
7 | public static class FileManager
8 | {
9 | public static bool ByteArrayToFile(string fileName, byte[] content)
10 | {
11 | try
12 | {
13 | using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
14 | fs.Write(content, 0, content.Length);
15 | return true;
16 | }
17 | catch (Exception ex)
18 | {
19 | Utils.SaveLog(ex.Message, ex);
20 | }
21 | return false;
22 | }
23 |
24 | public static void UncompressFile(string fileName, byte[] content)
25 | {
26 | try
27 | {
28 | // Because the uncompressed size of the file is unknown,
29 | // we are using an arbitrary buffer size.
30 | byte[] buffer = new byte[4096];
31 | int n;
32 |
33 | using (FileStream fs = File.Create(fileName))
34 | using (GZipStream input = new GZipStream(new MemoryStream(content),
35 | CompressionMode.Decompress, false))
36 | {
37 | while ((n = input.Read(buffer, 0, buffer.Length)) > 0)
38 | {
39 | fs.Write(buffer, 0, n);
40 | }
41 | }
42 | }
43 | catch (Exception ex)
44 | {
45 | Utils.SaveLog(ex.Message, ex);
46 | }
47 | }
48 |
49 | public static string NonExclusiveReadAllText(string path)
50 | {
51 | return NonExclusiveReadAllText(path, Encoding.Default);
52 | }
53 |
54 | public static string NonExclusiveReadAllText(string path, Encoding encoding)
55 | {
56 | try
57 | {
58 | using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
59 | using (StreamReader sr = new StreamReader(fs, encoding))
60 | {
61 | return sr.ReadToEnd();
62 | }
63 | }
64 | catch (Exception ex)
65 | {
66 | Utils.SaveLog(ex.Message, ex);
67 | throw ex;
68 | }
69 | }
70 |
71 | public static bool ZipExtractToFile(string fileName, string toPath, string ignoredName)
72 | {
73 | try
74 | {
75 | using (ZipArchive archive = ZipFile.OpenRead(fileName))
76 | {
77 | foreach (ZipArchiveEntry entry in archive.Entries)
78 | {
79 | if (entry.Length == 0)
80 | {
81 | continue;
82 | }
83 | try
84 | {
85 | if (!string.IsNullOrEmpty(ignoredName) && entry.Name.Contains(ignoredName))
86 | {
87 | continue;
88 | }
89 | entry.ExtractToFile(Path.Combine(toPath, entry.Name), true);
90 | }
91 | catch (IOException ex)
92 | {
93 | Utils.SaveLog(ex.Message, ex);
94 | }
95 | }
96 | }
97 | }
98 | catch (Exception ex)
99 | {
100 | Utils.SaveLog(ex.Message, ex);
101 | return false;
102 | }
103 | return true;
104 | }
105 |
106 | public static bool ZipExtractToFullFile(string fileName)
107 | {
108 | try
109 | {
110 | using (ZipArchive archive = ZipFile.OpenRead(fileName))
111 | {
112 | foreach (ZipArchiveEntry entry in archive.Entries)
113 | {
114 | if (entry.Length == 0)
115 | continue;
116 |
117 | string entryOuputPath = Utils.GetPath(entry.FullName);
118 | FileInfo fileInfo = new FileInfo(entryOuputPath);
119 | fileInfo.Directory.Create();
120 | entry.ExtractToFile(entryOuputPath, true);
121 | }
122 | }
123 | }
124 | catch (Exception ex)
125 | {
126 | Utils.SaveLog(ex.Message, ex);
127 | return false;
128 | }
129 | return true;
130 | }
131 | }
132 | }
--------------------------------------------------------------------------------
/clashN/clashN/Tool/Job.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace ClashN
5 | {
6 | /*
7 | * See:
8 | * http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net
9 | */
10 |
11 | public class Job : IDisposable
12 | {
13 | private IntPtr handle = IntPtr.Zero;
14 |
15 | public Job()
16 | {
17 | handle = CreateJobObject(IntPtr.Zero, null);
18 | IntPtr extendedInfoPtr = IntPtr.Zero;
19 | JOBOBJECT_BASIC_LIMIT_INFORMATION info = new JOBOBJECT_BASIC_LIMIT_INFORMATION
20 | {
21 | LimitFlags = 0x2000
22 | };
23 |
24 | JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION
25 | {
26 | BasicLimitInformation = info
27 | };
28 |
29 | try
30 | {
31 | int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
32 | extendedInfoPtr = Marshal.AllocHGlobal(length);
33 | Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);
34 |
35 | if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr,
36 | (uint)length))
37 | throw new Exception(string.Format("Unable to set information. Error: {0}",
38 | Marshal.GetLastWin32Error()));
39 | }
40 | finally
41 | {
42 | if (extendedInfoPtr != IntPtr.Zero)
43 | {
44 | Marshal.FreeHGlobal(extendedInfoPtr);
45 | }
46 | }
47 | }
48 |
49 | public bool AddProcess(IntPtr processHandle)
50 | {
51 | bool succ = AssignProcessToJobObject(handle, processHandle);
52 |
53 | if (!succ)
54 | {
55 | Utils.SaveLog("Failed to call AssignProcessToJobObject! GetLastError=" + Marshal.GetLastWin32Error());
56 | }
57 |
58 | return succ;
59 | }
60 |
61 | public bool AddProcess(int processId)
62 | {
63 | return AddProcess(Process.GetProcessById(processId).Handle);
64 | }
65 |
66 | #region IDisposable
67 |
68 | private bool disposed;
69 |
70 | public void Dispose()
71 | {
72 | Dispose(true);
73 | GC.SuppressFinalize(this);
74 | }
75 |
76 | protected virtual void Dispose(bool disposing)
77 | {
78 | if (disposed) return;
79 | disposed = true;
80 |
81 | if (disposing)
82 | {
83 | // no managed objects to free
84 | }
85 |
86 | if (handle != IntPtr.Zero)
87 | {
88 | CloseHandle(handle);
89 | handle = IntPtr.Zero;
90 | }
91 | }
92 |
93 | ~Job()
94 | {
95 | Dispose(false);
96 | }
97 |
98 | #endregion IDisposable
99 |
100 | #region Interop
101 |
102 | [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
103 | private static extern IntPtr CreateJobObject(IntPtr a, string lpName);
104 |
105 | [DllImport("kernel32.dll", SetLastError = true)]
106 | private static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, UInt32 cbJobObjectInfoLength);
107 |
108 | [DllImport("kernel32.dll", SetLastError = true)]
109 | private static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);
110 |
111 | [DllImport("kernel32.dll", SetLastError = true)]
112 | [return: MarshalAs(UnmanagedType.Bool)]
113 | private static extern bool CloseHandle(IntPtr hObject);
114 |
115 | #endregion Interop
116 | }
117 |
118 | #region Helper classes
119 |
120 | [StructLayout(LayoutKind.Sequential)]
121 | internal struct IO_COUNTERS
122 | {
123 | public UInt64 ReadOperationCount;
124 | public UInt64 WriteOperationCount;
125 | public UInt64 OtherOperationCount;
126 | public UInt64 ReadTransferCount;
127 | public UInt64 WriteTransferCount;
128 | public UInt64 OtherTransferCount;
129 | }
130 |
131 | [StructLayout(LayoutKind.Sequential)]
132 | internal struct JOBOBJECT_BASIC_LIMIT_INFORMATION
133 | {
134 | public Int64 PerProcessUserTimeLimit;
135 | public Int64 PerJobUserTimeLimit;
136 | public UInt32 LimitFlags;
137 | public UIntPtr MinimumWorkingSetSize;
138 | public UIntPtr MaximumWorkingSetSize;
139 | public UInt32 ActiveProcessLimit;
140 | public UIntPtr Affinity;
141 | public UInt32 PriorityClass;
142 | public UInt32 SchedulingClass;
143 | }
144 |
145 | [StructLayout(LayoutKind.Sequential)]
146 | public struct SECURITY_ATTRIBUTES
147 | {
148 | public UInt32 nLength;
149 | public IntPtr lpSecurityDescriptor;
150 | public Int32 bInheritHandle;
151 | }
152 |
153 | [StructLayout(LayoutKind.Sequential)]
154 | internal struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
155 | {
156 | public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
157 | public IO_COUNTERS IoInfo;
158 | public UIntPtr ProcessMemoryLimit;
159 | public UIntPtr JobMemoryLimit;
160 | public UIntPtr PeakProcessMemoryUsed;
161 | public UIntPtr PeakJobMemoryUsed;
162 | }
163 |
164 | public enum JobObjectInfoType
165 | {
166 | AssociateCompletionPortInformation = 7,
167 | BasicLimitInformation = 2,
168 | BasicUIRestrictions = 4,
169 | EndOfJobTimeInformation = 6,
170 | ExtendedLimitInformation = 9,
171 | SecurityLimitInformation = 5,
172 | GroupInformation = 11
173 | }
174 |
175 | #endregion Helper classes
176 | }
--------------------------------------------------------------------------------
/clashN/clashN/Tool/Logging.cs:
--------------------------------------------------------------------------------
1 | using NLog;
2 | using NLog.Config;
3 | using NLog.Targets;
4 | using System.IO;
5 |
6 | namespace ClashN.Tool
7 | {
8 | public class Logging
9 | {
10 | public static void Setup()
11 | {
12 | LoggingConfiguration config = new LoggingConfiguration();
13 | FileTarget fileTarget = new FileTarget();
14 | config.AddTarget("file", fileTarget);
15 | fileTarget.Layout = "${longdate}-${level:uppercase=true} ${message}";
16 | fileTarget.FileName = Utils.GetPath(@"guiLogs/") + "${shortdate}.txt";
17 | config.LoggingRules.Add(new LoggingRule("*", LogLevel.Debug, fileTarget));
18 | LogManager.Configuration = config;
19 | }
20 |
21 | public static void ClearLogs()
22 | {
23 | Task.Run(() =>
24 | {
25 | try
26 | {
27 | var now = DateTime.Now.AddMonths(-1);
28 | var dir = Utils.GetPath(@"guiLogs\");
29 | var files = Directory.GetFiles(dir, "*.txt");
30 | foreach (var filePath in files)
31 | {
32 | var file = new FileInfo(filePath);
33 | if (file.CreationTime < now)
34 | {
35 | try
36 | {
37 | file.Delete();
38 | }
39 | catch { }
40 | }
41 | }
42 | }
43 | catch { }
44 | });
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/clashN/clashN/Tool/QueryableExtension.cs:
--------------------------------------------------------------------------------
1 | using System.Linq.Expressions;
2 | using System.Reflection;
3 |
4 | namespace ClashN.Tool
5 | {
6 | public static class QueryableExtension
7 | {
8 | public static IOrderedQueryable OrderBy(this IQueryable query, string propertyName)
9 | {
10 | return OrderBy(query, propertyName, false);
11 | }
12 |
13 | public static IOrderedQueryable OrderByDescending(this IQueryable query, string propertyName)
14 | {
15 | return OrderBy(query, propertyName, true);
16 | }
17 |
18 | private static IOrderedQueryable OrderBy(IQueryable query, string propertyName, bool isDesc)
19 | {
20 | string methodname = (isDesc) ? "OrderByDescendingInternal" : "OrderByInternal";
21 |
22 | var memberProp = typeof(T).GetProperty(propertyName);
23 |
24 | var method = typeof(QueryableExtension).GetMethod(methodname)
25 | .MakeGenericMethod(typeof(T), memberProp.PropertyType);
26 |
27 | return (IOrderedQueryable)method.Invoke(null, new object[] { query, memberProp });
28 | }
29 |
30 | public static IOrderedQueryable OrderByInternal(IQueryable query, PropertyInfo memberProperty)
31 | {
32 | return query.OrderBy(GetLambda(memberProperty));
33 | }
34 |
35 | public static IOrderedQueryable OrderByDescendingInternal(IQueryable query, PropertyInfo memberProperty)
36 | {
37 | return query.OrderByDescending(GetLambda(memberProperty));
38 | }
39 |
40 | private static Expression> GetLambda(PropertyInfo memberProperty)
41 | {
42 | if (memberProperty.PropertyType != typeof(TProp))
43 | throw new Exception();
44 |
45 | var thisArg = Expression.Parameter(typeof(T));
46 | var lamba = Expression.Lambda>(Expression.Property(thisArg, memberProperty), thisArg);
47 |
48 | return lamba;
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/clashN/clashN/Tool/UI.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Forms;
2 |
3 | namespace ClashN
4 | {
5 | internal class UI
6 | {
7 | public static void Show(string msg)
8 | {
9 | MessageBox.Show(msg, "ClashN", MessageBoxButtons.OK, MessageBoxIcon.Information);
10 | }
11 |
12 | public static void ShowWarning(string msg)
13 | {
14 | MessageBox.Show(msg, "ClashN", MessageBoxButtons.OK, MessageBoxIcon.Warning);
15 | }
16 |
17 | public static void ShowError(string msg)
18 | {
19 | MessageBox.Show(msg, "ClashN", MessageBoxButtons.OK, MessageBoxIcon.Error);
20 | }
21 |
22 | public static DialogResult ShowYesNo(string msg)
23 | {
24 | return MessageBox.Show(msg, "ClashN", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
25 | }
26 |
27 | //public static string GetResourseString(string key)
28 | //{
29 | // CultureInfo cultureInfo = null;
30 | // try
31 | // {
32 | // string languageCode = this.LanguageCode;
33 | // cultureInfo = new CultureInfo(languageCode);
34 | // return Common.ResourceManager.GetString(key, cultureInfo);
35 | // }
36 | // catch (Exception)
37 | // {
38 | // //默认读取英文的多语言
39 | // cultureInfo = new CultureInfo(MKey.kDefaultLanguageCode);
40 | // return Common.ResourceManager.GetString(key, cultureInfo);
41 | // }
42 | //}
43 | }
44 | }
--------------------------------------------------------------------------------
/clashN/clashN/ViewModels/ConnectionsViewModel.cs:
--------------------------------------------------------------------------------
1 | using ClashN.Handler;
2 | using ClashN.Mode;
3 | using DynamicData;
4 | using DynamicData.Binding;
5 | using ReactiveUI;
6 | using ReactiveUI.Fody.Helpers;
7 | using Splat;
8 | using System.Reactive;
9 | using System.Reactive.Linq;
10 | using System.Windows;
11 |
12 | namespace ClashN.ViewModels
13 | {
14 | public class ConnectionsViewModel : ReactiveObject
15 | {
16 | private static Config _config;
17 |
18 | static ConnectionsViewModel()
19 | {
20 | _config = LazyConfig.Instance.Config;
21 | }
22 |
23 | private NoticeHandler? _noticeHandler;
24 | private IObservableCollection _connectionItems = new ObservableCollectionExtended();
25 |
26 | public IObservableCollection ConnectionItems => _connectionItems;
27 |
28 | [Reactive]
29 | public ConnectionModel SelectedSource { get; set; }
30 |
31 | public ReactiveCommand ConnectionCloseCmd { get; }
32 | public ReactiveCommand ConnectionCloseAllCmd { get; }
33 |
34 | [Reactive]
35 | public int SortingSelected { get; set; }
36 |
37 | [Reactive]
38 | public bool AutoRefresh { get; set; }
39 |
40 | private int AutoRefreshInterval;
41 |
42 | public ConnectionsViewModel()
43 | {
44 | _noticeHandler = Locator.Current.GetService();
45 |
46 | AutoRefreshInterval = 10;
47 | SortingSelected = _config.UiItem.connectionsSorting;
48 | AutoRefresh = _config.UiItem.connectionsAutoRefresh;
49 |
50 | var canEditRemove = this.WhenAnyValue(
51 | x => x.SelectedSource,
52 | selectedSource => selectedSource != null && !string.IsNullOrEmpty(selectedSource.id));
53 |
54 | this.WhenAnyValue(
55 | x => x.SortingSelected,
56 | y => y >= 0)
57 | .Subscribe(c => DoSortingSelected(c));
58 |
59 | this.WhenAnyValue(
60 | x => x.AutoRefresh,
61 | y => y == true)
62 | .Subscribe(c => { _config.UiItem.connectionsAutoRefresh = AutoRefresh; });
63 |
64 | ConnectionCloseCmd = ReactiveCommand.Create(() =>
65 | {
66 | ClashConnectionClose(false);
67 | }, canEditRemove);
68 |
69 | ConnectionCloseAllCmd = ReactiveCommand.Create(() =>
70 | {
71 | ClashConnectionClose(true);
72 | });
73 |
74 | Init();
75 | }
76 |
77 | private void DoSortingSelected(bool c)
78 | {
79 | if (!c)
80 | {
81 | return;
82 | }
83 | if (SortingSelected != _config.UiItem.connectionsSorting)
84 | {
85 | _config.UiItem.connectionsSorting = SortingSelected;
86 | }
87 |
88 | GetClashConnections();
89 | }
90 |
91 | private void Init()
92 | {
93 | Observable.Interval(TimeSpan.FromSeconds(AutoRefreshInterval))
94 | .Subscribe(x =>
95 | {
96 | if (!(AutoRefresh && Global.ShowInTaskbar))
97 | {
98 | return;
99 | }
100 | GetClashConnections();
101 | });
102 |
103 | //Task.Run(() =>
104 | //{
105 | // while (true)
106 | // {
107 | // if (AutoRefresh)
108 | // {
109 | // GetClashConnections();
110 | // }
111 | // Thread.Sleep(1000 * AutoRefreshInterval);
112 | // }
113 | //});
114 | }
115 |
116 | private void GetClashConnections()
117 | {
118 | MainFormHandler.Instance.GetClashConnections(_config, (it) =>
119 | {
120 | //_noticeHandler?.SendMessage("Refresh Clash Connections", true);
121 | if (it == null)
122 | {
123 | return;
124 | }
125 |
126 | Application.Current?.Dispatcher.Invoke((Action)(() =>
127 | {
128 | RefreshConnections(it?.Connections!);
129 | }));
130 | });
131 | }
132 |
133 | private void RefreshConnections(List connections)
134 | {
135 | _connectionItems.Clear();
136 |
137 | var dtNow = DateTime.Now;
138 | var lstModel = new List();
139 | foreach (var item in connections)
140 | {
141 | ConnectionModel model = new();
142 |
143 | model.id = item.Id;
144 | model.network = item.metadata.Network;
145 | model.type = item.metadata.Type;
146 | model.host = $"{(string.IsNullOrEmpty(item.metadata.Host) ? item.metadata.DestinationIP : item.metadata.Host)}:{item.metadata.DestinationPort}";
147 | var sp = (dtNow - item.start);
148 | model.time = sp.TotalSeconds < 0 ? 1 : sp.TotalSeconds;
149 | model.upload = item.upload;
150 | model.download = item.download;
151 | model.uploadTraffic = $"{Utils.HumanFy(item.upload)}";
152 | model.downloadTraffic = $"{Utils.HumanFy(item.download)}";
153 | model.elapsed = sp.ToString(@"hh\:mm\:ss");
154 | model.chain = item.Chains.Count > 0 ? item.Chains[0] : String.Empty;
155 |
156 | lstModel.Add(model);
157 | }
158 | if (lstModel.Count <= 0) { return; }
159 |
160 | //sort
161 | switch (SortingSelected)
162 | {
163 | case 0:
164 | lstModel = lstModel.OrderBy(t => t.upload / t.time).ToList();
165 | break;
166 |
167 | case 1:
168 | lstModel = lstModel.OrderBy(t => t.download / t.time).ToList();
169 | break;
170 |
171 | case 2:
172 | lstModel = lstModel.OrderBy(t => t.upload).ToList();
173 | break;
174 |
175 | case 3:
176 | lstModel = lstModel.OrderBy(t => t.download).ToList();
177 | break;
178 |
179 | case 4:
180 | lstModel = lstModel.OrderBy(t => t.time).ToList();
181 | break;
182 |
183 | case 5:
184 | lstModel = lstModel.OrderBy(t => t.host).ToList();
185 | break;
186 | }
187 |
188 | _connectionItems.AddRange(lstModel);
189 | }
190 |
191 | public void ClashConnectionClose(bool all)
192 | {
193 | var id = string.Empty;
194 | if (!all)
195 | {
196 | var item = SelectedSource;
197 | if (item is null)
198 | {
199 | return;
200 | }
201 | id = item.id;
202 | }
203 | else
204 | {
205 | _connectionItems.Clear();
206 | }
207 | MainFormHandler.Instance.ClashConnectionClose(id);
208 | GetClashConnections();
209 | }
210 | }
211 | }
--------------------------------------------------------------------------------
/clashN/clashN/ViewModels/DashboardViewModel.cs:
--------------------------------------------------------------------------------
1 | using ReactiveUI;
2 |
3 | namespace ClashN.ViewModels
4 | {
5 | public class DashboardViewModel : ReactiveObject
6 | {
7 | public DashboardViewModel()
8 | {
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/clashN/clashN/ViewModels/HelpViewModel.cs:
--------------------------------------------------------------------------------
1 | using ClashN.Handler;
2 | using ClashN.Mode;
3 | using ClashN.Resx;
4 | using ClashN.Tool;
5 | using ReactiveUI;
6 | using Splat;
7 | using System.Reactive;
8 |
9 | namespace ClashN.ViewModels
10 | {
11 | public class HelpViewModel : ReactiveObject
12 | {
13 | private static Config _config;
14 | private NoticeHandler? _noticeHandler;
15 |
16 | public ReactiveCommand CheckUpdateCmd { get; }
17 |
18 | //public ReactiveCommand CheckUpdateClashCoreCmd { get; }
19 | public ReactiveCommand CheckUpdateMihomoCoreCmd { get; }
20 |
21 | //public ReactiveCommand CheckUpdateGeoDataCmd { get; }
22 |
23 | public HelpViewModel()
24 | {
25 | _config = LazyConfig.Instance.Config;
26 | _noticeHandler = Locator.Current.GetService();
27 |
28 | CheckUpdateCmd = ReactiveCommand.Create(() =>
29 | {
30 | CheckUpdateN();
31 | });
32 | //CheckUpdateClashCoreCmd = ReactiveCommand.Create(() =>
33 | //{
34 | // CheckUpdateCore(CoreKind.Clash);
35 | //});
36 | CheckUpdateMihomoCoreCmd = ReactiveCommand.Create(() =>
37 | {
38 | CheckUpdateCore(CoreKind.Mihomo);
39 | });
40 | //CheckUpdateGeoDataCmd = ReactiveCommand.Create(() =>
41 | //{
42 | // CheckUpdateGeoData();
43 | //});
44 | }
45 |
46 | //private void CheckUpdateGeoData()
47 | //{
48 | // void _updateUI(bool success, string msg)
49 | // {
50 | // _noticeHandler?.SendMessage(msg);
51 | // if (success)
52 | // {
53 | // Locator.Current.GetService()?.MyAppExit(false);
54 | // }
55 | // };
56 | // UpdateHandle update = new UpdateHandle();
57 | // update.UpdateGeoFile(GeoKind.GEO_IP, _config, _updateUI);
58 | // update.UpdateGeoFile(GeoKind.GEO_SITE, _config, _updateUI);
59 | //}
60 |
61 | private void CheckUpdateN()
62 | {
63 | void _updateUI(bool success, string msg)
64 | {
65 | _noticeHandler?.SendMessage(msg);
66 | if (success)
67 | {
68 | Locator.Current.GetService()?.MyAppExit(false);
69 | }
70 | };
71 | (new UpdateHandle()).CheckUpdateGuiN(_config, _updateUI);
72 | }
73 |
74 | private void CheckUpdateCore(CoreKind type)
75 | {
76 | void _updateUI(bool success, string msg)
77 | {
78 | _noticeHandler?.SendMessage(msg);
79 | if (success)
80 | {
81 | Locator.Current.GetService()?.CloseCore();
82 |
83 | string fileName = Utils.GetTempPath(Utils.GetDownloadFileName(msg));
84 | string toPath = Utils.GetBinPath("", type);
85 | if (FileManager.ZipExtractToFile(fileName, toPath, "") == false)
86 | {
87 | Global.reloadCore = true;
88 | _ = Locator.Current.GetService()?.LoadCore();
89 | _noticeHandler?.Enqueue(ResUI.MsgUpdateCoreCoreFailed);
90 | }
91 | else
92 | {
93 | _noticeHandler?.Enqueue(ResUI.MsgUpdateCoreCoreSuccessfullyMore);
94 |
95 | Global.reloadCore = true;
96 | _ = Locator.Current.GetService()?.LoadCore();
97 | _noticeHandler?.Enqueue(ResUI.MsgUpdateCoreCoreSuccessfully);
98 | }
99 | }
100 | };
101 | (new UpdateHandle()).CheckUpdateCore(type, _config, _updateUI);
102 | }
103 | }
104 | }
--------------------------------------------------------------------------------
/clashN/clashN/ViewModels/LogsViewModel.cs:
--------------------------------------------------------------------------------
1 | using ReactiveUI;
2 | using ReactiveUI.Fody.Helpers;
3 |
4 | namespace ClashN.ViewModels
5 | {
6 | public class LogsViewModel : ReactiveObject
7 | {
8 | [Reactive]
9 | public int SortingSelected { get; set; }
10 |
11 | [Reactive]
12 | public bool AutoRefresh { get; set; }
13 |
14 | [Reactive]
15 | public string MsgFilter { get; set; }
16 |
17 | [Reactive]
18 | public int LineCount { get; set; }
19 |
20 | public LogsViewModel()
21 | {
22 | AutoRefresh = true;
23 | MsgFilter = string.Empty;
24 | LineCount = 1000;
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/clashN/clashN/ViewModels/ProfileEditViewModel.cs:
--------------------------------------------------------------------------------
1 | using ClashN.Handler;
2 | using ClashN.Mode;
3 | using ClashN.Resx;
4 | using ClashN.Views;
5 | using ReactiveUI;
6 | using ReactiveUI.Fody.Helpers;
7 | using ReactiveUI.Validation.Helpers;
8 | using Splat;
9 | using System.IO;
10 | using System.Reactive;
11 | using System.Windows.Forms;
12 |
13 | namespace ClashN.ViewModels
14 | {
15 | public class ProfileEditViewModel : ReactiveValidationObject
16 | {
17 | private static Config _config;
18 | private NoticeHandler? _noticeHandler;
19 | private PorfileEditWindow _view;
20 |
21 | [Reactive]
22 | public ProfileItem SelectedSource { get; set; }
23 |
24 | [Reactive]
25 | public string CoreType { get; set; }
26 |
27 | public ReactiveCommand BrowseProfileCmd { get; }
28 | public ReactiveCommand EditProfileCmd { get; }
29 | public ReactiveCommand SaveProfileCmd { get; }
30 |
31 | public ProfileEditViewModel(ProfileItem profileItem, PorfileEditWindow view)
32 | {
33 | _noticeHandler = Locator.Current.GetService();
34 | _config = LazyConfig.Instance.Config;
35 |
36 | if (string.IsNullOrEmpty(profileItem.indexId))
37 | {
38 | SelectedSource = profileItem;
39 | }
40 | else
41 | {
42 | SelectedSource = Utils.DeepCopy(profileItem);
43 | }
44 |
45 | _view = view;
46 | CoreType = (SelectedSource.coreType ?? CoreKind.Clash).ToString();
47 |
48 | BrowseProfileCmd = ReactiveCommand.Create(() =>
49 | {
50 | BrowseProfile();
51 | });
52 |
53 | EditProfileCmd = ReactiveCommand.Create(() =>
54 | {
55 | EditProfile();
56 | });
57 |
58 | SaveProfileCmd = ReactiveCommand.Create(() =>
59 | {
60 | SaveProfile();
61 | });
62 |
63 | Utils.SetDarkBorder(view, _config.UiItem.colorModeDark);
64 | }
65 |
66 | private void SaveProfile()
67 | {
68 | string remarks = SelectedSource.remarks;
69 | if (string.IsNullOrEmpty(remarks))
70 | {
71 | _noticeHandler?.Enqueue(ResUI.PleaseFillRemarks);
72 | return;
73 | }
74 |
75 | if (string.IsNullOrEmpty(CoreType))
76 | {
77 | SelectedSource.coreType = null;
78 | }
79 | else
80 | {
81 | SelectedSource.coreType = (CoreKind)Enum.Parse(typeof(CoreKind), CoreType);
82 | }
83 |
84 | var item = _config.GetProfileItem(SelectedSource.indexId);
85 | if (item is null)
86 | {
87 | item = SelectedSource;
88 | }
89 | else
90 | {
91 | item.remarks = SelectedSource.remarks;
92 | item.url = SelectedSource.url;
93 | item.address = SelectedSource.address;
94 | item.userAgent = SelectedSource.userAgent;
95 | item.coreType = SelectedSource.coreType;
96 | item.enabled = SelectedSource.enabled;
97 | item.enableConvert = SelectedSource.enableConvert;
98 | }
99 |
100 | if (ConfigProc.EditProfile(ref _config, item) == 0)
101 | {
102 | Locator.Current.GetService()?.RefreshProfiles();
103 | _noticeHandler?.Enqueue(ResUI.OperationSuccess);
104 | _view?.Close();
105 | }
106 | else
107 | {
108 | _noticeHandler?.Enqueue(ResUI.OperationFailed);
109 | }
110 | }
111 |
112 | private void BrowseProfile()
113 | {
114 | OpenFileDialog fileDialog = new OpenFileDialog
115 | {
116 | Multiselect = false,
117 | Filter = "YAML|*.yaml;*.yml|All|*.*"
118 | };
119 |
120 | IWin32Window parent = App.Current.MainWindow.WpfWindow2WinFormWin32Window();
121 | if (fileDialog.ShowDialog(parent) != DialogResult.OK)
122 | {
123 | return;
124 | }
125 | if (UI.ShowYesNo(ResUI.MsgSureContinue) == DialogResult.No)
126 | {
127 | return;
128 | }
129 | string fileName = fileDialog.FileName;
130 | if (string.IsNullOrEmpty(fileName))
131 | {
132 | return;
133 | }
134 | var item = _config.GetProfileItem(SelectedSource.indexId);
135 | if (item is null)
136 | {
137 | item = SelectedSource;
138 | }
139 | if (ConfigProc.AddProfileViaPath(ref _config, item, fileName) == 0)
140 | {
141 | _noticeHandler?.Enqueue(ResUI.SuccessfullyImportedCustomProfile);
142 | Locator.Current.GetService()?.RefreshProfiles();
143 | _view?.Close();
144 | }
145 | else
146 | {
147 | _noticeHandler?.Enqueue(ResUI.FailedImportedCustomProfile);
148 | }
149 | }
150 |
151 | private void EditProfile()
152 | {
153 | var address = SelectedSource.address;
154 | if (string.IsNullOrEmpty(address))
155 | {
156 | _noticeHandler?.Enqueue(ResUI.FillProfileAddressCustom);
157 | return;
158 | }
159 |
160 | address = Path.Combine(Utils.GetConfigPath(), address);
161 | if (File.Exists(address))
162 | {
163 | Utils.ProcessStart(address);
164 | }
165 | else
166 | {
167 | _noticeHandler?.Enqueue(ResUI.FailedReadConfiguration);
168 | }
169 | }
170 | }
171 | }
--------------------------------------------------------------------------------
/clashN/clashN/ViewModels/PromotionViewModel.cs:
--------------------------------------------------------------------------------
1 | using ReactiveUI;
2 |
3 | namespace ClashN.ViewModels
4 | {
5 | public class PromotionViewModel : ReactiveObject
6 | {
7 | public PromotionViewModel()
8 | {
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/clashN/clashN/Views/ConnectionsView.xaml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
22 |
23 |
28 |
29 |
30 |
31 |
37 |
41 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
63 |
64 |
68 |
72 |
73 |
74 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
97 |
101 |
105 |
109 |
113 |
117 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/clashN/clashN/Views/ConnectionsView.xaml.cs:
--------------------------------------------------------------------------------
1 | using ClashN.ViewModels;
2 | using ReactiveUI;
3 | using System.Reactive.Disposables;
4 |
5 | namespace ClashN.Views
6 | {
7 | ///
8 | /// Interaction logic for ConnectionsView.xaml
9 | ///
10 | public partial class ConnectionsView
11 | {
12 | public ConnectionsView()
13 | {
14 | InitializeComponent();
15 | ViewModel = new ConnectionsViewModel();
16 |
17 | this.WhenActivated(disposables =>
18 | {
19 | this.OneWayBind(ViewModel, vm => vm.ConnectionItems, v => v.lstConnections.ItemsSource).DisposeWith(disposables);
20 | this.Bind(ViewModel, vm => vm.SelectedSource, v => v.lstConnections.SelectedItem).DisposeWith(disposables);
21 | this.OneWayBind(ViewModel, vm => vm.ConnectionItems.Count, v => v.chipCount.Content).DisposeWith(disposables);
22 |
23 | this.BindCommand(ViewModel, vm => vm.ConnectionCloseCmd, v => v.menuConnectionClose).DisposeWith(disposables);
24 | this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.menuConnectionCloseAll).DisposeWith(disposables);
25 |
26 | this.Bind(ViewModel, vm => vm.SortingSelected, v => v.cmbSorting.SelectedIndex).DisposeWith(disposables);
27 | this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.btnConnectionCloseAll).DisposeWith(disposables);
28 | this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
29 | });
30 | }
31 |
32 | private void btnClose_Click(object sender, System.Windows.RoutedEventArgs e)
33 | {
34 | ViewModel?.ClashConnectionClose(false);
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/clashN/clashN/Views/DashboardView.xaml:
--------------------------------------------------------------------------------
1 |
15 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
33 | Dashboard
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/clashN/clashN/Views/DashboardView.xaml.cs:
--------------------------------------------------------------------------------
1 | using ReactiveUI;
2 |
3 | namespace ClashN.Views
4 | {
5 | ///
6 | /// Interaction logic for DashboardView.xaml
7 | ///
8 | public partial class DashboardView
9 | {
10 | public DashboardView()
11 | {
12 | InitializeComponent();
13 | this.WhenActivated(disposables =>
14 | {
15 | });
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/clashN/clashN/Views/GlobalHotkeySettingWindow.xaml:
--------------------------------------------------------------------------------
1 |
27 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
57 |
58 |
65 |
66 |
75 |
76 |
83 |
92 |
93 |
100 |
109 |
116 |
125 |
132 |
141 |
142 |
143 |
149 |
150 |
154 |
155 |
156 |
157 |
158 |
159 |
166 |
173 |
180 |
181 |
182 |
183 |
--------------------------------------------------------------------------------
/clashN/clashN/Views/GlobalHotkeySettingWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using ClashN.Handler;
2 | using ClashN.Mode;
3 | using ClashN.Resx;
4 | using System.Windows;
5 | using System.Windows.Controls;
6 | using System.Windows.Input;
7 | using Forms = System.Windows.Forms;
8 |
9 | namespace ClashN.Views
10 | {
11 | ///
12 | /// GlobalHotkeySettingWindow.xaml 的交互逻辑
13 | ///
14 | public partial class GlobalHotkeySettingWindow
15 | {
16 | private static Config _config;
17 | private List lstKey;
18 |
19 | public GlobalHotkeySettingWindow()
20 | {
21 | InitializeComponent();
22 | _config = LazyConfig.Instance.Config;
23 |
24 | foreach (GlobalHotkeyAction it in Enum.GetValues(typeof(GlobalHotkeyAction)))
25 | {
26 | if (_config.globalHotkeys.FindIndex(t => t.GlobalHotkey == it) >= 0)
27 | {
28 | continue;
29 | }
30 |
31 | _config.globalHotkeys.Add(new KeyShortcut()
32 | {
33 | GlobalHotkey = it,
34 | Alt = false,
35 | Control = false,
36 | Shift = false,
37 | KeyCode = null
38 | });
39 | }
40 |
41 | lstKey = Utils.DeepCopy(_config.globalHotkeys);
42 |
43 | txtGlobalHotkey0.KeyDown += TxtGlobalHotkey_KeyDown;
44 | txtGlobalHotkey1.KeyDown += TxtGlobalHotkey_KeyDown;
45 | txtGlobalHotkey2.KeyDown += TxtGlobalHotkey_KeyDown;
46 | txtGlobalHotkey3.KeyDown += TxtGlobalHotkey_KeyDown;
47 | txtGlobalHotkey4.KeyDown += TxtGlobalHotkey_KeyDown;
48 |
49 | BindingData(-1);
50 |
51 | Utils.SetDarkBorder(this, _config.UiItem.colorModeDark);
52 | }
53 |
54 | private void TxtGlobalHotkey_KeyDown(object sender, KeyEventArgs e)
55 | {
56 | var txt = ((TextBox)sender);
57 | var index = Utils.ToInt(txt.Name.Substring(txt.Name.Length - 1, 1));
58 |
59 | if (e.Key == Key.System)
60 | return;
61 | var formsKey = (Forms.Keys)KeyInterop.VirtualKeyFromKey(e.Key);
62 |
63 | lstKey[index] = new KeyShortcut()
64 | {
65 | KeyCode = formsKey,
66 | Alt = Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt),
67 | Control = Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl),
68 | Shift = Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift),
69 | };
70 |
71 | BindingData(index);
72 | }
73 |
74 | private void BindingData(int index)
75 | {
76 | for (int k = 0; k < lstKey.Count; k++)
77 | {
78 | if (index >= 0 && index != k)
79 | {
80 | continue;
81 | }
82 | var item = lstKey[k];
83 | var keys = string.Empty;
84 |
85 | if (item.Control)
86 | {
87 | keys += $"{Forms.Keys.Control.ToString()} + ";
88 | }
89 | if (item.Alt)
90 | {
91 | keys += $"{Forms.Keys.Alt.ToString()} + ";
92 | }
93 | if (item.Shift)
94 | {
95 | keys += $"{Forms.Keys.Shift.ToString()} + ";
96 | }
97 | if (item.KeyCode != null)
98 | {
99 | keys += $"{item.KeyCode.ToString()}";
100 | }
101 |
102 | SetText($"txtGlobalHotkey{k}", keys);
103 | }
104 | }
105 |
106 | private void btnSave_Click(object sender, RoutedEventArgs e)
107 | {
108 | _config.globalHotkeys.Clear();
109 | _config.globalHotkeys.AddRange(lstKey);
110 |
111 | if (ConfigProc.SaveConfig(_config, false) == 0)
112 | {
113 | this.Close();
114 | }
115 | else
116 | {
117 | UI.ShowWarning(ResUI.OperationFailed);
118 | }
119 | }
120 |
121 | private void btnCancel_Click(object sender, RoutedEventArgs e)
122 | {
123 | this.Close();
124 | }
125 |
126 | private void btnReset_Click(object sender, RoutedEventArgs e)
127 | {
128 | lstKey.Clear();
129 | foreach (GlobalHotkeyAction it in Enum.GetValues(typeof(GlobalHotkeyAction)))
130 | {
131 | if (lstKey.FindIndex(t => t.GlobalHotkey == it) >= 0)
132 | {
133 | continue;
134 | }
135 |
136 | lstKey.Add(new KeyShortcut()
137 | {
138 | GlobalHotkey = it,
139 | Alt = false,
140 | Control = false,
141 | Shift = false,
142 | KeyCode = null
143 | });
144 | }
145 | BindingData(-1);
146 | }
147 |
148 | private void SetText(string name, string txt)
149 | {
150 | foreach (UIElement element in gridText.Children)
151 | {
152 | if (element is TextBox)
153 | {
154 | if (((TextBox)element).Name == name)
155 | {
156 | ((TextBox)element).Text = txt;
157 | }
158 | }
159 | }
160 | }
161 |
162 | private void GlobalHotkeySettingWindow_KeyDown(object sender, KeyEventArgs e)
163 | {
164 | if (e.Key == Key.Escape)
165 | {
166 | this.Close();
167 | }
168 | }
169 | }
170 | }
--------------------------------------------------------------------------------
/clashN/clashN/Views/HelpView.xaml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
22 |
23 |
24 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
41 |
42 |
43 |
44 |
45 |
50 |
57 |
58 |
59 |
60 |
61 |
62 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
79 |
80 |
81 |
82 |
83 |
88 |
94 |
95 |
96 |
97 |
98 |
99 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
116 |
117 |
118 |
119 |
120 |
125 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
--------------------------------------------------------------------------------
/clashN/clashN/Views/HelpView.xaml.cs:
--------------------------------------------------------------------------------
1 | using ClashN.ViewModels;
2 | using ReactiveUI;
3 | using System.Reactive.Disposables;
4 |
5 | namespace ClashN.Views
6 | {
7 | ///
8 | /// Interaction logic for HelpView.xaml
9 | ///
10 | public partial class HelpView
11 | {
12 | public HelpView()
13 | {
14 | InitializeComponent();
15 | ViewModel = new HelpViewModel();
16 |
17 | this.WhenActivated(disposables =>
18 | {
19 | this.BindCommand(ViewModel, vm => vm.CheckUpdateCmd, v => v.btnCheckUpdateN).DisposeWith(disposables);
20 | //this.BindCommand(ViewModel, vm => vm.CheckUpdateClashCoreCmd, v => v.btnCheckUpdateClashCore).DisposeWith(disposables);
21 | this.BindCommand(ViewModel, vm => vm.CheckUpdateMihomoCoreCmd, v => v.btnCheckUpdateMihomoCore).DisposeWith(disposables);
22 | //this.BindCommand(ViewModel, vm => vm.CheckUpdateGeoDataCmd, v => v.btnCheckUpdateGeo).DisposeWith(disposables);
23 | });
24 | }
25 |
26 | private void btnAbout_Click(object sender, System.Windows.RoutedEventArgs e)
27 | {
28 | Utils.ProcessStart(Global.AboutUrl);
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/clashN/clashN/Views/LogsView.xaml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
22 |
23 |
24 |
25 |
26 |
32 |
36 |
40 |
41 |
49 |
50 |
54 |
58 |
59 |
63 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
82 |
83 |
--------------------------------------------------------------------------------
/clashN/clashN/Views/LogsView.xaml.cs:
--------------------------------------------------------------------------------
1 | using ClashN.ViewModels;
2 | using ReactiveUI;
3 | using System.Reactive.Disposables;
4 | using System.Text.RegularExpressions;
5 | using System.Windows;
6 | using System.Windows.Threading;
7 |
8 | namespace ClashN.Views
9 | {
10 | ///
11 | /// Interaction logic for LogsView.xaml
12 | ///
13 | public partial class LogsView
14 | {
15 | public LogsView()
16 | {
17 | InitializeComponent();
18 | ViewModel = new LogsViewModel();
19 |
20 | MessageBus.Current.Listen("MsgView").Subscribe(x => DelegateAppendText(x));
21 |
22 | this.WhenActivated(disposables =>
23 | {
24 | this.Bind(ViewModel, vm => vm.MsgFilter, v => v.txtFilter.Text).DisposeWith(disposables);
25 | this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
26 | this.Bind(ViewModel, vm => vm.LineCount, v => v.cmbLineCount.Text).DisposeWith(disposables);
27 | });
28 | }
29 |
30 | private void DelegateAppendText(string msg)
31 | {
32 | Dispatcher.BeginInvoke(new Action(AppendText), DispatcherPriority.Send, msg);
33 | }
34 |
35 | public void AppendText(string msg)
36 | {
37 | if (ViewModel?.AutoRefresh == false)
38 | {
39 | return;
40 | }
41 | string? msgFilter = ViewModel?.MsgFilter;
42 | if (!string.IsNullOrEmpty(msgFilter))
43 | {
44 | if (!Regex.IsMatch(msg, msgFilter))
45 | {
46 | return;
47 | }
48 | }
49 |
50 | ShowMsg(msg);
51 | }
52 |
53 | private void ShowMsg(string msg)
54 | {
55 | if (txtMsg.LineCount > ViewModel?.LineCount)
56 | {
57 | ClearMsg();
58 | }
59 | this.txtMsg.AppendText(msg);
60 | if (!msg.EndsWith(Environment.NewLine))
61 | {
62 | this.txtMsg.AppendText(Environment.NewLine);
63 | }
64 | txtMsg.ScrollToEnd();
65 | }
66 |
67 | public void ClearMsg()
68 | {
69 | Dispatcher.Invoke((Action)(() =>
70 | {
71 | txtMsg.Clear();
72 | }));
73 | }
74 |
75 | private void btnDelete_Click(object sender, RoutedEventArgs e)
76 | {
77 | ClearMsg();
78 | }
79 | }
80 | }
--------------------------------------------------------------------------------
/clashN/clashN/Views/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using ClashN.Resx;
2 | using ClashN.ViewModels;
3 | using ReactiveUI;
4 | using Splat;
5 | using System.ComponentModel;
6 | using System.Reactive.Disposables;
7 | using System.Windows;
8 |
9 | namespace ClashN.Views
10 | {
11 | ///
12 | /// Interaction logic for MainWindow.xaml
13 | ///
14 | public partial class MainWindow
15 | {
16 | public MainWindow()
17 | {
18 | InitializeComponent();
19 | this.Closing += MainWindow_Closing;
20 | App.Current.SessionEnding += Current_SessionEnding;
21 |
22 | ViewModel = new MainWindowViewModel(MainSnackbar.MessageQueue!);
23 | Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel));
24 |
25 | this.WhenActivated(disposables =>
26 | {
27 | //this.OneWayBind(ViewModel, vm => vm.GetDashboardView, v => v.dashboardTabItem.Content).DisposeWith(disposables);
28 | this.OneWayBind(ViewModel, vm => vm.GetProxyView, v => v.proxiesTabItem.Content).DisposeWith(disposables);
29 | this.OneWayBind(ViewModel, vm => vm.GetProfilesView, v => v.profilesTabItem.Content).DisposeWith(disposables);
30 | this.OneWayBind(ViewModel, vm => vm.GetLogsView, v => v.logsTabItem.Content).DisposeWith(disposables);
31 | this.OneWayBind(ViewModel, vm => vm.GetConnectionsView, v => v.connectionsTabItem.Content).DisposeWith(disposables);
32 |
33 | this.OneWayBind(ViewModel, vm => vm.GetSettingsView, v => v.settingsTabItem.Content).DisposeWith(disposables);
34 | this.OneWayBind(ViewModel, vm => vm.GetHelpView, v => v.helpTabItem.Content).DisposeWith(disposables);
35 | this.OneWayBind(ViewModel, vm => vm.GetPromotionView, v => v.promotionTabItem.Content).DisposeWith(disposables);
36 | this.OneWayBind(ViewModel, vm => vm.SpeedUpload, v => v.txtSpeedUpload.Text).DisposeWith(disposables);
37 | this.OneWayBind(ViewModel, vm => vm.SpeedDownload, v => v.txtSpeedDownload.Text).DisposeWith(disposables);
38 |
39 | this.OneWayBind(ViewModel, vm => vm.BlSystemProxyClear, v => v.menuSystemProxyClear2.Visibility, conversionHint: BooleanToVisibilityHint.UseHidden, vmToViewConverterOverride: new BooleanToVisibilityTypeConverter()).DisposeWith(disposables);
40 | this.OneWayBind(ViewModel, vm => vm.BlSystemProxySet, v => v.menuSystemProxySet2.Visibility, conversionHint: BooleanToVisibilityHint.UseHidden, vmToViewConverterOverride: new BooleanToVisibilityTypeConverter()).DisposeWith(disposables);
41 | this.OneWayBind(ViewModel, vm => vm.BlSystemProxyNothing, v => v.menuSystemProxyNothing2.Visibility, conversionHint: BooleanToVisibilityHint.UseHidden, vmToViewConverterOverride: new BooleanToVisibilityTypeConverter()).DisposeWith(disposables);
42 | this.OneWayBind(ViewModel, vm => vm.BlSystemProxyPac, v => v.menuSystemProxyPac2.Visibility, conversionHint: BooleanToVisibilityHint.UseHidden, vmToViewConverterOverride: new BooleanToVisibilityTypeConverter()).DisposeWith(disposables);
43 | this.BindCommand(ViewModel, vm => vm.SystemProxyClearCmd, v => v.menuSystemProxyClear).DisposeWith(disposables);
44 | this.BindCommand(ViewModel, vm => vm.SystemProxySetCmd, v => v.menuSystemProxySet).DisposeWith(disposables);
45 | this.BindCommand(ViewModel, vm => vm.SystemProxyPacCmd, v => v.menuSystemProxyPac).DisposeWith(disposables);
46 | this.BindCommand(ViewModel, vm => vm.SystemProxyNothingCmd, v => v.menuSystemProxyNothing).DisposeWith(disposables);
47 |
48 | this.OneWayBind(ViewModel, vm => vm.BlModeRule, v => v.menuModeRule2.Visibility, conversionHint: BooleanToVisibilityHint.UseHidden, vmToViewConverterOverride: new BooleanToVisibilityTypeConverter()).DisposeWith(disposables);
49 | this.OneWayBind(ViewModel, vm => vm.BlModeGlobal, v => v.menuModeGlobal2.Visibility, conversionHint: BooleanToVisibilityHint.UseHidden, vmToViewConverterOverride: new BooleanToVisibilityTypeConverter()).DisposeWith(disposables);
50 | this.OneWayBind(ViewModel, vm => vm.BlModeDirect, v => v.menuModeDirect2.Visibility, conversionHint: BooleanToVisibilityHint.UseHidden, vmToViewConverterOverride: new BooleanToVisibilityTypeConverter()).DisposeWith(disposables);
51 | this.OneWayBind(ViewModel, vm => vm.BlModeNothing, v => v.menuModeNothing2.Visibility, conversionHint: BooleanToVisibilityHint.UseHidden, vmToViewConverterOverride: new BooleanToVisibilityTypeConverter()).DisposeWith(disposables);
52 | this.BindCommand(ViewModel, vm => vm.ModeRuleCmd, v => v.menuModeRule).DisposeWith(disposables);
53 | this.BindCommand(ViewModel, vm => vm.ModeGlobalCmd, v => v.menuModeGlobal).DisposeWith(disposables);
54 | this.BindCommand(ViewModel, vm => vm.ModeDirectCmd, v => v.menuModeDirect).DisposeWith(disposables);
55 | this.BindCommand(ViewModel, vm => vm.ModeNothingCmd, v => v.menuModeNothing).DisposeWith(disposables);
56 |
57 | this.BindCommand(ViewModel, vm => vm.AddProfileViaScanCmd, v => v.menuAddProfileViaScan).DisposeWith(disposables);
58 | this.BindCommand(ViewModel, vm => vm.SubUpdateCmd, v => v.menuSubUpdate).DisposeWith(disposables);
59 | this.BindCommand(ViewModel, vm => vm.SubUpdateViaProxyCmd, v => v.menuSubUpdateViaProxy).DisposeWith(disposables);
60 |
61 | //this.BindCommand(ViewModel, vm => vm.ExitCmd, v => v.menuExit).DisposeWith(disposables);
62 | this.BindCommand(ViewModel, vm => vm.ReloadCmd, v => v.btnReload).DisposeWith(disposables);
63 |
64 | this.OneWayBind(ViewModel, vm => vm.NotifyIcon, v => v.tbNotify.Icon).DisposeWith(disposables);
65 | this.OneWayBind(ViewModel, vm => vm.NotifyLeftClickCmd, v => v.tbNotify.LeftClickCommand).DisposeWith(disposables);
66 | });
67 |
68 | this.Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
69 | }
70 |
71 | private void MainWindow_Closing(object? sender, CancelEventArgs e)
72 | {
73 | e.Cancel = true;
74 | ViewModel?.ShowHideWindow(false);
75 | }
76 |
77 | private void menuExit_Click(object sender, RoutedEventArgs e)
78 | {
79 | tbNotify.Dispose();
80 | ViewModel?.MyAppExit(false);
81 | }
82 |
83 | private void Current_SessionEnding(object sender, SessionEndingCancelEventArgs e)
84 | {
85 | Utils.SaveLog("Current_SessionEnding");
86 | ViewModel?.MyAppExit(true);
87 | }
88 | }
89 | }
--------------------------------------------------------------------------------
/clashN/clashN/Views/MessageSampleDialog.xaml:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
22 |
30 |
31 |
--------------------------------------------------------------------------------
/clashN/clashN/Views/MessageSampleDialog.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Controls;
2 |
3 | namespace ClashN.Views
4 | {
5 | ///
6 | /// Interaction logic for MessageSampleDialog.xaml
7 | ///
8 | public partial class MessageSampleDialog : UserControl
9 | {
10 | public MessageSampleDialog()
11 | {
12 | InitializeComponent();
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/clashN/clashN/Views/MsgView.xaml:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
21 |
29 |
30 |
--------------------------------------------------------------------------------
/clashN/clashN/Views/MsgView.xaml.cs:
--------------------------------------------------------------------------------
1 | using ReactiveUI;
2 | using System.Reactive.Linq;
3 | using System.Windows.Threading;
4 |
5 | namespace ClashN.Views
6 | {
7 | ///
8 | /// Interaction logic for MsgView.xaml
9 | ///
10 | public partial class MsgView
11 | {
12 | public MsgView()
13 | {
14 | InitializeComponent();
15 | MessageBus.Current.Listen("MsgView").Subscribe(x => DelegateAppendText(x));
16 | }
17 |
18 | private void DelegateAppendText(string msg)
19 | {
20 | Dispatcher.BeginInvoke(new Action(AppendText), DispatcherPriority.Send, msg);
21 | }
22 |
23 | public void AppendText(string msg)
24 | {
25 | //if (!string.IsNullOrEmpty(MsgFilter))
26 | //{
27 | // if (!Regex.IsMatch(text, MsgFilter))
28 | // {
29 | // return;
30 | // }
31 | //}
32 |
33 | ShowMsg(msg);
34 | }
35 |
36 | private void ShowMsg(string msg)
37 | {
38 | if (txtMsg.LineCount > 999)
39 | {
40 | ClearMsg();
41 | }
42 | this.txtMsg.AppendText(msg);
43 | if (!msg.EndsWith(Environment.NewLine))
44 | {
45 | this.txtMsg.AppendText(Environment.NewLine);
46 | }
47 | txtMsg.ScrollToEnd();
48 | }
49 |
50 | public void ClearMsg()
51 | {
52 | Dispatcher.Invoke((Action)(() =>
53 | {
54 | txtMsg.Clear();
55 | }));
56 | }
57 | }
58 | }
--------------------------------------------------------------------------------
/clashN/clashN/Views/PorfileEditWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using ClashN.Mode;
2 | using ClashN.ViewModels;
3 | using ReactiveUI;
4 | using System.Reactive.Disposables;
5 | using System.Windows.Input;
6 |
7 | namespace ClashN.Views
8 | {
9 | ///
10 | /// PorfileEditWindow.xaml 的交互逻辑
11 | ///
12 | public partial class PorfileEditWindow
13 | {
14 | public PorfileEditWindow(ProfileItem profileItem)
15 | {
16 | InitializeComponent();
17 | ViewModel = new ProfileEditViewModel(profileItem, this);
18 | Global.coreTypes.ForEach(it =>
19 | {
20 | cmbCoreType.Items.Add(it);
21 | });
22 | this.WhenActivated(disposables =>
23 | {
24 | this.Bind(ViewModel, vm => vm.SelectedSource.remarks, v => v.txtRemarks.Text).DisposeWith(disposables);
25 | this.Bind(ViewModel, vm => vm.SelectedSource.url, v => v.txtUrl.Text).DisposeWith(disposables);
26 | this.Bind(ViewModel, vm => vm.SelectedSource.address, v => v.txtAddress.Text).DisposeWith(disposables);
27 | this.Bind(ViewModel, vm => vm.SelectedSource.userAgent, v => v.txtUserAgent.Text).DisposeWith(disposables);
28 |
29 | this.Bind(ViewModel, vm => vm.CoreType, v => v.cmbCoreType.Text).DisposeWith(disposables);
30 | this.Bind(ViewModel, vm => vm.SelectedSource.enabled, v => v.togEnabled.IsChecked).DisposeWith(disposables);
31 | this.Bind(ViewModel, vm => vm.SelectedSource.enableConvert, v => v.togEnableConvert.IsChecked).DisposeWith(disposables);
32 |
33 | this.BindCommand(ViewModel, vm => vm.BrowseProfileCmd, v => v.btnBrowse).DisposeWith(disposables);
34 | this.BindCommand(ViewModel, vm => vm.EditProfileCmd, v => v.btnEdit).DisposeWith(disposables);
35 | this.BindCommand(ViewModel, vm => vm.SaveProfileCmd, v => v.btnSave).DisposeWith(disposables);
36 | });
37 | }
38 |
39 | private void btnCancel_Click(object sender, System.Windows.RoutedEventArgs e)
40 | {
41 | this.Close();
42 | }
43 |
44 | private void PorfileEditWindow_KeyDown(object sender, KeyEventArgs e)
45 | {
46 | if (e.Key == Key.Escape)
47 | {
48 | this.Close();
49 | }
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/clashN/clashN/Views/ProfileQrcodeView.xaml:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
25 |
26 |
36 |
37 |
47 |
48 |
--------------------------------------------------------------------------------
/clashN/clashN/Views/ProfileQrcodeView.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Controls;
2 |
3 | namespace ClashN.Views
4 | {
5 | ///
6 | /// Interaction logic for ProfileQrcodeView.xaml
7 | ///
8 | public partial class ProfileQrcodeView : UserControl
9 | {
10 | public ProfileQrcodeView()
11 | {
12 | InitializeComponent();
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/clashN/clashN/Views/PromotionView.xaml:
--------------------------------------------------------------------------------
1 |
16 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
35 |
36 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
53 |
54 |
59 |
60 |
61 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/clashN/clashN/Views/PromotionView.xaml.cs:
--------------------------------------------------------------------------------
1 | using ReactiveUI;
2 | using System.Windows;
3 |
4 | namespace ClashN.Views
5 | {
6 | ///
7 | /// Interaction logic for PromotionView.xaml
8 | ///
9 | public partial class PromotionView
10 | {
11 | public PromotionView()
12 | {
13 | InitializeComponent();
14 | this.WhenActivated(disposables =>
15 | {
16 | });
17 | }
18 |
19 | private void btnPromotion_Click(object sender, RoutedEventArgs e)
20 | {
21 | Utils.ProcessStart($"{Utils.Base64Decode(Global.PromotionUrl)}?t={DateTime.Now.Ticks}");
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/clashN/clashN/Views/ProxiesView.xaml.cs:
--------------------------------------------------------------------------------
1 | using ClashN.ViewModels;
2 | using ReactiveUI;
3 | using Splat;
4 | using System.Reactive.Disposables;
5 | using System.Windows.Input;
6 |
7 | namespace ClashN.Views
8 | {
9 | ///
10 | /// Interaction logic for ProxiesView.xaml
11 | ///
12 | public partial class ProxiesView
13 | {
14 | public ProxiesView()
15 | {
16 | InitializeComponent();
17 | ViewModel = new ProxiesViewModel();
18 | Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(ProxiesViewModel));
19 | lstProxyDetails.PreviewMouseDoubleClick += lstProxyDetails_PreviewMouseDoubleClick;
20 |
21 | this.WhenActivated(disposables =>
22 | {
23 | this.OneWayBind(ViewModel, vm => vm.ProxyGroups, v => v.lstProxyGroups.ItemsSource).DisposeWith(disposables);
24 | this.Bind(ViewModel, vm => vm.SelectedGroup, v => v.lstProxyGroups.SelectedItem).DisposeWith(disposables);
25 |
26 | this.OneWayBind(ViewModel, vm => vm.ProxyDetails, v => v.lstProxyDetails.ItemsSource).DisposeWith(disposables);
27 | this.Bind(ViewModel, vm => vm.SelectedDetail, v => v.lstProxyDetails.SelectedItem).DisposeWith(disposables);
28 |
29 | this.BindCommand(ViewModel, vm => vm.ProxiesReloadCmd, v => v.menuProxiesReload).DisposeWith(disposables);
30 | this.BindCommand(ViewModel, vm => vm.ProxiesDelaytestCmd, v => v.menuProxiesDelaytest).DisposeWith(disposables);
31 |
32 | this.BindCommand(ViewModel, vm => vm.ProxiesDelaytestPartCmd, v => v.menuProxiesDelaytestPart).DisposeWith(disposables);
33 | this.BindCommand(ViewModel, vm => vm.ProxiesSelectActivityCmd, v => v.menuProxiesSelectActivity).DisposeWith(disposables);
34 |
35 | this.Bind(ViewModel, vm => vm.SystemProxySelected, v => v.cmbSystemProxy.SelectedIndex).DisposeWith(disposables);
36 | this.Bind(ViewModel, vm => vm.RuleModeSelected, v => v.cmbRulemode.SelectedIndex).DisposeWith(disposables);
37 | this.Bind(ViewModel, vm => vm.SortingSelected, v => v.cmbSorting.SelectedIndex).DisposeWith(disposables);
38 | this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
39 | this.Bind(ViewModel, vm => vm.EnableTun, v => v.togEnableTun.IsChecked).DisposeWith(disposables);
40 | });
41 | }
42 |
43 | private void ProxiesView_KeyDown(object sender, KeyEventArgs e)
44 | {
45 | switch (e.Key)
46 | {
47 | case Key.F5:
48 | ViewModel?.ProxiesReload();
49 | break;
50 |
51 | case Key.Enter:
52 | ViewModel?.SetActiveProxy();
53 | break;
54 | }
55 | }
56 |
57 | private void lstProxyDetails_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
58 | {
59 | ViewModel?.SetActiveProxy();
60 | }
61 | }
62 | }
--------------------------------------------------------------------------------
/clashN/clashN/Views/SettingsView.xaml.cs:
--------------------------------------------------------------------------------
1 | using ClashN.Handler;
2 | using ClashN.Mode;
3 | using ClashN.ViewModels;
4 | using ReactiveUI;
5 | using System.Globalization;
6 | using System.IO;
7 | using System.Reactive.Disposables;
8 | using System.Windows.Media;
9 |
10 | namespace ClashN.Views
11 | {
12 | ///
13 | /// Interaction logic for SettingsView.xaml
14 | ///
15 | public partial class SettingsView
16 | {
17 | private static Config _config;
18 |
19 | public SettingsView()
20 | {
21 | InitializeComponent();
22 | _config = LazyConfig.Instance.Config;
23 | ViewModel = new SettingsViewModel();
24 |
25 | Global.SubConvertUrls.ForEach(it =>
26 | {
27 | cmbSubConvertUrl.Items.Add(it);
28 | });
29 | Global.Languages.ForEach(it =>
30 | {
31 | cmbCurrentLanguage.Items.Add(it);
32 | });
33 | Global.IEProxyProtocols.ForEach(it =>
34 | {
35 | cmbsystemProxyAdvancedProtocol.Items.Add(it);
36 | });
37 | Global.LogLevel.ForEach(it =>
38 | {
39 | cmbLogLevel.Items.Add(it);
40 | });
41 |
42 | for (int i = Global.MinFontSize; i <= Global.MinFontSize + 8; i++)
43 | {
44 | cmbCurrentFontSize.Items.Add(i.ToString());
45 | }
46 |
47 | //fill fonts
48 | try
49 | {
50 | var dir = new DirectoryInfo(Utils.GetFontsPath());
51 | var files = dir.GetFiles("*.ttf");
52 | var culture = "zh-cn";
53 | var culture2 = "en-us";
54 | foreach (var it in files)
55 | {
56 | var families = Fonts.GetFontFamilies(Utils.GetFontsPath(it.Name));
57 | foreach (FontFamily family in families)
58 | {
59 | var typefaces = family.GetTypefaces();
60 | foreach (Typeface typeface in typefaces)
61 | {
62 | typeface.TryGetGlyphTypeface(out GlyphTypeface glyph);
63 | //var fontFace = glyph.Win32FaceNames[new CultureInfo("en-us")];
64 | //if (!fontFace.Equals("Regular") && !fontFace.Equals("Normal"))
65 | //{
66 | // continue;
67 | //}
68 | var fontFamily = glyph.Win32FamilyNames[new CultureInfo(culture)];
69 | if (string.IsNullOrEmpty(fontFamily))
70 | {
71 | fontFamily = glyph.Win32FamilyNames[new CultureInfo(culture2)];
72 | if (string.IsNullOrEmpty(fontFamily))
73 | {
74 | continue;
75 | }
76 | }
77 | cmbcurrentFontFamily.Items.Add(fontFamily);
78 | break;
79 | }
80 | }
81 | }
82 | }
83 | catch (Exception ex)
84 | {
85 | Utils.SaveLog("fill fonts error", ex);
86 | }
87 | cmbcurrentFontFamily.Items.Add(string.Empty);
88 |
89 | this.WhenActivated(disposables =>
90 | {
91 | this.Bind(ViewModel, vm => vm.MixedPort, v => v.txtMixedPort.Text).DisposeWith(disposables);
92 | this.Bind(ViewModel, vm => vm.SocksPort, v => v.txtSocksPort.Text).DisposeWith(disposables);
93 | this.Bind(ViewModel, vm => vm.HttpPort, v => v.txtHttpPort.Text).DisposeWith(disposables);
94 | this.Bind(ViewModel, vm => vm.APIPort, v => v.txtAPIPort.Text).DisposeWith(disposables);
95 | this.Bind(ViewModel, vm => vm.AllowLANConn, v => v.togAllowLANConn.IsChecked).DisposeWith(disposables);
96 | this.Bind(ViewModel, vm => vm.EnableIpv6, v => v.togEnableIpv6.IsChecked).DisposeWith(disposables);
97 | this.Bind(ViewModel, vm => vm.LogLevel, v => v.cmbLogLevel.Text).DisposeWith(disposables);
98 | this.Bind(ViewModel, vm => vm.EnableMixinContent, v => v.togEnableMixinContent.IsChecked).DisposeWith(disposables);
99 | this.BindCommand(ViewModel, vm => vm.EditMixinContentCmd, v => v.btnEditMixinContent).DisposeWith(disposables);
100 |
101 | this.OneWayBind(ViewModel, vm => vm.Swatches, v => v.cmbSwatches.ItemsSource).DisposeWith(disposables);
102 | this.Bind(ViewModel, vm => vm.SelectedSwatch, v => v.cmbSwatches.SelectedItem).DisposeWith(disposables);
103 | this.Bind(ViewModel, vm => vm.ColorModeDark, v => v.togDarkMode.IsChecked).DisposeWith(disposables);
104 | this.Bind(ViewModel, vm => vm.CurrentLanguage, v => v.cmbCurrentLanguage.Text).DisposeWith(disposables);
105 | this.Bind(ViewModel, vm => vm.CurrentFontSize, v => v.cmbCurrentFontSize.Text).DisposeWith(disposables);
106 | this.Bind(ViewModel, vm => vm.AutoRun, v => v.togAutoRun.IsChecked).DisposeWith(disposables);
107 | this.Bind(ViewModel, vm => vm.EnableStatistics, v => v.togEnableStatistics.IsChecked).DisposeWith(disposables);
108 | this.Bind(ViewModel, vm => vm.EnableSecurityProtocolTls13, v => v.togEnableSecurityProtocolTls13.IsChecked).DisposeWith(disposables);
109 | this.Bind(ViewModel, vm => vm.AutoHideStartup, v => v.togAutoHideStartup.IsChecked).DisposeWith(disposables);
110 | this.Bind(ViewModel, vm => vm.autoUpdateSubInterval, v => v.txtautoUpdateSubInterval.Text).DisposeWith(disposables);
111 | this.Bind(ViewModel, vm => vm.autoDelayTestInterval, v => v.txtautoDelayTestInterval.Text).DisposeWith(disposables);
112 | this.Bind(ViewModel, vm => vm.SubConvertUrl, v => v.cmbSubConvertUrl.Text).DisposeWith(disposables);
113 | this.Bind(ViewModel, vm => vm.currentFontFamily, v => v.cmbcurrentFontFamily.Text).DisposeWith(disposables);
114 | this.BindCommand(ViewModel, vm => vm.SetLoopbackCmd, v => v.btnSetLoopback).DisposeWith(disposables);
115 | this.BindCommand(ViewModel, vm => vm.SetGlobalHotkeyCmd, v => v.btnSetGlobalHotkey).DisposeWith(disposables);
116 |
117 | this.Bind(ViewModel, vm => vm.systemProxyExceptions, v => v.txtsystemProxyExceptions.Text).DisposeWith(disposables);
118 | this.Bind(ViewModel, vm => vm.systemProxyAdvancedProtocol, v => v.cmbsystemProxyAdvancedProtocol.Text).DisposeWith(disposables);
119 | this.Bind(ViewModel, vm => vm.PacPort, v => v.txtPacPort.Text).DisposeWith(disposables);
120 |
121 | this.BindCommand(ViewModel, vm => vm.SaveCommand, v => v.btnSave).DisposeWith(disposables);
122 | });
123 | }
124 | }
125 | }
--------------------------------------------------------------------------------
/clashN/clashN/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | true
6 | PerMonitorV2
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/clashN/clashN/clashN.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WinExe
5 | net8.0-windows10.0.17763.0
6 | enable
7 | true
8 | app.manifest
9 | enable
10 | ClashN.ico
11 | Copyright © 2019-2024 (GPLv3)
12 | 2.22
13 | 7.0
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | Never
39 |
40 |
41 |
42 |
43 |
44 | Always
45 |
46 |
47 | True
48 | True
49 | ResUI.resx
50 |
51 |
52 | PublicResXFileCodeGenerator
53 | ResUI.Designer.cs
54 |
55 |
56 | PublicResXFileCodeGenerator
57 |
58 |
59 | PublicResXFileCodeGenerator
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/clashN/clashN/clashN.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/2dust/clashN/0b498424d3bc1ef3a9fa1b9a6c05b4b216303984/clashN/clashN/clashN.ico
--------------------------------------------------------------------------------
/clashN/clashUpgrade/MainForm.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace clashUpgrade
2 | {
3 | partial class MainForm
4 | {
5 | ///
6 | /// 必需的设计器变量。
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// 清理所有正在使用的资源。
12 | ///
13 | /// 如果应释放托管资源,为 true;否则为 false。
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows 窗体设计器生成的代码
24 |
25 | ///
26 | /// 设计器支持所需的方法 - 不要修改
27 | /// 使用代码编辑器修改此方法的内容。
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.btnClose = new System.Windows.Forms.Button();
32 | this.btnOK = new System.Windows.Forms.Button();
33 | this.label1 = new System.Windows.Forms.Label();
34 | this.label2 = new System.Windows.Forms.Label();
35 | this.SuspendLayout();
36 | //
37 | // btnClose
38 | //
39 | this.btnClose.DialogResult = System.Windows.Forms.DialogResult.Cancel;
40 | this.btnClose.Font = new System.Drawing.Font("微软雅黑", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
41 | this.btnClose.ImeMode = System.Windows.Forms.ImeMode.NoControl;
42 | this.btnClose.Location = new System.Drawing.Point(367, 118);
43 | this.btnClose.Name = "btnClose";
44 | this.btnClose.Size = new System.Drawing.Size(184, 89);
45 | this.btnClose.TabIndex = 1;
46 | this.btnClose.Text = "&Exit(退出)";
47 | this.btnClose.UseVisualStyleBackColor = true;
48 | this.btnClose.Click += new System.EventHandler(this.btnClose_Click);
49 | //
50 | // btnOK
51 | //
52 | this.btnOK.Font = new System.Drawing.Font("微软雅黑", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
53 | this.btnOK.ImeMode = System.Windows.Forms.ImeMode.NoControl;
54 | this.btnOK.Location = new System.Drawing.Point(81, 118);
55 | this.btnOK.Name = "btnOK";
56 | this.btnOK.Size = new System.Drawing.Size(184, 89);
57 | this.btnOK.TabIndex = 0;
58 | this.btnOK.Text = "&Upgrade(升级)";
59 | this.btnOK.UseVisualStyleBackColor = true;
60 | this.btnOK.Click += new System.EventHandler(this.btnOK_Click);
61 | //
62 | // label1
63 | //
64 | this.label1.AutoSize = true;
65 | this.label1.Font = new System.Drawing.Font("微软雅黑", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
66 | this.label1.Location = new System.Drawing.Point(79, 64);
67 | this.label1.Name = "label1";
68 | this.label1.Size = new System.Drawing.Size(205, 15);
69 | this.label1.TabIndex = 8;
70 | this.label1.Text = "升级成功后将自动重启clashN";
71 | //
72 | // label2
73 | //
74 | this.label2.AutoSize = true;
75 | this.label2.Font = new System.Drawing.Font("微软雅黑", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
76 | this.label2.Location = new System.Drawing.Point(79, 37);
77 | this.label2.Name = "label2";
78 | this.label2.Size = new System.Drawing.Size(471, 15);
79 | this.label2.TabIndex = 9;
80 | this.label2.Text = "clashN will restart automatically after successful upgrade";
81 | //
82 | // MainForm
83 | //
84 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
85 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
86 | this.ClientSize = new System.Drawing.Size(616, 284);
87 | this.Controls.Add(this.label2);
88 | this.Controls.Add(this.label1);
89 | this.Controls.Add(this.btnClose);
90 | this.Controls.Add(this.btnOK);
91 | this.Name = "MainForm";
92 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
93 | this.Text = "clashUpgrade";
94 | this.ResumeLayout(false);
95 | this.PerformLayout();
96 |
97 | }
98 |
99 | #endregion
100 |
101 | private System.Windows.Forms.Button btnClose;
102 | private System.Windows.Forms.Button btnOK;
103 | private System.Windows.Forms.Label label1;
104 | private System.Windows.Forms.Label label2;
105 | }
106 | }
107 |
108 |
--------------------------------------------------------------------------------
/clashN/clashUpgrade/MainForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using System.IO.Compression;
5 | using System.Text;
6 | using System.Web;
7 | using System.Windows.Forms;
8 |
9 | namespace clashUpgrade
10 | {
11 | public partial class MainForm : Form
12 | {
13 | private readonly string defaultFilename = "clashN.zip_temp";
14 | private string fileName;
15 |
16 | public MainForm(string[] args)
17 | {
18 | InitializeComponent();
19 | if (args.Length > 0)
20 | {
21 | fileName = string.Join(" ", args);
22 | fileName = HttpUtility.UrlDecode(fileName);
23 | }
24 | }
25 |
26 | private void showWarn(string message)
27 | {
28 | MessageBox.Show(message, "", MessageBoxButtons.OK, MessageBoxIcon.Warning);
29 | }
30 |
31 | private void btnOK_Click(object sender, EventArgs e)
32 | {
33 | try
34 | {
35 | Process[] existing = Process.GetProcessesByName("clashN");
36 | foreach (Process p in existing)
37 | {
38 | string path = p.MainModule.FileName;
39 | if (path == GetPath("clashN.exe"))
40 | {
41 | p.Kill();
42 | p.WaitForExit(100);
43 | }
44 | }
45 | }
46 | catch (Exception ex)
47 | {
48 | // Access may be denied without admin right. The user may not be an administrator.
49 | showWarn("Failed to close clashN(关闭clashN失败).\n" +
50 | "Close it manually, or the upgrade may fail.(请手动关闭正在运行的clashN,否则可能升级失败。\n\n" + ex.StackTrace);
51 | }
52 |
53 | StringBuilder sb = new StringBuilder();
54 | try
55 | {
56 | if (!File.Exists(fileName))
57 | {
58 | if (File.Exists(defaultFilename))
59 | {
60 | fileName = defaultFilename;
61 | }
62 | else
63 | {
64 | showWarn("Upgrade Failed, File Not Exist(升级失败,文件不存在).");
65 | return;
66 | }
67 | }
68 |
69 | string thisAppOldFile = Application.ExecutablePath + ".tmp";
70 | File.Delete(thisAppOldFile);
71 | string startKey = "clashN/";
72 |
73 | using (ZipArchive archive = ZipFile.OpenRead(fileName))
74 | {
75 | foreach (ZipArchiveEntry entry in archive.Entries)
76 | {
77 | try
78 | {
79 | if (entry.Length == 0)
80 | {
81 | continue;
82 | }
83 | string fullName = entry.FullName;
84 | if (fullName.StartsWith(startKey))
85 | {
86 | fullName = fullName.Substring(startKey.Length, fullName.Length - startKey.Length);
87 | }
88 | if (Application.ExecutablePath.ToLower() == GetPath(fullName).ToLower())
89 | {
90 | File.Move(Application.ExecutablePath, thisAppOldFile);
91 | }
92 |
93 | string entryOuputPath = GetPath(fullName);
94 |
95 | FileInfo fileInfo = new FileInfo(entryOuputPath);
96 | fileInfo.Directory.Create();
97 | entry.ExtractToFile(entryOuputPath, true);
98 | }
99 | catch (Exception ex)
100 | {
101 | sb.Append(ex.StackTrace);
102 | }
103 | }
104 | }
105 | }
106 | catch (Exception ex)
107 | {
108 | showWarn("Upgrade Failed(升级失败)." + ex.StackTrace);
109 | return;
110 | }
111 | if (sb.Length > 0)
112 | {
113 | showWarn("Upgrade Failed,Hold ctrl + c to copy to clipboard.\n" +
114 | "(升级失败,按住ctrl+c可以复制到剪贴板)." + sb.ToString());
115 | return;
116 | }
117 |
118 | Process.Start("clashN.exe");
119 | MessageBox.Show("Upgrade successed(升级成功)", "", MessageBoxButtons.OK, MessageBoxIcon.Information);
120 |
121 | Close();
122 | }
123 |
124 | private void btnClose_Click(object sender, EventArgs e)
125 | {
126 | Close();
127 | }
128 |
129 | public static string GetExePath()
130 | {
131 | return Application.ExecutablePath;
132 | }
133 |
134 | public static string StartupPath()
135 | {
136 | return Application.StartupPath;
137 | }
138 |
139 | public static string GetPath(string fileName)
140 | {
141 | string startupPath = StartupPath();
142 | if (string.IsNullOrEmpty(fileName))
143 | {
144 | return startupPath;
145 | }
146 | return Path.Combine(startupPath, fileName);
147 | }
148 | }
149 | }
--------------------------------------------------------------------------------
/clashN/clashUpgrade/MainForm.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/clashN/clashUpgrade/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Forms;
3 |
4 | namespace clashUpgrade
5 | {
6 | internal static class Program
7 | {
8 | ///
9 | /// 应用程序的主入口点。
10 | ///
11 | [STAThread]
12 | private static void Main(string[] args)
13 | {
14 | Application.EnableVisualStyles();
15 | Application.SetHighDpiMode(HighDpiMode.SystemAware);
16 | Application.SetCompatibleTextRenderingDefault(false);
17 | Application.Run(new MainForm(args));
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/clashN/clashUpgrade/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // 此代码由工具生成。
4 | // 运行时版本:4.0.30319.42000
5 | //
6 | // 对此文件的更改可能会导致不正确的行为,并且如果
7 | // 重新生成代码,这些更改将会丢失。
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace clashUpgrade.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// 一个强类型的资源类,用于查找本地化的字符串等。
17 | ///
18 | // 此类是由 StronglyTypedResourceBuilder
19 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
20 | // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
21 | // (以 /str 作为命令选项),或重新生成 VS 项目。
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// 返回此类使用的缓存的 ResourceManager 实例。
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("clashUpgrade.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// 重写当前线程的 CurrentUICulture 属性,对
51 | /// 使用此强类型资源类的所有资源查找执行重写。
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/clashN/clashUpgrade/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/clashN/clashUpgrade/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // 此代码由工具生成。
4 | // 运行时版本:4.0.30319.42000
5 | //
6 | // 对此文件的更改可能会导致不正确的行为,并且如果
7 | // 重新生成代码,这些更改将会丢失。
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace clashUpgrade.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.10.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/clashN/clashUpgrade/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/clashN/clashUpgrade/clashUpgrade.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0-windows
4 | WinExe
5 | true
6 | Copyright © 2019-2020 (GPLv3)
7 | 1.1.0.0
8 | en
9 |
10 |
--------------------------------------------------------------------------------