From bf8783fed733a56c1aea9e528caa944b5d3db724 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sat, 20 Sep 2025 14:06:41 +0800 Subject: [PATCH 001/132] Update CheckUpdateViewModel.cs --- .../ViewModels/CheckUpdateViewModel.cs | 25 ++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs b/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs index 4c9c0550..5ec1f80b 100644 --- a/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs @@ -38,7 +38,7 @@ public class CheckUpdateViewModel : MyReactiveObject this.WhenAnyValue( x => x.EnableCheckPreReleaseUpdate, y => y == true) - .Subscribe(c => { _config.CheckUpdateItem.CheckPreReleaseUpdate = EnableCheckPreReleaseUpdate; }); + .Subscribe(c => _config.CheckUpdateItem.CheckPreReleaseUpdate = EnableCheckPreReleaseUpdate); RefreshCheckUpdateItems(); } @@ -158,11 +158,8 @@ public class CheckUpdateViewModel : MyReactiveObject UpdatedPlusPlus(_geo, ""); } } - await (new UpdateService()).UpdateGeoFileAll(_config, _updateUI) - .ContinueWith(t => - { - UpdatedPlusPlus(_geo, ""); - }); + await new UpdateService().UpdateGeoFileAll(_config, _updateUI) + .ContinueWith(t => UpdatedPlusPlus(_geo, "")); } private async Task CheckUpdateN(bool preRelease) @@ -176,11 +173,8 @@ public class CheckUpdateViewModel : MyReactiveObject UpdatedPlusPlus(_v2rayN, msg); } } - await (new UpdateService()).CheckUpdateGuiN(_config, _updateUI, preRelease) - .ContinueWith(t => - { - UpdatedPlusPlus(_v2rayN, ""); - }); + await new UpdateService().CheckUpdateGuiN(_config, _updateUI, preRelease) + .ContinueWith(t => UpdatedPlusPlus(_v2rayN, "")); } private async Task CheckUpdateCore(CheckUpdateModel model, bool preRelease) @@ -196,11 +190,8 @@ public class CheckUpdateViewModel : MyReactiveObject } } var type = (ECoreType)Enum.Parse(typeof(ECoreType), model.CoreType); - await (new UpdateService()).CheckUpdateCore(type, _config, _updateUI, preRelease) - .ContinueWith(t => - { - UpdatedPlusPlus(model.CoreType, ""); - }); + await new UpdateService().CheckUpdateCore(type, _config, _updateUI, preRelease) + .ContinueWith(t => UpdatedPlusPlus(model.CoreType, "")); } private async Task UpdateFinished() @@ -311,7 +302,7 @@ public class CheckUpdateViewModel : MyReactiveObject if (Utils.IsNonWindows()) { - var filesList = (new DirectoryInfo(toPath)).GetFiles().Select(u => u.FullName).ToList(); + var filesList = new DirectoryInfo(toPath).GetFiles().Select(u => u.FullName).ToList(); foreach (var file in filesList) { await Utils.SetLinuxChmod(Path.Combine(toPath, item.CoreType.ToLower())); From ef30d389dcee3202f03a4a8b7b554ad5e1a8c35b Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sat, 20 Sep 2025 14:06:55 +0800 Subject: [PATCH 002/132] up 7.14.11 --- v2rayN/Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2rayN/Directory.Build.props b/v2rayN/Directory.Build.props index 7bae47f6..ee835a9a 100644 --- a/v2rayN/Directory.Build.props +++ b/v2rayN/Directory.Build.props @@ -1,7 +1,7 @@ - 7.14.10 + 7.14.11 From 3a21596d959f447a57cd344f1a1b7cca7ce447fa Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sun, 21 Sep 2025 12:05:06 +0800 Subject: [PATCH 003/132] Fix node domain resolving in TUN mode https://github.com/2dust/v2rayN/pull/7989 --- v2rayN/ServiceLib/Global.cs | 1 + v2rayN/ServiceLib/Handler/ConfigHandler.cs | 4 +- .../CoreConfig/Singbox/SingboxDnsService.cs | 53 ++++++++++++------- v2rayN/v2rayN/Common/QRCodeUtils.cs | 4 +- 4 files changed, 38 insertions(+), 24 deletions(-) diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs index eb24d71e..b45a6eb3 100644 --- a/v2rayN/ServiceLib/Global.cs +++ b/v2rayN/ServiceLib/Global.cs @@ -598,6 +598,7 @@ public class Global { "cloudflare-dns.com", new List { "104.16.249.249", "104.16.248.249", "2606:4700::6810:f8f9", "2606:4700::6810:f9f9" } }, { "dns.cloudflare.com", new List { "104.16.132.229", "104.16.133.229", "2606:4700::6810:84e5", "2606:4700::6810:85e5" } }, { "dot.pub", new List { "1.12.12.12", "120.53.53.53" } }, + { "doh.pub", new List { "1.12.12.12", "120.53.53.53" } }, { "dns.quad9.net", new List { "9.9.9.9", "149.112.112.112", "2620:fe::fe", "2620:fe::9" } }, { "dns.yandex.net", new List { "77.88.8.8", "77.88.8.1", "2a02:6b8::feed:0ff", "2a02:6b8:0:1::feed:0ff" } }, { "dns.sb", new List { "185.222.222.222", "2a09::" } }, diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index 786f3aff..65f2ee53 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -1214,11 +1214,11 @@ public static class ConfigHandler CoreType = ECoreType.sing_box, ConfigType = EConfigType.SOCKS, Address = Global.Loopback, - Sni = node.Address, //Tun2SocksAddress + SpiderX = node.Address, // Tun2SocksAddress Port = AppManager.Instance.GetLocalPort(EInboundProtocol.socks) }; } - else if ((node.ConfigType == EConfigType.Custom && node.PreSocksPort > 0)) + else if (node.ConfigType == EConfigType.Custom && node.PreSocksPort > 0) { var preCoreType = config.RunningCoreType = config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray; itemSocks = new ProfileItem() diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs index 00bb14d9..9bd2502d 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxDnsService.cs @@ -43,16 +43,7 @@ public partial class CoreConfigSingboxService }); } - // Tun2SocksAddress - if (node != null && Utils.IsDomain(node.Address)) - { - singboxConfig.dns.rules ??= new List(); - singboxConfig.dns.rules.Insert(0, new Rule4Sbox - { - server = Global.SingboxOutboundResolverTag, - domain = [node.Address], - }); - } + await GenOutboundDnsRule(node, singboxConfig, Global.SingboxOutboundResolverTag); } catch (Exception ex) { @@ -346,16 +337,7 @@ public partial class CoreConfigSingboxService await GenDnsDomainsLegacyCompatible(singboxConfig, item); } - // Tun2SocksAddress - if (node != null && Utils.IsDomain(node.Address)) - { - singboxConfig.dns.rules ??= new List(); - singboxConfig.dns.rules.Insert(0, new Rule4Sbox - { - server = Global.SingboxFinalResolverTag, - domain = [node.Address], - }); - } + await GenOutboundDnsRule(node, singboxConfig, Global.SingboxFinalResolverTag); } catch (Exception ex) { @@ -425,6 +407,37 @@ public partial class CoreConfigSingboxService return await Task.FromResult(0); } + private async Task GenOutboundDnsRule(ProfileItem? node, SingboxConfig singboxConfig, string? server) + { + if (node == null) + { + return 0; + } + + var domain = string.Empty; + if (Utils.IsDomain(node.Address)) // normal outbound + { + domain = node.Address; + } + else if (node.Address == Global.Loopback && node.SpiderX.IsNotEmpty() && Utils.IsDomain(node.SpiderX)) // Tun2SocksAddress + { + domain = node.SpiderX; + } + if (domain.IsNullOrEmpty()) + { + return 0; + } + + singboxConfig.dns.rules ??= new List(); + singboxConfig.dns.rules.Insert(0, new Rule4Sbox + { + server = server, + domain = [domain], + }); + + return await Task.FromResult(0); + } + private static Server4Sbox? ParseDnsAddress(string address) { var addressFirst = address?.Split(address.Contains(',') ? ',' : ';').FirstOrDefault()?.Trim(); diff --git a/v2rayN/v2rayN/Common/QRCodeUtils.cs b/v2rayN/v2rayN/Common/QRCodeUtils.cs index 3013e45f..ef98aca8 100644 --- a/v2rayN/v2rayN/Common/QRCodeUtils.cs +++ b/v2rayN/v2rayN/Common/QRCodeUtils.cs @@ -32,8 +32,8 @@ public class QRCodeUtils { GetDpi(window, out var dpiX, out var dpiY); - var left = (int)(SystemParameters.WorkArea.Left); - var top = (int)(SystemParameters.WorkArea.Top); + var left = (int)SystemParameters.WorkArea.Left; + var top = (int)SystemParameters.WorkArea.Top; var width = (int)(SystemParameters.WorkArea.Width / dpiX); var height = (int)(SystemParameters.WorkArea.Height / dpiY); From c2c13ad3188014ddc5960c52cc5a8c1feaebb3e6 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sun, 21 Sep 2025 12:12:24 +0800 Subject: [PATCH 004/132] Create v2rayN.slnx https://github.com/2dust/v2rayN/pull/7969 --- v2rayN/v2rayN.slnx | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 v2rayN/v2rayN.slnx diff --git a/v2rayN/v2rayN.slnx b/v2rayN/v2rayN.slnx new file mode 100644 index 00000000..05c0ea96 --- /dev/null +++ b/v2rayN/v2rayN.slnx @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + From 534c7ab444b5ec95e0a222f34fbbb69154cb5e24 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sun, 21 Sep 2025 14:35:49 +0800 Subject: [PATCH 005/132] Optimize and improve QR code display --- v2rayN/ServiceLib/Common/QRCodeUtils.cs | 42 +++++++++++++++++-- v2rayN/v2rayN.Desktop/Views/QrcodeView.axaml | 16 ++++--- .../v2rayN.Desktop/Views/QrcodeView.axaml.cs | 12 +++++- v2rayN/v2rayN/Common/QRCodeUtils.cs | 3 +- v2rayN/v2rayN/Views/QrcodeView.xaml | 21 ++++++---- 5 files changed, 75 insertions(+), 19 deletions(-) diff --git a/v2rayN/ServiceLib/Common/QRCodeUtils.cs b/v2rayN/ServiceLib/Common/QRCodeUtils.cs index 3d3dc90b..d9015367 100644 --- a/v2rayN/ServiceLib/Common/QRCodeUtils.cs +++ b/v2rayN/ServiceLib/Common/QRCodeUtils.cs @@ -1,4 +1,5 @@ using QRCoder; +using QRCoder.Exceptions; using SkiaSharp; using ZXing.SkiaSharp; @@ -8,10 +9,45 @@ public class QRCodeUtils { public static byte[]? GenQRCode(string? url) { + if (url.IsNullOrEmpty()) + { + return null; + } using QRCodeGenerator qrGenerator = new(); - using var qrCodeData = qrGenerator.CreateQrCode(url ?? string.Empty, QRCodeGenerator.ECCLevel.Q); - using PngByteQRCode qrCode = new(qrCodeData); - return qrCode.GetGraphic(20); + DataTooLongException? lastDtle = null; + + var levels = new[] + { + QRCodeGenerator.ECCLevel.H, + QRCodeGenerator.ECCLevel.Q, + QRCodeGenerator.ECCLevel.M, + QRCodeGenerator.ECCLevel.L + }; + foreach (var level in levels) + { + try + { + using var qrCodeData = qrGenerator.CreateQrCode(url, level); + using PngByteQRCode qrCode = new(qrCodeData); + return qrCode.GetGraphic(20); + } + catch (DataTooLongException ex) + { + lastDtle = ex; + continue; + } + catch + { + throw; + } + } + + if (lastDtle != null) + { + throw lastDtle; + } + + return null; } public static string? ParseBarcode(string? fileName) diff --git a/v2rayN/v2rayN.Desktop/Views/QrcodeView.axaml b/v2rayN/v2rayN.Desktop/Views/QrcodeView.axaml index 90d94685..b67a7a1f 100644 --- a/v2rayN/v2rayN.Desktop/Views/QrcodeView.axaml +++ b/v2rayN/v2rayN.Desktop/Views/QrcodeView.axaml @@ -4,19 +4,25 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - d:DesignHeight="480" - d:DesignWidth="400" + xmlns:sys="clr-namespace:System;assembly=netstandard" + d:DesignHeight="600" + d:DesignWidth="600" mc:Ignorable="d"> + + + 500 + + + Width="{StaticResource QrcodeWidth}" + Height="{StaticResource QrcodeWidth}" /> + + 500 + + - + - \ No newline at end of file + From 3e1e23a52472fdb44338f3d7163e7661c57eae4a Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sun, 21 Sep 2025 14:48:54 +0800 Subject: [PATCH 006/132] Update Directory.Packages.props --- v2rayN/Directory.Packages.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v2rayN/Directory.Packages.props b/v2rayN/Directory.Packages.props index 6f8fadf2..71d906f7 100644 --- a/v2rayN/Directory.Packages.props +++ b/v2rayN/Directory.Packages.props @@ -18,8 +18,8 @@ - - + + From 18ac76e683eeeb3011353bee14ddbe4167e338cf Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sun, 21 Sep 2025 14:50:01 +0800 Subject: [PATCH 007/132] up 7.14.12 --- v2rayN/Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2rayN/Directory.Build.props b/v2rayN/Directory.Build.props index ee835a9a..c9f59177 100644 --- a/v2rayN/Directory.Build.props +++ b/v2rayN/Directory.Build.props @@ -1,7 +1,7 @@ - 7.14.11 + 7.14.12 From 27b45aee8312edda910b98460a73a00ecc8e778d Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Tue, 23 Sep 2025 11:39:55 +0800 Subject: [PATCH 008/132] Optimization and improvement, using event subscribers --- v2rayN/ServiceLib/Handler/AppEvents.cs | 4 ++++ .../ServiceLib/ViewModels/MainWindowViewModel.cs | 2 +- v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs | 14 ++++++++++++-- v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs | 2 +- v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml.cs | 2 -- v2rayN/v2rayN/Views/ProfilesView.xaml.cs | 2 -- 6 files changed, 18 insertions(+), 8 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/AppEvents.cs b/v2rayN/ServiceLib/Handler/AppEvents.cs index 109ee762..1cf3204b 100644 --- a/v2rayN/ServiceLib/Handler/AppEvents.cs +++ b/v2rayN/ServiceLib/Handler/AppEvents.cs @@ -7,6 +7,8 @@ public static class AppEvents { public static readonly Subject ProfilesRefreshRequested = new(); + public static readonly Subject SubscriptionsRefreshRequested = new(); + public static readonly Subject SendSnackMsgRequested = new(); public static readonly Subject SendMsgViewRequested = new(); @@ -18,4 +20,6 @@ public static class AppEvents public static readonly Subject AdjustMainLvColWidthRequested = new(); public static readonly Subject DispatcherStatisticsRequested = new(); + + public static readonly Subject SetDefaultServerRequested = new(); } diff --git a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs index 4e9fc9fc..4bc9997d 100644 --- a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs @@ -301,7 +301,7 @@ public class MainWindowViewModel : MyReactiveObject private void RefreshSubscriptions() { - Locator.Current.GetService()?.RefreshSubscriptions(); + AppEvents.SubscriptionsRefreshRequested.OnNext(Unit.Default); } #endregion Servers && Groups diff --git a/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs b/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs index 4c0fd2b9..87e1d78f 100644 --- a/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs @@ -240,11 +240,21 @@ public class ProfilesViewModel : MyReactiveObject .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(async _ => await RefreshServersBiz()); + AppEvents.SubscriptionsRefreshRequested + .AsObservable() + .ObserveOn(RxApp.MainThreadScheduler) + .Subscribe(async _ => await RefreshSubscriptions()); + AppEvents.DispatcherStatisticsRequested .AsObservable() .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(async result => await UpdateStatistics(result)); + AppEvents.SetDefaultServerRequested + .AsObservable() + .ObserveOn(RxApp.MainThreadScheduler) + .Subscribe(async indexId => await SetDefaultServer(indexId)); + #endregion AppEvents _ = Init(); @@ -380,7 +390,7 @@ public class ProfilesViewModel : MyReactiveObject await _updateView?.Invoke(EViewAction.DispatcherRefreshServersBiz, null); } - public async Task RefreshSubscriptions() + private async Task RefreshSubscriptions() { SubItems.Clear(); @@ -565,7 +575,7 @@ public class ProfilesViewModel : MyReactiveObject await SetDefaultServer(SelectedProfile.IndexId); } - public async Task SetDefaultServer(string? indexId) + private async Task SetDefaultServer(string? indexId) { if (indexId.IsNullOrEmpty()) { diff --git a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs index e9ee033e..e51f9418 100644 --- a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs @@ -329,7 +329,7 @@ public class StatusBarViewModel : MyReactiveObject { return; } - Locator.Current.GetService()?.SetDefaultServer(SelectedServer.ID); + AppEvents.SetDefaultServerRequested.OnNext(SelectedServer.ID); } public async Task TestServerAvailability() diff --git a/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml.cs index 5bade399..88763851 100644 --- a/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml.cs @@ -8,7 +8,6 @@ using Avalonia.Threading; using DialogHostAvalonia; using MsBox.Avalonia.Enums; using ReactiveUI; -using Splat; using v2rayN.Desktop.Common; namespace v2rayN.Desktop.Views; @@ -48,7 +47,6 @@ public partial class ProfilesView : ReactiveUserControl //} ViewModel = new ProfilesViewModel(UpdateViewHandler); - Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(ProfilesViewModel)); this.WhenActivated(disposables => { diff --git a/v2rayN/v2rayN/Views/ProfilesView.xaml.cs b/v2rayN/v2rayN/Views/ProfilesView.xaml.cs index 8ef236ab..3c46966c 100644 --- a/v2rayN/v2rayN/Views/ProfilesView.xaml.cs +++ b/v2rayN/v2rayN/Views/ProfilesView.xaml.cs @@ -8,7 +8,6 @@ using System.Windows.Media; using System.Windows.Threading; using MaterialDesignThemes.Wpf; using ReactiveUI; -using Splat; using v2rayN.Base; using Point = System.Windows.Point; @@ -42,7 +41,6 @@ public partial class ProfilesView } ViewModel = new ProfilesViewModel(UpdateViewHandler); - Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(ProfilesViewModel)); this.WhenActivated(disposables => { From 721d70c8c76e3e64bfebeeacc79540cb2d229b28 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Tue, 23 Sep 2025 11:39:57 +0800 Subject: [PATCH 009/132] Update Directory.Packages.props --- v2rayN/Directory.Packages.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v2rayN/Directory.Packages.props b/v2rayN/Directory.Packages.props index 71d906f7..283e95a6 100644 --- a/v2rayN/Directory.Packages.props +++ b/v2rayN/Directory.Packages.props @@ -20,11 +20,11 @@ - + - \ No newline at end of file + From 6929886b3e49ce079917c3e962460d82c2f97dca Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Tue, 23 Sep 2025 12:08:43 +0800 Subject: [PATCH 010/132] Optimization and improvement, using event subscribers --- v2rayN/ServiceLib/Handler/AppEvents.cs | 2 ++ .../ViewModels/ClashProxiesViewModel.cs | 13 +++++++++++ .../ViewModels/MainWindowViewModel.cs | 22 +++++++++---------- .../Views/ClashProxiesView.axaml.cs | 2 -- v2rayN/v2rayN/Views/ClashProxiesView.xaml.cs | 2 -- 5 files changed, 25 insertions(+), 16 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/AppEvents.cs b/v2rayN/ServiceLib/Handler/AppEvents.cs index 1cf3204b..ccc4e5cb 100644 --- a/v2rayN/ServiceLib/Handler/AppEvents.cs +++ b/v2rayN/ServiceLib/Handler/AppEvents.cs @@ -9,6 +9,8 @@ public static class AppEvents public static readonly Subject SubscriptionsRefreshRequested = new(); + public static readonly Subject ProxiesReloadRequested = new(); + public static readonly Subject SendSnackMsgRequested = new(); public static readonly Subject SendMsgViewRequested = new(); diff --git a/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs b/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs index 343d49df..bf999d12 100644 --- a/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs @@ -69,6 +69,8 @@ public class ClashProxiesViewModel : MyReactiveObject SortingSelected = _config.ClashUIItem.ProxiesSorting; RuleModeSelected = (int)_config.ClashUIItem.RuleMode; + #region WhenAnyValue && ReactiveCommand + this.WhenAnyValue( x => x.SelectedGroup, y => y != null && y.Name.IsNotEmpty()) @@ -89,6 +91,17 @@ public class ClashProxiesViewModel : MyReactiveObject y => y == true) .Subscribe(c => { _config.ClashUIItem.ProxiesAutoRefresh = AutoRefresh; }); + #endregion WhenAnyValue && ReactiveCommand + + #region AppEvents + + AppEvents.ProxiesReloadRequested + .AsObservable() + .ObserveOn(RxApp.MainThreadScheduler) + .Subscribe(async _ => await ProxiesReload()); + + #endregion AppEvents + _ = Init(); } diff --git a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs index 4bc9997d..249a4322 100644 --- a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs @@ -520,7 +520,13 @@ public class MainWindowViewModel : MyReactiveObject }); Locator.Current.GetService()?.TestServerAvailability(); - RxApp.MainThreadScheduler.Schedule(() => _ = ReloadResult()); + var showClashUI = _config.IsRunningCore(ECoreType.sing_box); + if (showClashUI) + { + AppEvents.ProxiesReloadRequested.OnNext(Unit.Default); + } + + RxApp.MainThreadScheduler.Schedule(() => ReloadResult(showClashUI)); BlReloadEnabled = true; if (_hasNextReloadJob) @@ -530,19 +536,11 @@ public class MainWindowViewModel : MyReactiveObject } } - public async Task ReloadResult() + private void ReloadResult(bool showClashUI) { // BlReloadEnabled = true; - //Locator.Current.GetService()?.ChangeSystemProxyAsync(_config.systemProxyItem.sysProxyType, false); - ShowClashUI = _config.IsRunningCore(ECoreType.sing_box); - if (ShowClashUI) - { - Locator.Current.GetService()?.ProxiesReload(); - } - else - { - TabMainSelectedIndex = 0; - } + ShowClashUI = showClashUI; + TabMainSelectedIndex = showClashUI ? TabMainSelectedIndex : 0; } private async Task LoadCore() diff --git a/v2rayN/v2rayN.Desktop/Views/ClashProxiesView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/ClashProxiesView.axaml.cs index 754dd2a1..c8e7aeba 100644 --- a/v2rayN/v2rayN.Desktop/Views/ClashProxiesView.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/ClashProxiesView.axaml.cs @@ -3,7 +3,6 @@ using Avalonia.Input; using Avalonia.ReactiveUI; using DynamicData; using ReactiveUI; -using Splat; namespace v2rayN.Desktop.Views; @@ -13,7 +12,6 @@ public partial class ClashProxiesView : ReactiveUserControl ViewModel, typeof(ClashProxiesViewModel)); lstProxyDetails.DoubleTapped += LstProxyDetails_DoubleTapped; this.KeyDown += ClashProxiesView_KeyDown; diff --git a/v2rayN/v2rayN/Views/ClashProxiesView.xaml.cs b/v2rayN/v2rayN/Views/ClashProxiesView.xaml.cs index eff95de9..23943da1 100644 --- a/v2rayN/v2rayN/Views/ClashProxiesView.xaml.cs +++ b/v2rayN/v2rayN/Views/ClashProxiesView.xaml.cs @@ -1,7 +1,6 @@ using System.Reactive.Disposables; using System.Windows.Input; using ReactiveUI; -using Splat; namespace v2rayN.Views; @@ -14,7 +13,6 @@ public partial class ClashProxiesView { InitializeComponent(); ViewModel = new ClashProxiesViewModel(UpdateViewHandler); - Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(ClashProxiesViewModel)); lstProxyDetails.PreviewMouseDoubleClick += lstProxyDetails_PreviewMouseDoubleClick; this.WhenActivated(disposables => From 0377e7ce19f60200caee10909f5a506623bfa4e8 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Tue, 23 Sep 2025 14:27:42 +0800 Subject: [PATCH 011/132] Optimization and improvement, using event subscribers --- v2rayN/ServiceLib/Handler/AppEvents.cs | 14 +++++----- .../ViewModels/MainWindowViewModel.cs | 10 +++---- .../ViewModels/StatusBarViewModel.cs | 28 ++++++++++++++++--- .../v2rayN.Desktop/Views/MainWindow.axaml.cs | 2 +- v2rayN/v2rayN/Views/MainWindow.xaml.cs | 8 ++---- v2rayN/v2rayN/Views/StatusBarView.xaml.cs | 2 -- 6 files changed, 38 insertions(+), 26 deletions(-) diff --git a/v2rayN/ServiceLib/Handler/AppEvents.cs b/v2rayN/ServiceLib/Handler/AppEvents.cs index ccc4e5cb..a4c2917d 100644 --- a/v2rayN/ServiceLib/Handler/AppEvents.cs +++ b/v2rayN/ServiceLib/Handler/AppEvents.cs @@ -6,22 +6,22 @@ namespace ServiceLib.Handler; public static class AppEvents { public static readonly Subject ProfilesRefreshRequested = new(); - public static readonly Subject SubscriptionsRefreshRequested = new(); - - public static readonly Subject ProxiesReloadRequested = new(); + public static readonly Subject ProxiesReloadRequested = new(); + public static readonly Subject DispatcherStatisticsRequested = new(); public static readonly Subject SendSnackMsgRequested = new(); - public static readonly Subject SendMsgViewRequested = new(); public static readonly Subject AppExitRequested = new(); - public static readonly Subject ShutdownRequested = new(); public static readonly Subject AdjustMainLvColWidthRequested = new(); - public static readonly Subject DispatcherStatisticsRequested = new(); - public static readonly Subject SetDefaultServerRequested = new(); + + public static readonly Subject RoutingsMenuRefreshRequested = new(); + public static readonly Subject TestServerRequested = new(); + public static readonly Subject InboundDisplayRequested = new(); + public static readonly Subject SysProxyChangeRequested = new(); } diff --git a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs index 249a4322..3492670c 100644 --- a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs @@ -2,7 +2,6 @@ using System.Reactive; using System.Reactive.Concurrency; using ReactiveUI; using ReactiveUI.Fody.Helpers; -using Splat; namespace ServiceLib.ViewModels; @@ -240,7 +239,6 @@ public class MainWindowViewModel : MyReactiveObject BlReloadEnabled = true; await Reload(); await AutoHideStartup(); - Locator.Current.GetService()?.RefreshRoutingsMenu(); } #endregion Init @@ -433,7 +431,7 @@ public class MainWindowViewModel : MyReactiveObject var ret = await _updateView?.Invoke(EViewAction.OptionSettingWindow, null); if (ret == true) { - Locator.Current.GetService()?.InboundDisplayStatus(); + AppEvents.InboundDisplayRequested.OnNext(Unit.Default); await Reload(); } } @@ -444,7 +442,7 @@ public class MainWindowViewModel : MyReactiveObject if (ret == true) { await ConfigHandler.InitBuiltinRouting(_config); - Locator.Current.GetService()?.RefreshRoutingsMenu(); + AppEvents.RoutingsMenuRefreshRequested.OnNext(Unit.Default); await Reload(); } } @@ -518,7 +516,7 @@ public class MainWindowViewModel : MyReactiveObject await SysProxyHandler.UpdateSysProxy(_config, false); await Task.Delay(1000); }); - Locator.Current.GetService()?.TestServerAvailability(); + AppEvents.TestServerRequested.OnNext(Unit.Default); var showClashUI = _config.IsRunningCore(ECoreType.sing_box); if (showClashUI) @@ -572,7 +570,7 @@ public class MainWindowViewModel : MyReactiveObject { await ConfigHandler.ApplyRegionalPreset(_config, type); await ConfigHandler.InitRouting(_config); - Locator.Current.GetService()?.RefreshRoutingsMenu(); + AppEvents.RoutingsMenuRefreshRequested.OnNext(Unit.Default); await ConfigHandler.SaveConfig(_config); await new UpdateService().UpdateGeoFileAll(_config, UpdateTaskHandler); diff --git a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs index e51f9418..9465e6a2 100644 --- a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs @@ -209,6 +209,26 @@ public class StatusBarViewModel : MyReactiveObject .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(async result => await UpdateStatistics(result)); + AppEvents.RoutingsMenuRefreshRequested + .AsObservable() + .ObserveOn(RxApp.MainThreadScheduler) + .Subscribe(async _ => await RefreshRoutingsMenu()); + + AppEvents.TestServerRequested + .AsObservable() + .ObserveOn(RxApp.MainThreadScheduler) + .Subscribe(async _ => await TestServerAvailability()); + + AppEvents.InboundDisplayRequested + .AsObservable() + .ObserveOn(RxApp.MainThreadScheduler) + .Subscribe(async _ => await InboundDisplayStatus()); + + AppEvents.SysProxyChangeRequested + .AsObservable() + .ObserveOn(RxApp.MainThreadScheduler) + .Subscribe(async result => await SetListenerType(result)); + #endregion AppEvents _ = Init(); @@ -329,7 +349,7 @@ public class StatusBarViewModel : MyReactiveObject { return; } - AppEvents.SetDefaultServerRequested.OnNext(SelectedServer.ID); + AppEvents.SetDefaultServerRequested.OnNext(SelectedServer.ID); } public async Task TestServerAvailability() @@ -364,7 +384,7 @@ public class StatusBarViewModel : MyReactiveObject #region System proxy and Routings - public async Task SetListenerType(ESysProxyType type) + private async Task SetListenerType(ESysProxyType type) { if (_config.SystemProxyItem.SysProxyType == type) { @@ -393,7 +413,7 @@ public class StatusBarViewModel : MyReactiveObject } } - public async Task RefreshRoutingsMenu() + private async Task RefreshRoutingsMenu() { RoutingItems.Clear(); @@ -501,7 +521,7 @@ public class StatusBarViewModel : MyReactiveObject #region UI - public async Task InboundDisplayStatus() + private async Task InboundDisplayStatus() { StringBuilder sb = new(); sb.Append($"[{EInboundProtocol.mixed}:{AppManager.Instance.GetLocalPort(EInboundProtocol.socks)}"); diff --git a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs index d72c37a7..532bda6f 100644 --- a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs @@ -259,7 +259,7 @@ public partial class MainWindow : WindowBase case EGlobalHotkey.SystemProxySet: case EGlobalHotkey.SystemProxyUnchanged: case EGlobalHotkey.SystemProxyPac: - Locator.Current.GetService()?.SetListenerType((ESysProxyType)((int)e - 1)); + AppEvents.SysProxyChangeRequested.OnNext((ESysProxyType)((int)e - 1)); break; } } diff --git a/v2rayN/v2rayN/Views/MainWindow.xaml.cs b/v2rayN/v2rayN/Views/MainWindow.xaml.cs index a804775e..28073be8 100644 --- a/v2rayN/v2rayN/Views/MainWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/MainWindow.xaml.cs @@ -249,7 +249,7 @@ public partial class MainWindow case EGlobalHotkey.SystemProxySet: case EGlobalHotkey.SystemProxyUnchanged: case EGlobalHotkey.SystemProxyPac: - Locator.Current.GetService()?.SetListenerType((ESysProxyType)((int)e - 1)); + AppEvents.SysProxyChangeRequested.OnNext((ESysProxyType)((int)e - 1)); break; } } @@ -287,11 +287,7 @@ public partial class MainWindow var clipboardData = WindowsUtils.GetClipboardData(); if (clipboardData.IsNotEmpty()) { - var service = Locator.Current.GetService(); - if (service != null) - { - _ = service.AddServerViaClipboardAsync(clipboardData); - } + ViewModel?.AddServerViaClipboardAsync(clipboardData); } break; diff --git a/v2rayN/v2rayN/Views/StatusBarView.xaml.cs b/v2rayN/v2rayN/Views/StatusBarView.xaml.cs index 8a3a5df4..f9b551c2 100644 --- a/v2rayN/v2rayN/Views/StatusBarView.xaml.cs +++ b/v2rayN/v2rayN/Views/StatusBarView.xaml.cs @@ -3,7 +3,6 @@ using System.Windows; using System.Windows.Input; using System.Windows.Threading; using ReactiveUI; -using Splat; using v2rayN.Manager; namespace v2rayN.Views; @@ -17,7 +16,6 @@ public partial class StatusBarView InitializeComponent(); _config = AppManager.Instance.Config; ViewModel = new StatusBarViewModel(UpdateViewHandler); - Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(StatusBarViewModel)); menuExit.Click += menuExit_Click; txtRunningServerDisplay.PreviewMouseDown += txtRunningInfoDisplay_MouseDoubleClick; From e96a4818c47a2d7b707ba7570720c8f7ef67c9df Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Tue, 23 Sep 2025 15:31:19 +0800 Subject: [PATCH 012/132] Optimization and improvement --- v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs | 3 +++ v2rayN/v2rayN.Desktop/App.axaml.cs | 4 +--- v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs | 2 +- v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs | 6 ++---- v2rayN/v2rayN/Views/StatusBarView.xaml.cs | 3 ++- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs index 9465e6a2..ee66351c 100644 --- a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs @@ -11,6 +11,9 @@ namespace ServiceLib.ViewModels; public class StatusBarViewModel : MyReactiveObject { + private static readonly Lazy _instance = new(() => new(null)); + public static StatusBarViewModel Instance => _instance.Value; + #region ObservableCollection public IObservableCollection RoutingItems { get; } = new ObservableCollectionExtended(); diff --git a/v2rayN/v2rayN.Desktop/App.axaml.cs b/v2rayN/v2rayN.Desktop/App.axaml.cs index aa357ca4..515354d5 100644 --- a/v2rayN/v2rayN.Desktop/App.axaml.cs +++ b/v2rayN/v2rayN.Desktop/App.axaml.cs @@ -16,9 +16,7 @@ public partial class App : Application AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; - var ViewModel = new StatusBarViewModel(null); - Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(StatusBarViewModel)); - DataContext = ViewModel; + DataContext = StatusBarViewModel.Instance; } public override void OnFrameworkInitializationCompleted() diff --git a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs index 532bda6f..eb884e4b 100644 --- a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs @@ -20,7 +20,7 @@ namespace v2rayN.Desktop.Views; public partial class MainWindow : WindowBase { private static Config _config; - private WindowNotificationManager? _manager; + private readonly WindowNotificationManager? _manager; private CheckUpdateView? _checkUpdateView; private BackupAndRestoreView? _backupAndRestoreView; private bool _blCloseByUser = false; diff --git a/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs index 3eb08f96..976c156d 100644 --- a/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs @@ -6,7 +6,6 @@ using Avalonia.ReactiveUI; using Avalonia.Threading; using DialogHostAvalonia; using ReactiveUI; -using Splat; using v2rayN.Desktop.Common; namespace v2rayN.Desktop.Views; @@ -20,9 +19,8 @@ public partial class StatusBarView : ReactiveUserControl InitializeComponent(); _config = AppManager.Instance.Config; - //ViewModel = new StatusBarViewModel(UpdateViewHandler); - //Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(StatusBarViewModel)); - ViewModel = Locator.Current.GetService(); + + ViewModel = StatusBarViewModel.Instance; ViewModel?.InitUpdateView(UpdateViewHandler); txtRunningServerDisplay.Tapped += TxtRunningServerDisplay_Tapped; diff --git a/v2rayN/v2rayN/Views/StatusBarView.xaml.cs b/v2rayN/v2rayN/Views/StatusBarView.xaml.cs index f9b551c2..1d80a694 100644 --- a/v2rayN/v2rayN/Views/StatusBarView.xaml.cs +++ b/v2rayN/v2rayN/Views/StatusBarView.xaml.cs @@ -15,7 +15,8 @@ public partial class StatusBarView { InitializeComponent(); _config = AppManager.Instance.Config; - ViewModel = new StatusBarViewModel(UpdateViewHandler); + ViewModel = StatusBarViewModel.Instance; + ViewModel?.InitUpdateView(UpdateViewHandler); menuExit.Click += menuExit_Click; txtRunningServerDisplay.PreviewMouseDown += txtRunningInfoDisplay_MouseDoubleClick; From 671678724b3cc2186f9d2370350bbb80dcdb0b6c Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 24 Sep 2025 10:57:06 +0800 Subject: [PATCH 013/132] Optimization and improvement, using event subscribers --- v2rayN/ServiceLib/Enums/EViewAction.cs | 1 - v2rayN/ServiceLib/Handler/AppEvents.cs | 6 +++ v2rayN/ServiceLib/Manager/AppManager.cs | 6 +++ .../ViewModels/CheckUpdateViewModel.cs | 6 +-- .../ViewModels/MainWindowViewModel.cs | 46 +++++++++++-------- .../ViewModels/ProfilesViewModel.cs | 3 +- .../ViewModels/StatusBarViewModel.cs | 28 +++++------ v2rayN/v2rayN.Desktop/App.axaml.cs | 15 ++---- .../v2rayN.Desktop/Views/MainWindow.axaml.cs | 35 +++++++------- v2rayN/v2rayN/Views/MainWindow.xaml.cs | 46 +++++++++---------- 10 files changed, 95 insertions(+), 97 deletions(-) diff --git a/v2rayN/ServiceLib/Enums/EViewAction.cs b/v2rayN/ServiceLib/Enums/EViewAction.cs index 2c47d31d..075439fd 100644 --- a/v2rayN/ServiceLib/Enums/EViewAction.cs +++ b/v2rayN/ServiceLib/Enums/EViewAction.cs @@ -12,7 +12,6 @@ public enum EViewAction ProfilesFocus, ShareSub, ShareServer, - ShowHideWindow, ScanScreenTask, ScanImageTask, BrowseServer, diff --git a/v2rayN/ServiceLib/Handler/AppEvents.cs b/v2rayN/ServiceLib/Handler/AppEvents.cs index a4c2917d..bda78922 100644 --- a/v2rayN/ServiceLib/Handler/AppEvents.cs +++ b/v2rayN/ServiceLib/Handler/AppEvents.cs @@ -5,6 +5,12 @@ namespace ServiceLib.Handler; public static class AppEvents { + public static readonly Subject ReloadRequested = new(); + public static readonly Subject ShowHideWindowRequested = new(); + public static readonly Subject AddServerViaScanRequested = new(); + public static readonly Subject AddServerViaClipboardRequested = new(); + public static readonly Subject SubscriptionsUpdateRequested = new(); + public static readonly Subject ProfilesRefreshRequested = new(); public static readonly Subject SubscriptionsRefreshRequested = new(); public static readonly Subject ProxiesReloadRequested = new(); diff --git a/v2rayN/ServiceLib/Manager/AppManager.cs b/v2rayN/ServiceLib/Manager/AppManager.cs index 33125289..57238b15 100644 --- a/v2rayN/ServiceLib/Manager/AppManager.cs +++ b/v2rayN/ServiceLib/Manager/AppManager.cs @@ -122,6 +122,12 @@ public sealed class AppManager AppEvents.ShutdownRequested.OnNext(byUser); } + public async Task RebootAsAdmin() + { + ProcUtils.RebootAsAdmin(); + await AppManager.Instance.AppExitAsync(true); + } + #endregion App #region Config diff --git a/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs b/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs index 5ec1f80b..171cb467 100644 --- a/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs @@ -2,11 +2,9 @@ using System.Reactive; using System.Reactive.Disposables; using System.Reactive.Linq; using System.Runtime.InteropServices; -using DynamicData; using DynamicData.Binding; using ReactiveUI; using ReactiveUI.Fody.Helpers; -using Splat; namespace ServiceLib.ViewModels; @@ -225,11 +223,11 @@ public class CheckUpdateViewModel : MyReactiveObject { if (blReload) { - Locator.Current.GetService()?.Reload(); + AppEvents.ReloadRequested.OnNext(Unit.Default); } else { - Locator.Current.GetService()?.CloseCore(); + await CoreManager.Instance.CoreStop(); } } diff --git a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs index 3492670c..ba5de790 100644 --- a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs @@ -1,5 +1,6 @@ using System.Reactive; using System.Reactive.Concurrency; +using System.Reactive.Linq; using ReactiveUI; using ReactiveUI.Fody.Helpers; @@ -183,7 +184,7 @@ public class MainWindowViewModel : MyReactiveObject }); RebootAsAdminCmd = ReactiveCommand.CreateFromTask(async () => { - await RebootAsAdmin(); + await AppManager.Instance.RebootAsAdmin(); }); ClearServerStatisticsCmd = ReactiveCommand.CreateFromTask(async () => { @@ -216,6 +217,30 @@ public class MainWindowViewModel : MyReactiveObject #endregion WhenAnyValue && ReactiveCommand + #region AppEvents + + AppEvents.ReloadRequested + .AsObservable() + .ObserveOn(RxApp.MainThreadScheduler) + .Subscribe(async _ => await Reload()); + + AppEvents.AddServerViaScanRequested + .AsObservable() + .ObserveOn(RxApp.MainThreadScheduler) + .Subscribe(async _ => await AddServerViaScanAsync()); + + AppEvents.AddServerViaClipboardRequested + .AsObservable() + .ObserveOn(RxApp.MainThreadScheduler) + .Subscribe(async _ => await AddServerViaClipboardAsync(null)); + + AppEvents.SubscriptionsUpdateRequested + .AsObservable() + .ObserveOn(RxApp.MainThreadScheduler) + .Subscribe(async blProxy => await UpdateSubscriptionProcess("", blProxy)); + + #endregion AppEvents + _ = Init(); } @@ -281,11 +306,6 @@ public class MainWindowViewModel : MyReactiveObject AppEvents.DispatcherStatisticsRequested.OnNext(update); } - public void ShowHideWindow(bool? blShow) - { - _updateView?.Invoke(EViewAction.ShowHideWindow, blShow); - } - #endregion Actions #region Servers && Groups @@ -465,12 +485,6 @@ public class MainWindowViewModel : MyReactiveObject } } - public async Task RebootAsAdmin() - { - ProcUtils.RebootAsAdmin(); - await AppManager.Instance.AppExitAsync(true); - } - private async Task ClearServerStatistics() { await StatisticsManager.Instance.ClearAllServerStatistics(); @@ -547,17 +561,11 @@ public class MainWindowViewModel : MyReactiveObject await CoreManager.Instance.LoadCore(node); } - public async Task CloseCore() - { - await ConfigHandler.SaveConfig(_config); - await CoreManager.Instance.CoreStop(); - } - private async Task AutoHideStartup() { if (_config.UiItem.AutoHideStartup) { - ShowHideWindow(false); + AppEvents.ShowHideWindowRequested.OnNext(false); } await Task.CompletedTask; } diff --git a/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs b/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs index 87e1d78f..fcf3450d 100644 --- a/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs @@ -6,7 +6,6 @@ using DynamicData; using DynamicData.Binding; using ReactiveUI; using ReactiveUI.Fody.Helpers; -using Splat; namespace ServiceLib.ViewModels; @@ -276,7 +275,7 @@ public class ProfilesViewModel : MyReactiveObject private void Reload() { - Locator.Current.GetService()?.Reload(); + AppEvents.ReloadRequested.OnNext(Unit.Default); } public async Task SetSpeedTestResult(SpeedTestResult result) diff --git a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs index ee66351c..6dc795ef 100644 --- a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs @@ -5,7 +5,6 @@ using System.Text; using DynamicData.Binding; using ReactiveUI; using ReactiveUI.Fody.Helpers; -using Splat; namespace ServiceLib.ViewModels; @@ -149,17 +148,17 @@ public class StatusBarViewModel : MyReactiveObject NotifyLeftClickCmd = ReactiveCommand.CreateFromTask(async () => { - Locator.Current.GetService()?.ShowHideWindow(null); + AppEvents.ShowHideWindowRequested.OnNext(null); await Task.CompletedTask; }); ShowWindowCmd = ReactiveCommand.CreateFromTask(async () => { - Locator.Current.GetService()?.ShowHideWindow(true); + AppEvents.ShowHideWindowRequested.OnNext(true); await Task.CompletedTask; }); HideWindowCmd = ReactiveCommand.CreateFromTask(async () => { - Locator.Current.GetService()?.ShowHideWindow(false); + AppEvents.ShowHideWindowRequested.OnNext(false); await Task.CompletedTask; }); @@ -275,23 +274,20 @@ public class StatusBarViewModel : MyReactiveObject private async Task AddServerViaClipboard() { - var service = Locator.Current.GetService(); - if (service != null) - await service.AddServerViaClipboardAsync(null); + AppEvents.AddServerViaClipboardRequested.OnNext(Unit.Default); + await Task.Delay(1000); } private async Task AddServerViaScan() { - var service = Locator.Current.GetService(); - if (service != null) - await service.AddServerViaScanAsync(); + AppEvents.AddServerViaScanRequested.OnNext(Unit.Default); + await Task.Delay(1000); } private async Task UpdateSubscriptionProcess(bool blProxy) { - var service = Locator.Current.GetService(); - if (service != null) - await service.UpdateSubscriptionProcess("", blProxy); + AppEvents.SubscriptionsUpdateRequested.OnNext(blProxy); + await Task.Delay(1000); } private async Task RefreshServersBiz() @@ -453,7 +449,7 @@ public class StatusBarViewModel : MyReactiveObject if (await ConfigHandler.SetDefaultRouting(_config, item) == 0) { NoticeManager.Instance.SendMessageEx(ResUI.TipChangeRouting); - Locator.Current.GetService()?.Reload(); + AppEvents.ReloadRequested.OnNext(Unit.Default); _updateView?.Invoke(EViewAction.DispatcherRefreshIcon, null); } } @@ -486,7 +482,7 @@ public class StatusBarViewModel : MyReactiveObject if (Utils.IsWindows()) { _config.TunModeItem.EnableTun = false; - Locator.Current.GetService()?.RebootAsAdmin(); + await AppManager.Instance.RebootAsAdmin(); return; } else @@ -500,7 +496,7 @@ public class StatusBarViewModel : MyReactiveObject } } await ConfigHandler.SaveConfig(_config); - Locator.Current.GetService()?.Reload(); + AppEvents.ReloadRequested.OnNext(Unit.Default); } private bool AllowEnableTun() diff --git a/v2rayN/v2rayN.Desktop/App.axaml.cs b/v2rayN/v2rayN.Desktop/App.axaml.cs index 515354d5..fa4779cf 100644 --- a/v2rayN/v2rayN.Desktop/App.axaml.cs +++ b/v2rayN/v2rayN.Desktop/App.axaml.cs @@ -1,8 +1,7 @@ +using System.Reactive; using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; -using Splat; -using v2rayN.Desktop.Common; using v2rayN.Desktop.Views; namespace v2rayN.Desktop; @@ -55,16 +54,8 @@ public partial class App : Application { if (desktop.MainWindow != null) { - var clipboardData = await AvaUtils.GetClipboardData(desktop.MainWindow); - if (clipboardData.IsNullOrEmpty()) - { - return; - } - var service = Locator.Current.GetService(); - if (service != null) - { - _ = service.AddServerViaClipboardAsync(clipboardData); - } + AppEvents.AddServerViaClipboardRequested.OnNext(Unit.Default); + await Task.Delay(1000); } } } diff --git a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs index eb884e4b..86bd5d7b 100644 --- a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs @@ -10,7 +10,6 @@ using Avalonia.Threading; using DialogHostAvalonia; using MsBox.Avalonia.Enums; using ReactiveUI; -using Splat; using v2rayN.Desktop.Base; using v2rayN.Desktop.Common; using v2rayN.Desktop.Manager; @@ -40,7 +39,6 @@ public partial class MainWindow : WindowBase menuClose.Click += MenuClose_Click; ViewModel = new MainWindowViewModel(UpdateViewHandler); - Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel)); switch (_config.UiItem.MainGirdOrientation) { @@ -153,6 +151,12 @@ public partial class MainWindow : WindowBase .ObserveOn(RxApp.MainThreadScheduler) .Subscribe(content => Shutdown(content)) .DisposeWith(disposables); + + AppEvents.ShowHideWindowRequested + .AsObservable() + .ObserveOn(RxApp.MainThreadScheduler) + .Subscribe(blShow => ShowHideWindow(blShow)) + .DisposeWith(disposables); }); if (Utils.IsWindows()) @@ -221,12 +225,6 @@ public partial class MainWindow : WindowBase case EViewAction.SubSettingWindow: return await new SubSettingWindow().ShowDialog(this); - case EViewAction.ShowHideWindow: - Dispatcher.UIThread.Post(() => - ShowHideWindow((bool?)obj), - DispatcherPriority.Default); - break; - case EViewAction.ScanScreenTask: await ScanScreenTaskAsync(); break; @@ -236,11 +234,7 @@ public partial class MainWindow : WindowBase break; case EViewAction.AddServerViaClipboard: - var clipboardData = await AvaUtils.GetClipboardData(this); - if (clipboardData.IsNotEmpty() && ViewModel != null) - { - await ViewModel.AddServerViaClipboardAsync(clipboardData); - } + await AddServerViaClipboardAsync(); break; } @@ -295,11 +289,7 @@ public partial class MainWindow : WindowBase switch (e.Key) { case Key.V: - var clipboardData = await AvaUtils.GetClipboardData(this); - if (clipboardData.IsNotEmpty() && ViewModel != null) - { - await ViewModel.AddServerViaClipboardAsync(clipboardData); - } + await AddServerViaClipboardAsync(); break; case Key.S: @@ -326,6 +316,15 @@ public partial class MainWindow : WindowBase ProcUtils.ProcessStart(Utils.GetBinPath("EnableLoopback.exe")); } + public async Task AddServerViaClipboardAsync() + { + var clipboardData = await AvaUtils.GetClipboardData(this); + if (clipboardData.IsNotEmpty() && ViewModel != null) + { + await ViewModel.AddServerViaClipboardAsync(clipboardData); + } + } + public async Task ScanScreenTaskAsync() { //ShowHideWindow(false); diff --git a/v2rayN/v2rayN/Views/MainWindow.xaml.cs b/v2rayN/v2rayN/Views/MainWindow.xaml.cs index 28073be8..1d0d530c 100644 --- a/v2rayN/v2rayN/Views/MainWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/MainWindow.xaml.cs @@ -6,10 +6,8 @@ using System.Windows.Controls; using System.Windows.Input; using System.Windows.Interop; using System.Windows.Media; -using System.Windows.Threading; using MaterialDesignThemes.Wpf; using ReactiveUI; -using Splat; using v2rayN.Manager; namespace v2rayN.Views; @@ -37,7 +35,6 @@ public partial class MainWindow menuBackupAndRestore.Click += MenuBackupAndRestore_Click; ViewModel = new MainWindowViewModel(UpdateViewHandler); - Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel)); switch (_config.UiItem.MainGirdOrientation) { @@ -146,10 +143,16 @@ public partial class MainWindow .DisposeWith(disposables); AppEvents.ShutdownRequested - .AsObservable() - .ObserveOn(RxApp.MainThreadScheduler) - .Subscribe(content => Shutdown(content)) - .DisposeWith(disposables); + .AsObservable() + .ObserveOn(RxApp.MainThreadScheduler) + .Subscribe(content => Shutdown(content)) + .DisposeWith(disposables); + + AppEvents.ShowHideWindowRequested + .AsObservable() + .ObserveOn(RxApp.MainThreadScheduler) + .Subscribe(blShow => ShowHideWindow(blShow)) + .DisposeWith(disposables); }); this.Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}"; @@ -210,13 +213,6 @@ public partial class MainWindow case EViewAction.SubSettingWindow: return (new SubSettingWindow().ShowDialog() ?? false); - case EViewAction.ShowHideWindow: - Application.Current?.Dispatcher.Invoke((() => - { - ShowHideWindow((bool?)obj); - }), DispatcherPriority.Normal); - break; - case EViewAction.ScanScreenTask: await ScanScreenTaskAsync(); break; @@ -226,11 +222,7 @@ public partial class MainWindow break; case EViewAction.AddServerViaClipboard: - var clipboardData = WindowsUtils.GetClipboardData(); - if (clipboardData.IsNotEmpty()) - { - ViewModel?.AddServerViaClipboardAsync(clipboardData); - } + await AddServerViaClipboardAsync(); break; } @@ -283,12 +275,7 @@ public partial class MainWindow { return; } - - var clipboardData = WindowsUtils.GetClipboardData(); - if (clipboardData.IsNotEmpty()) - { - ViewModel?.AddServerViaClipboardAsync(clipboardData); - } + AddServerViaClipboardAsync().ContinueWith(_ => { }); break; @@ -322,6 +309,15 @@ public partial class MainWindow ProcUtils.ProcessStart(Utils.GetBinPath("EnableLoopback.exe")); } + public async Task AddServerViaClipboardAsync() + { + var clipboardData = WindowsUtils.GetClipboardData(); + if (clipboardData.IsNotEmpty() && ViewModel != null) + { + await ViewModel.AddServerViaClipboardAsync(clipboardData); + } + } + private async Task ScanScreenTaskAsync() { ShowHideWindow(false); From 6b85aa0b03ce73e97352d882fde5f979f22c98a0 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 24 Sep 2025 10:57:23 +0800 Subject: [PATCH 014/132] Remove Splat.NLog package --- v2rayN/Directory.Packages.props | 2 +- v2rayN/ServiceLib/ServiceLib.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/v2rayN/Directory.Packages.props b/v2rayN/Directory.Packages.props index 283e95a6..24484112 100644 --- a/v2rayN/Directory.Packages.props +++ b/v2rayN/Directory.Packages.props @@ -20,7 +20,7 @@ - + diff --git a/v2rayN/ServiceLib/ServiceLib.csproj b/v2rayN/ServiceLib/ServiceLib.csproj index 7ee72196..457b065d 100644 --- a/v2rayN/ServiceLib/ServiceLib.csproj +++ b/v2rayN/ServiceLib/ServiceLib.csproj @@ -11,7 +11,7 @@ - + From faff8e4ea2d32c5381ee122b4296305040ec6e40 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 24 Sep 2025 18:41:00 +0800 Subject: [PATCH 015/132] Remove secret data from mihomo configuration --- v2rayN/ServiceLib/Services/CoreConfig/CoreConfigClashService.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigClashService.cs b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigClashService.cs index e102f17d..b9fcc126 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigClashService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/CoreConfigClashService.cs @@ -79,6 +79,7 @@ public class CoreConfigClashService //external-controller fileContent["external-controller"] = $"{Global.Loopback}:{AppManager.Instance.StatePort2}"; + fileContent.Remove("secret"); //allow-lan if (_config.Inbound.First().AllowLANConn) { From d86003df55cff21199eb251de3bd2da984ec899b Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Thu, 25 Sep 2025 10:56:10 +0800 Subject: [PATCH 016/132] Optimize and improve the Subject --- v2rayN/ServiceLib/Events/AppEvents.cs | 32 ++++++++++++++++++ v2rayN/ServiceLib/Events/EventChannel.cs | 29 ++++++++++++++++ v2rayN/ServiceLib/GlobalUsings.cs | 1 + v2rayN/ServiceLib/Handler/AppEvents.cs | 33 ------------------- v2rayN/ServiceLib/Manager/AppManager.cs | 4 +-- v2rayN/ServiceLib/Manager/NoticeManager.cs | 4 +-- .../ViewModels/CheckUpdateViewModel.cs | 2 +- .../ViewModels/MainWindowViewModel.cs | 20 +++++------ .../ViewModels/ProfilesViewModel.cs | 4 +-- .../ViewModels/StatusBarViewModel.cs | 18 +++++----- v2rayN/v2rayN.Desktop/App.axaml.cs | 2 +- v2rayN/v2rayN.Desktop/GlobalUsings.cs | 1 + .../v2rayN.Desktop/Views/MainWindow.axaml.cs | 2 +- v2rayN/v2rayN/GlobalUsings.cs | 1 + v2rayN/v2rayN/Views/MainWindow.xaml.cs | 2 +- 15 files changed, 93 insertions(+), 62 deletions(-) create mode 100644 v2rayN/ServiceLib/Events/AppEvents.cs create mode 100644 v2rayN/ServiceLib/Events/EventChannel.cs delete mode 100644 v2rayN/ServiceLib/Handler/AppEvents.cs diff --git a/v2rayN/ServiceLib/Events/AppEvents.cs b/v2rayN/ServiceLib/Events/AppEvents.cs new file mode 100644 index 00000000..45de4de1 --- /dev/null +++ b/v2rayN/ServiceLib/Events/AppEvents.cs @@ -0,0 +1,32 @@ +using System.Reactive; + +namespace ServiceLib.Events; + +public static class AppEvents +{ + public static readonly EventChannel ReloadRequested = new(); + public static readonly EventChannel ShowHideWindowRequested = new(); + public static readonly EventChannel AddServerViaScanRequested = new(); + public static readonly EventChannel AddServerViaClipboardRequested = new(); + public static readonly EventChannel SubscriptionsUpdateRequested = new(); + + public static readonly EventChannel ProfilesRefreshRequested = new(); + public static readonly EventChannel SubscriptionsRefreshRequested = new(); + public static readonly EventChannel ProxiesReloadRequested = new(); + public static readonly EventChannel DispatcherStatisticsRequested = new(); + + public static readonly EventChannel SendSnackMsgRequested = new(); + public static readonly EventChannel SendMsgViewRequested = new(); + + public static readonly EventChannel AppExitRequested = new(); + public static readonly EventChannel ShutdownRequested = new(); + + public static readonly EventChannel AdjustMainLvColWidthRequested = new(); + + public static readonly EventChannel SetDefaultServerRequested = new(); + + public static readonly EventChannel RoutingsMenuRefreshRequested = new(); + public static readonly EventChannel TestServerRequested = new(); + public static readonly EventChannel InboundDisplayRequested = new(); + public static readonly EventChannel SysProxyChangeRequested = new(); +} diff --git a/v2rayN/ServiceLib/Events/EventChannel.cs b/v2rayN/ServiceLib/Events/EventChannel.cs new file mode 100644 index 00000000..c3c58f3e --- /dev/null +++ b/v2rayN/ServiceLib/Events/EventChannel.cs @@ -0,0 +1,29 @@ +using System.Reactive; +using System.Reactive.Linq; +using System.Reactive.Subjects; + +namespace ServiceLib.Events; + +public sealed class EventChannel +{ + private readonly ISubject _subject = Subject.Synchronize(new Subject()); + + public IObservable AsObservable() + { + return _subject.AsObservable(); + } + + public void Publish(T value) + { + _subject.OnNext(value); + } + + public void Publish() + { + if (typeof(T) != typeof(Unit)) + { + throw new InvalidOperationException("Publish() without value is only valid for EventChannel."); + } + _subject.OnNext((T)(object)Unit.Default); + } +} diff --git a/v2rayN/ServiceLib/GlobalUsings.cs b/v2rayN/ServiceLib/GlobalUsings.cs index 9a78c73b..a952f4a8 100644 --- a/v2rayN/ServiceLib/GlobalUsings.cs +++ b/v2rayN/ServiceLib/GlobalUsings.cs @@ -1,6 +1,7 @@ global using ServiceLib.Base; global using ServiceLib.Common; global using ServiceLib.Enums; +global using ServiceLib.Events; global using ServiceLib.Handler; global using ServiceLib.Helper; global using ServiceLib.Manager; diff --git a/v2rayN/ServiceLib/Handler/AppEvents.cs b/v2rayN/ServiceLib/Handler/AppEvents.cs deleted file mode 100644 index bda78922..00000000 --- a/v2rayN/ServiceLib/Handler/AppEvents.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Reactive; -using System.Reactive.Subjects; - -namespace ServiceLib.Handler; - -public static class AppEvents -{ - public static readonly Subject ReloadRequested = new(); - public static readonly Subject ShowHideWindowRequested = new(); - public static readonly Subject AddServerViaScanRequested = new(); - public static readonly Subject AddServerViaClipboardRequested = new(); - public static readonly Subject SubscriptionsUpdateRequested = new(); - - public static readonly Subject ProfilesRefreshRequested = new(); - public static readonly Subject SubscriptionsRefreshRequested = new(); - public static readonly Subject ProxiesReloadRequested = new(); - public static readonly Subject DispatcherStatisticsRequested = new(); - - public static readonly Subject SendSnackMsgRequested = new(); - public static readonly Subject SendMsgViewRequested = new(); - - public static readonly Subject AppExitRequested = new(); - public static readonly Subject ShutdownRequested = new(); - - public static readonly Subject AdjustMainLvColWidthRequested = new(); - - public static readonly Subject SetDefaultServerRequested = new(); - - public static readonly Subject RoutingsMenuRefreshRequested = new(); - public static readonly Subject TestServerRequested = new(); - public static readonly Subject InboundDisplayRequested = new(); - public static readonly Subject SysProxyChangeRequested = new(); -} diff --git a/v2rayN/ServiceLib/Manager/AppManager.cs b/v2rayN/ServiceLib/Manager/AppManager.cs index 57238b15..a0518d21 100644 --- a/v2rayN/ServiceLib/Manager/AppManager.cs +++ b/v2rayN/ServiceLib/Manager/AppManager.cs @@ -96,7 +96,7 @@ public sealed class AppManager Logging.SaveLog("AppExitAsync Begin"); await SysProxyHandler.UpdateSysProxy(_config, true); - AppEvents.AppExitRequested.OnNext(Unit.Default); + AppEvents.AppExitRequested.Publish(); await Task.Delay(50); //Wait for AppExitRequested to be processed await ConfigHandler.SaveConfig(_config); @@ -119,7 +119,7 @@ public sealed class AppManager public void Shutdown(bool byUser) { - AppEvents.ShutdownRequested.OnNext(byUser); + AppEvents.ShutdownRequested.Publish(byUser); } public async Task RebootAsAdmin() diff --git a/v2rayN/ServiceLib/Manager/NoticeManager.cs b/v2rayN/ServiceLib/Manager/NoticeManager.cs index f9e149ed..da034f02 100644 --- a/v2rayN/ServiceLib/Manager/NoticeManager.cs +++ b/v2rayN/ServiceLib/Manager/NoticeManager.cs @@ -11,7 +11,7 @@ public class NoticeManager { return; } - AppEvents.SendSnackMsgRequested.OnNext(content); + AppEvents.SendSnackMsgRequested.Publish(content); } public void SendMessage(string? content) @@ -20,7 +20,7 @@ public class NoticeManager { return; } - AppEvents.SendMsgViewRequested.OnNext(content); + AppEvents.SendMsgViewRequested.Publish(content); } public void SendMessageEx(string? content) diff --git a/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs b/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs index 171cb467..ade01d3c 100644 --- a/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs @@ -223,7 +223,7 @@ public class CheckUpdateViewModel : MyReactiveObject { if (blReload) { - AppEvents.ReloadRequested.OnNext(Unit.Default); + AppEvents.ReloadRequested.Publish(); } else { diff --git a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs index ba5de790..be8898ae 100644 --- a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs @@ -292,7 +292,7 @@ public class MainWindowViewModel : MyReactiveObject } if (_config.UiItem.EnableAutoAdjustMainLvColWidth) { - AppEvents.AdjustMainLvColWidthRequested.OnNext(Unit.Default); + AppEvents.AdjustMainLvColWidthRequested.Publish(); } } } @@ -303,7 +303,7 @@ public class MainWindowViewModel : MyReactiveObject { return; } - AppEvents.DispatcherStatisticsRequested.OnNext(update); + AppEvents.DispatcherStatisticsRequested.Publish(update); } #endregion Actions @@ -312,14 +312,14 @@ public class MainWindowViewModel : MyReactiveObject private async Task RefreshServers() { - AppEvents.ProfilesRefreshRequested.OnNext(Unit.Default); + AppEvents.ProfilesRefreshRequested.Publish(); await Task.Delay(200); } private void RefreshSubscriptions() { - AppEvents.SubscriptionsRefreshRequested.OnNext(Unit.Default); + AppEvents.SubscriptionsRefreshRequested.Publish(); } #endregion Servers && Groups @@ -451,7 +451,7 @@ public class MainWindowViewModel : MyReactiveObject var ret = await _updateView?.Invoke(EViewAction.OptionSettingWindow, null); if (ret == true) { - AppEvents.InboundDisplayRequested.OnNext(Unit.Default); + AppEvents.InboundDisplayRequested.Publish(); await Reload(); } } @@ -462,7 +462,7 @@ public class MainWindowViewModel : MyReactiveObject if (ret == true) { await ConfigHandler.InitBuiltinRouting(_config); - AppEvents.RoutingsMenuRefreshRequested.OnNext(Unit.Default); + AppEvents.RoutingsMenuRefreshRequested.Publish(); await Reload(); } } @@ -530,12 +530,12 @@ public class MainWindowViewModel : MyReactiveObject await SysProxyHandler.UpdateSysProxy(_config, false); await Task.Delay(1000); }); - AppEvents.TestServerRequested.OnNext(Unit.Default); + AppEvents.TestServerRequested.Publish(); var showClashUI = _config.IsRunningCore(ECoreType.sing_box); if (showClashUI) { - AppEvents.ProxiesReloadRequested.OnNext(Unit.Default); + AppEvents.ProxiesReloadRequested.Publish(); } RxApp.MainThreadScheduler.Schedule(() => ReloadResult(showClashUI)); @@ -565,7 +565,7 @@ public class MainWindowViewModel : MyReactiveObject { if (_config.UiItem.AutoHideStartup) { - AppEvents.ShowHideWindowRequested.OnNext(false); + AppEvents.ShowHideWindowRequested.Publish(false); } await Task.CompletedTask; } @@ -578,7 +578,7 @@ public class MainWindowViewModel : MyReactiveObject { await ConfigHandler.ApplyRegionalPreset(_config, type); await ConfigHandler.InitRouting(_config); - AppEvents.RoutingsMenuRefreshRequested.OnNext(Unit.Default); + AppEvents.RoutingsMenuRefreshRequested.Publish(); await ConfigHandler.SaveConfig(_config); await new UpdateService().UpdateGeoFileAll(_config, UpdateTaskHandler); diff --git a/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs b/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs index fcf3450d..b36c933c 100644 --- a/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs @@ -275,7 +275,7 @@ public class ProfilesViewModel : MyReactiveObject private void Reload() { - AppEvents.ReloadRequested.OnNext(Unit.Default); + AppEvents.ReloadRequested.Publish(); } public async Task SetSpeedTestResult(SpeedTestResult result) @@ -361,7 +361,7 @@ public class ProfilesViewModel : MyReactiveObject public async Task RefreshServers() { - AppEvents.ProfilesRefreshRequested.OnNext(Unit.Default); + AppEvents.ProfilesRefreshRequested.Publish(); await Task.Delay(200); } diff --git a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs index 6dc795ef..26386e0f 100644 --- a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs @@ -148,17 +148,17 @@ public class StatusBarViewModel : MyReactiveObject NotifyLeftClickCmd = ReactiveCommand.CreateFromTask(async () => { - AppEvents.ShowHideWindowRequested.OnNext(null); + AppEvents.ShowHideWindowRequested.Publish(null); await Task.CompletedTask; }); ShowWindowCmd = ReactiveCommand.CreateFromTask(async () => { - AppEvents.ShowHideWindowRequested.OnNext(true); + AppEvents.ShowHideWindowRequested.Publish(true); await Task.CompletedTask; }); HideWindowCmd = ReactiveCommand.CreateFromTask(async () => { - AppEvents.ShowHideWindowRequested.OnNext(false); + AppEvents.ShowHideWindowRequested.Publish(false); await Task.CompletedTask; }); @@ -274,19 +274,19 @@ public class StatusBarViewModel : MyReactiveObject private async Task AddServerViaClipboard() { - AppEvents.AddServerViaClipboardRequested.OnNext(Unit.Default); + AppEvents.AddServerViaClipboardRequested.Publish(); await Task.Delay(1000); } private async Task AddServerViaScan() { - AppEvents.AddServerViaScanRequested.OnNext(Unit.Default); + AppEvents.AddServerViaScanRequested.Publish(); await Task.Delay(1000); } private async Task UpdateSubscriptionProcess(bool blProxy) { - AppEvents.SubscriptionsUpdateRequested.OnNext(blProxy); + AppEvents.SubscriptionsUpdateRequested.Publish(blProxy); await Task.Delay(1000); } @@ -348,7 +348,7 @@ public class StatusBarViewModel : MyReactiveObject { return; } - AppEvents.SetDefaultServerRequested.OnNext(SelectedServer.ID); + AppEvents.SetDefaultServerRequested.Publish(SelectedServer.ID); } public async Task TestServerAvailability() @@ -449,7 +449,7 @@ public class StatusBarViewModel : MyReactiveObject if (await ConfigHandler.SetDefaultRouting(_config, item) == 0) { NoticeManager.Instance.SendMessageEx(ResUI.TipChangeRouting); - AppEvents.ReloadRequested.OnNext(Unit.Default); + AppEvents.ReloadRequested.Publish(); _updateView?.Invoke(EViewAction.DispatcherRefreshIcon, null); } } @@ -496,7 +496,7 @@ public class StatusBarViewModel : MyReactiveObject } } await ConfigHandler.SaveConfig(_config); - AppEvents.ReloadRequested.OnNext(Unit.Default); + AppEvents.ReloadRequested.Publish(); } private bool AllowEnableTun() diff --git a/v2rayN/v2rayN.Desktop/App.axaml.cs b/v2rayN/v2rayN.Desktop/App.axaml.cs index fa4779cf..a3468a01 100644 --- a/v2rayN/v2rayN.Desktop/App.axaml.cs +++ b/v2rayN/v2rayN.Desktop/App.axaml.cs @@ -54,7 +54,7 @@ public partial class App : Application { if (desktop.MainWindow != null) { - AppEvents.AddServerViaClipboardRequested.OnNext(Unit.Default); + AppEvents.AddServerViaClipboardRequested.Publish(); await Task.Delay(1000); } } diff --git a/v2rayN/v2rayN.Desktop/GlobalUsings.cs b/v2rayN/v2rayN.Desktop/GlobalUsings.cs index 4f2b931d..aede90cc 100644 --- a/v2rayN/v2rayN.Desktop/GlobalUsings.cs +++ b/v2rayN/v2rayN.Desktop/GlobalUsings.cs @@ -2,6 +2,7 @@ global using ServiceLib; global using ServiceLib.Base; global using ServiceLib.Common; global using ServiceLib.Enums; +global using ServiceLib.Events; global using ServiceLib.Handler; global using ServiceLib.Manager; global using ServiceLib.Models; diff --git a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs index 86bd5d7b..6c56cefc 100644 --- a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs @@ -253,7 +253,7 @@ public partial class MainWindow : WindowBase case EGlobalHotkey.SystemProxySet: case EGlobalHotkey.SystemProxyUnchanged: case EGlobalHotkey.SystemProxyPac: - AppEvents.SysProxyChangeRequested.OnNext((ESysProxyType)((int)e - 1)); + AppEvents.SysProxyChangeRequested.Publish((ESysProxyType)((int)e - 1)); break; } } diff --git a/v2rayN/v2rayN/GlobalUsings.cs b/v2rayN/v2rayN/GlobalUsings.cs index 4f2b931d..aede90cc 100644 --- a/v2rayN/v2rayN/GlobalUsings.cs +++ b/v2rayN/v2rayN/GlobalUsings.cs @@ -2,6 +2,7 @@ global using ServiceLib; global using ServiceLib.Base; global using ServiceLib.Common; global using ServiceLib.Enums; +global using ServiceLib.Events; global using ServiceLib.Handler; global using ServiceLib.Manager; global using ServiceLib.Models; diff --git a/v2rayN/v2rayN/Views/MainWindow.xaml.cs b/v2rayN/v2rayN/Views/MainWindow.xaml.cs index 1d0d530c..39bc3985 100644 --- a/v2rayN/v2rayN/Views/MainWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/MainWindow.xaml.cs @@ -241,7 +241,7 @@ public partial class MainWindow case EGlobalHotkey.SystemProxySet: case EGlobalHotkey.SystemProxyUnchanged: case EGlobalHotkey.SystemProxyPac: - AppEvents.SysProxyChangeRequested.OnNext((ESysProxyType)((int)e - 1)); + AppEvents.SysProxyChangeRequested.Publish((ESysProxyType)((int)e - 1)); break; } } From 21a773f40076cb4df47c6d7068560f10c85d1921 Mon Sep 17 00:00:00 2001 From: JieXu Date: Fri, 26 Sep 2025 13:55:35 +0800 Subject: [PATCH 017/132] Update MsgView.axaml.cs Plan C (#8035) * Add avaloniaEdit for test * Adjust avaloniaEdit * Optimize and improve message function * Update build-linux.yml * Update MsgView.axaml * Update MsgView.axaml.cs --------- Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com> --- .github/workflows/build-linux.yml | 2 +- v2rayN/Directory.Packages.props | 4 +- v2rayN/ServiceLib/ViewModels/MsgViewModel.cs | 66 +++++++++---------- v2rayN/v2rayN.Desktop/App.axaml | 1 + .../ViewModels/ThemeSettingViewModel.cs | 7 +- v2rayN/v2rayN.Desktop/Views/MsgView.axaml | 62 ++++++++--------- v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs | 35 ++++++---- v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj | 2 + v2rayN/v2rayN/Views/MsgView.xaml.cs | 11 +++- 9 files changed, 107 insertions(+), 83 deletions(-) diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 40e9c953..d5f9b1c5 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -22,7 +22,7 @@ jobs: matrix: configuration: [Release] - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Checkout diff --git a/v2rayN/Directory.Packages.props b/v2rayN/Directory.Packages.props index 24484112..9dcfbe61 100644 --- a/v2rayN/Directory.Packages.props +++ b/v2rayN/Directory.Packages.props @@ -5,6 +5,7 @@ false + @@ -19,6 +20,7 @@ + @@ -27,4 +29,4 @@ - + \ No newline at end of file diff --git a/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs b/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs index 8fc62dfe..8b3467bd 100644 --- a/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs @@ -1,5 +1,6 @@ using System.Collections.Concurrent; using System.Reactive.Linq; +using System.Text; using System.Text.RegularExpressions; using ReactiveUI; using ReactiveUI.Fody.Helpers; @@ -9,9 +10,9 @@ namespace ServiceLib.ViewModels; public class MsgViewModel : MyReactiveObject { private readonly ConcurrentQueue _queueMsg = new(); - private readonly int _numMaxMsg = 500; - private bool _lastMsgFilterNotAvailable; - private bool _blLockShow = false; + private volatile bool _lastMsgFilterNotAvailable; + private int _showLock = 0; // 0 = unlocked, 1 = locked + public int NumMaxMsg { get; } = 50; [Reactive] public string MsgFilter { get; set; } @@ -33,46 +34,52 @@ public class MsgViewModel : MyReactiveObject this.WhenAnyValue( x => x.AutoRefresh, y => y == true) - .Subscribe(c => { _config.MsgUIItem.AutoRefresh = AutoRefresh; }); + .Subscribe(c => _config.MsgUIItem.AutoRefresh = AutoRefresh); AppEvents.SendMsgViewRequested .AsObservable() //.ObserveOn(RxApp.MainThreadScheduler) - .Subscribe(async content => await AppendQueueMsg(content)); + .Subscribe(content => _ = AppendQueueMsg(content)); } private async Task AppendQueueMsg(string msg) { - //if (msg == Global.CommandClearMsg) - //{ - // ClearMsg(); - // return; - //} if (AutoRefresh == false) { return; } - _ = EnqueueQueueMsg(msg); - if (_blLockShow) - { - return; - } + EnqueueQueueMsg(msg); + if (!_config.UiItem.ShowInTaskbar) { return; } - _blLockShow = true; + if (Interlocked.CompareExchange(ref _showLock, 1, 0) != 0) + { + return; + } - await Task.Delay(500); - var txt = string.Join("", _queueMsg.ToArray()); - await _updateView?.Invoke(EViewAction.DispatcherShowMsg, txt); + try + { + await Task.Delay(500).ConfigureAwait(false); - _blLockShow = false; + var sb = new StringBuilder(); + while (_queueMsg.TryDequeue(out var line)) + { + sb.Append(line); + } + + await _updateView?.Invoke(EViewAction.DispatcherShowMsg, sb.ToString()); + } + finally + { + Interlocked.Exchange(ref _showLock, 0); + } } - private async Task EnqueueQueueMsg(string msg) + private void EnqueueQueueMsg(string msg) { //filter msg if (MsgFilter.IsNotEmpty() && !_lastMsgFilterNotAvailable) @@ -91,26 +98,17 @@ public class MsgViewModel : MyReactiveObject } } - //Enqueue - if (_queueMsg.Count > _numMaxMsg) - { - for (int k = 0; k < _queueMsg.Count - _numMaxMsg; k++) - { - _queueMsg.TryDequeue(out _); - } - } _queueMsg.Enqueue(msg); if (!msg.EndsWith(Environment.NewLine)) { _queueMsg.Enqueue(Environment.NewLine); } - await Task.CompletedTask; } - public void ClearMsg() - { - _queueMsg.Clear(); - } + //public void ClearMsg() + //{ + // _queueMsg.Clear(); + //} private void DoMsgFilter() { diff --git a/v2rayN/v2rayN.Desktop/App.axaml b/v2rayN/v2rayN.Desktop/App.axaml index 8b61125f..222ab488 100644 --- a/v2rayN/v2rayN.Desktop/App.axaml +++ b/v2rayN/v2rayN.Desktop/App.axaml @@ -11,6 +11,7 @@ RequestedThemeVariant="Default"> + diff --git a/v2rayN/v2rayN.Desktop/ViewModels/ThemeSettingViewModel.cs b/v2rayN/v2rayN.Desktop/ViewModels/ThemeSettingViewModel.cs index 3473cf23..ae12fa07 100644 --- a/v2rayN/v2rayN.Desktop/ViewModels/ThemeSettingViewModel.cs +++ b/v2rayN/v2rayN.Desktop/ViewModels/ThemeSettingViewModel.cs @@ -5,6 +5,7 @@ using Avalonia.Controls.Notifications; using Avalonia.Controls.Primitives; using Avalonia.Media; using Avalonia.Styling; +using AvaloniaEdit; using ReactiveUI; using ReactiveUI.Fody.Helpers; using Semi.Avalonia; @@ -112,7 +113,8 @@ public class ThemeSettingViewModel : MyReactiveObject x.OfType(), x.OfType(), x.OfType(), - x.OfType() + x.OfType(), + x.OfType() )); style.Add(new Setter() { @@ -153,7 +155,8 @@ public class ThemeSettingViewModel : MyReactiveObject x.OfType(), x.OfType(), x.OfType(), - x.OfType() + x.OfType(), + x.OfType() )); style.Add(new Setter() { diff --git a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml index b6ca81a1..2f7ad807 100644 --- a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml +++ b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml @@ -2,6 +2,7 @@ x:Class="v2rayN.Desktop.Views.MsgView" xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:avaloniaEdit="clr-namespace:AvaloniaEdit;assembly=AvaloniaEdit" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" @@ -70,35 +71,36 @@ Theme="{DynamicResource SimpleToggleSwitch}" /> - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs index e2d21101..f7b2f9b9 100644 --- a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs @@ -1,5 +1,4 @@ using System.Reactive.Disposables; -using Avalonia.Controls; using Avalonia.Interactivity; using Avalonia.ReactiveUI; using Avalonia.Threading; @@ -10,13 +9,12 @@ namespace v2rayN.Desktop.Views; public partial class MsgView : ReactiveUserControl { - private readonly ScrollViewer _scrollViewer; + private const int MaxLines = 350; + private const int KeepLines = 320; public MsgView() { InitializeComponent(); - _scrollViewer = this.FindControl("msgScrollViewer"); - ViewModel = new MsgViewModel(UpdateViewHandler); this.WhenActivated(disposables => @@ -34,9 +32,8 @@ public partial class MsgView : ReactiveUserControl if (obj is null) return false; - Dispatcher.UIThread.Post(() => - ShowMsg(obj), - DispatcherPriority.ApplicationIdle); + Dispatcher.UIThread.Post(() => ShowMsg(obj), + DispatcherPriority.ApplicationIdle); break; } return await Task.FromResult(true); @@ -44,23 +41,35 @@ public partial class MsgView : ReactiveUserControl private void ShowMsg(object msg) { - txtMsg.Text = msg.ToString(); + txtMsg.AppendText(msg.ToString()); + + if (txtMsg.Document.LineCount > MaxLines) + { + var lc = txtMsg.Document.LineCount; + var cutLineNumber = lc - KeepLines; + var cutLine = txtMsg.Document.GetLineByNumber(cutLineNumber); + txtMsg.Document.Remove(0, cutLine.Offset); + } + if (togScrollToEnd.IsChecked ?? true) { - _scrollViewer?.ScrollToEnd(); + txtMsg.ScrollToEnd(); } } public void ClearMsg() { - ViewModel?.ClearMsg(); - txtMsg.Text = ""; + txtMsg.Text = string.Empty; + txtMsg.AppendText("----- Message cleared -----\n"); } private void menuMsgViewSelectAll_Click(object? sender, RoutedEventArgs e) { - txtMsg.Focus(); - txtMsg.SelectAll(); + Dispatcher.UIThread.Post(() => + { + txtMsg.TextArea.Focus(); + txtMsg.SelectAll(); + }, DispatcherPriority.Render); } private async void menuMsgViewCopy_Click(object? sender, RoutedEventArgs e) diff --git a/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj b/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj index 8c274a69..f6ff5735 100644 --- a/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj +++ b/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj @@ -9,6 +9,7 @@ + true @@ -17,6 +18,7 @@ + true diff --git a/v2rayN/v2rayN/Views/MsgView.xaml.cs b/v2rayN/v2rayN/Views/MsgView.xaml.cs index 3cc1fdfa..af8eb5f4 100644 --- a/v2rayN/v2rayN/Views/MsgView.xaml.cs +++ b/v2rayN/v2rayN/Views/MsgView.xaml.cs @@ -48,18 +48,25 @@ public partial class MsgView private void ShowMsg(object msg) { txtMsg.BeginChange(); - txtMsg.Text = msg.ToString(); + + if (txtMsg.LineCount > ViewModel?.NumMaxMsg) + { + ClearMsg(); + } + + txtMsg.AppendText(msg.ToString()); if (togScrollToEnd.IsChecked ?? true) { txtMsg.ScrollToEnd(); } + txtMsg.EndChange(); } public void ClearMsg() { - ViewModel?.ClearMsg(); txtMsg.Clear(); + txtMsg.AppendText("----- Message cleared -----\n"); } private void menuMsgViewSelectAll_Click(object sender, System.Windows.RoutedEventArgs e) From 326bf334e761d90a8b871f7acbfd231787d75095 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 26 Sep 2025 15:07:33 +0800 Subject: [PATCH 018/132] Optimize and improve MsgView --- v2rayN/ServiceLib/ViewModels/MsgViewModel.cs | 2 +- v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs | 21 ++++++++++++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs b/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs index 8b3467bd..e9d89b94 100644 --- a/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs @@ -12,7 +12,7 @@ public class MsgViewModel : MyReactiveObject private readonly ConcurrentQueue _queueMsg = new(); private volatile bool _lastMsgFilterNotAvailable; private int _showLock = 0; // 0 = unlocked, 1 = locked - public int NumMaxMsg { get; } = 50; + public int NumMaxMsg { get; } = 500; [Reactive] public string MsgFilter { get; set; } diff --git a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs index f7b2f9b9..637579ee 100644 --- a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs @@ -9,8 +9,7 @@ namespace v2rayN.Desktop.Views; public partial class MsgView : ReactiveUserControl { - private const int MaxLines = 350; - private const int KeepLines = 320; + //private const int KeepLines = 30; public MsgView() { @@ -41,20 +40,26 @@ public partial class MsgView : ReactiveUserControl private void ShowMsg(object msg) { - txtMsg.AppendText(msg.ToString()); + txtMsg.BeginChange(); - if (txtMsg.Document.LineCount > MaxLines) + //var lineCount = txtMsg.LineCount; + //if (lineCount > ViewModel?.NumMaxMsg) + //{ + // var cutLine = txtMsg.Document.GetLineByNumber(lineCount - KeepLines); + // txtMsg.Document.Remove(0, cutLine.Offset); + //} + if (txtMsg.LineCount > ViewModel?.NumMaxMsg) { - var lc = txtMsg.Document.LineCount; - var cutLineNumber = lc - KeepLines; - var cutLine = txtMsg.Document.GetLineByNumber(cutLineNumber); - txtMsg.Document.Remove(0, cutLine.Offset); + ClearMsg(); } + txtMsg.AppendText(msg.ToString()); if (togScrollToEnd.IsChecked ?? true) { txtMsg.ScrollToEnd(); } + + txtMsg.EndChange(); } public void ClearMsg() From a652fd879b6fcef1842c6c07848772fb76cb6e86 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 26 Sep 2025 15:29:46 +0800 Subject: [PATCH 019/132] Added simple highlight function to the message view --- v2rayN/ServiceLib/Global.cs | 8 ++ .../Common/TextEditorKeywordHighlighter.cs | 129 ++++++++++++++++++ v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs | 6 + 3 files changed, 143 insertions(+) create mode 100644 v2rayN/v2rayN.Desktop/Common/TextEditorKeywordHighlighter.cs diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs index b45a6eb3..6c4a4362 100644 --- a/v2rayN/ServiceLib/Global.cs +++ b/v2rayN/ServiceLib/Global.cs @@ -449,6 +449,14 @@ public class Global "none" ]; + public static readonly Dictionary LogLevelColors = new() + { + { "debug", "#6C757D" }, + { "info", "#2ECC71" }, + { "warning", "#FFA500" }, + { "error", "#E74C3C" }, + }; + public static readonly List InboundTags = [ "socks", diff --git a/v2rayN/v2rayN.Desktop/Common/TextEditorKeywordHighlighter.cs b/v2rayN/v2rayN.Desktop/Common/TextEditorKeywordHighlighter.cs new file mode 100644 index 00000000..af1de3d0 --- /dev/null +++ b/v2rayN/v2rayN.Desktop/Common/TextEditorKeywordHighlighter.cs @@ -0,0 +1,129 @@ +using Avalonia.Media; +using AvaloniaEdit; +using AvaloniaEdit.Document; +using AvaloniaEdit.Rendering; + +namespace v2rayN.Desktop.Common; + +public class KeywordColorizer : DocumentColorizingTransformer +{ + private readonly string[] _keywords; + private readonly Dictionary _brushMap; + + public KeywordColorizer(IDictionary keywordBrushMap) + { + if (keywordBrushMap == null || keywordBrushMap.Count == 0) + { + throw new ArgumentException("keywordBrushMap must not be null or empty", nameof(keywordBrushMap)); + } + + _brushMap = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (var kvp in keywordBrushMap) + { + if (string.IsNullOrEmpty(kvp.Key) || kvp.Value == null) + { + continue; + } + + if (!_brushMap.ContainsKey(kvp.Key)) + { + _brushMap[kvp.Key] = kvp.Value; + } + } + + if (_brushMap.Count == 0) + { + throw new ArgumentException("keywordBrushMap must contain at least one non-empty key with a non-null brush", nameof(keywordBrushMap)); + } + + _keywords = _brushMap.Keys.ToArray(); + } + + protected override void ColorizeLine(DocumentLine line) + { + var text = CurrentContext.Document.GetText(line); + if (string.IsNullOrEmpty(text)) + { + return; + } + + foreach (var kw in _keywords) + { + if (string.IsNullOrEmpty(kw)) + { + continue; + } + + var searchStart = 0; + while (true) + { + var idx = text.IndexOf(kw, searchStart, StringComparison.OrdinalIgnoreCase); + if (idx < 0) + { + break; + } + + var kwEndIndex = idx + kw.Length; + if (IsWordCharBefore(text, idx) || IsWordCharAfter(text, kwEndIndex)) + { + searchStart = idx + Math.Max(1, kw.Length); + continue; + } + + var start = line.Offset + idx; + var end = start + kw.Length; + + if (_brushMap.TryGetValue(kw, out var brush) && brush != null) + { + ChangeLinePart(start, end, element => element.TextRunProperties.SetForegroundBrush(brush)); + } + + searchStart = idx + Math.Max(1, kw.Length); + } + } + } + + private static bool IsWordCharBefore(string text, int idx) + { + if (idx <= 0) + { + return false; + } + + var c = text[idx - 1]; + return char.IsLetterOrDigit(c) || c == '_'; + } + + private static bool IsWordCharAfter(string text, int idx) + { + if (idx >= text.Length) + { + return false; + } + + var c = text[idx]; + return char.IsLetterOrDigit(c) || c == '_'; + } +} + +public static class TextEditorKeywordHighlighter +{ + public static void Attach(TextEditor editor, IDictionary keywordBrushMap) + { + ArgumentNullException.ThrowIfNull(editor); + + if (keywordBrushMap == null || keywordBrushMap.Count == 0) + { + return; + } + + if (editor.TextArea?.TextView?.LineTransformers?.OfType().Any() == true) + { + return; + } + + var colorizer = new KeywordColorizer(keywordBrushMap); + editor.TextArea.TextView.LineTransformers.Add(colorizer); + editor.TextArea.TextView.InvalidateVisual(); + } +} diff --git a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs index 637579ee..6a279284 100644 --- a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs @@ -1,5 +1,6 @@ using System.Reactive.Disposables; using Avalonia.Interactivity; +using Avalonia.Media; using Avalonia.ReactiveUI; using Avalonia.Threading; using ReactiveUI; @@ -21,6 +22,11 @@ public partial class MsgView : ReactiveUserControl this.Bind(ViewModel, vm => vm.MsgFilter, v => v.cmbMsgFilter.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables); }); + + TextEditorKeywordHighlighter.Attach(txtMsg, Global.LogLevelColors.ToDictionary( + kv => kv.Key, + kv => (IBrush)new SolidColorBrush(Color.Parse(kv.Value)) + )); } private async Task UpdateViewHandler(EViewAction action, object? obj) From 03d5b7a05b36ef002ed2439e703539e290444cdd Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 26 Sep 2025 17:11:48 +0800 Subject: [PATCH 020/132] Bug fix --- v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs | 6 +----- v2rayN/v2rayN/Views/MsgView.xaml.cs | 4 ---- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs index 6a279284..a49253c3 100644 --- a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs @@ -46,8 +46,6 @@ public partial class MsgView : ReactiveUserControl private void ShowMsg(object msg) { - txtMsg.BeginChange(); - //var lineCount = txtMsg.LineCount; //if (lineCount > ViewModel?.NumMaxMsg) //{ @@ -64,13 +62,11 @@ public partial class MsgView : ReactiveUserControl { txtMsg.ScrollToEnd(); } - - txtMsg.EndChange(); } public void ClearMsg() { - txtMsg.Text = string.Empty; + txtMsg.Clear(); txtMsg.AppendText("----- Message cleared -----\n"); } diff --git a/v2rayN/v2rayN/Views/MsgView.xaml.cs b/v2rayN/v2rayN/Views/MsgView.xaml.cs index af8eb5f4..449597cc 100644 --- a/v2rayN/v2rayN/Views/MsgView.xaml.cs +++ b/v2rayN/v2rayN/Views/MsgView.xaml.cs @@ -47,8 +47,6 @@ public partial class MsgView private void ShowMsg(object msg) { - txtMsg.BeginChange(); - if (txtMsg.LineCount > ViewModel?.NumMaxMsg) { ClearMsg(); @@ -59,8 +57,6 @@ public partial class MsgView { txtMsg.ScrollToEnd(); } - - txtMsg.EndChange(); } public void ClearMsg() From dc4611a2589a2a9f79fbdee1ba3761d316b2eead Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 26 Sep 2025 20:36:27 +0800 Subject: [PATCH 021/132] Adjust qrcode width --- v2rayN/v2rayN.Desktop/Views/QrcodeView.axaml | 2 +- v2rayN/v2rayN/Views/QrcodeView.xaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/v2rayN/v2rayN.Desktop/Views/QrcodeView.axaml b/v2rayN/v2rayN.Desktop/Views/QrcodeView.axaml index b67a7a1f..350cb06a 100644 --- a/v2rayN/v2rayN.Desktop/Views/QrcodeView.axaml +++ b/v2rayN/v2rayN.Desktop/Views/QrcodeView.axaml @@ -10,7 +10,7 @@ mc:Ignorable="d"> - 500 + 400 diff --git a/v2rayN/v2rayN/Views/QrcodeView.xaml b/v2rayN/v2rayN/Views/QrcodeView.xaml index b2dcdc3d..80aaae7d 100644 --- a/v2rayN/v2rayN/Views/QrcodeView.xaml +++ b/v2rayN/v2rayN/Views/QrcodeView.xaml @@ -12,7 +12,7 @@ Style="{StaticResource ViewGlobal}" mc:Ignorable="d"> - 500 + 400 From ebb95b5ee8374d05058a119a9454d6aaf747a426 Mon Sep 17 00:00:00 2001 From: JieXu Date: Sat, 27 Sep 2025 17:02:49 +0800 Subject: [PATCH 022/132] Update MsgView.axaml.cs (#8042) --- v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs index a49253c3..1f7665ca 100644 --- a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs @@ -15,6 +15,7 @@ public partial class MsgView : ReactiveUserControl public MsgView() { InitializeComponent(); + txtMsg.TextArea.TextView.Options.EnableHyperlinks = false; ViewModel = new MsgViewModel(UpdateViewHandler); this.WhenActivated(disposables => From 46edd8f9a4ac65097692f869b8c5231474689e76 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sat, 27 Sep 2025 18:07:20 +0800 Subject: [PATCH 023/132] Bug fix --- v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs | 2 +- v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs index be8898ae..a6a6bd31 100644 --- a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs @@ -248,7 +248,7 @@ public class MainWindowViewModel : MyReactiveObject { _config.UiItem.ShowInTaskbar = true; - await ConfigHandler.InitBuiltinRouting(_config); + //await ConfigHandler.InitBuiltinRouting(_config); await ConfigHandler.InitBuiltinDNS(_config); await ConfigHandler.InitBuiltinFullConfigTemplate(_config); await ProfileExManager.Instance.Init(); diff --git a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs index 26386e0f..9742cfe6 100644 --- a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs @@ -238,6 +238,7 @@ public class StatusBarViewModel : MyReactiveObject private async Task Init() { + await ConfigHandler.InitBuiltinRouting(_config); await RefreshRoutingsMenu(); await InboundDisplayStatus(); await ChangeSystemProxyAsync(_config.SystemProxyItem.SysProxyType, true); From b25d4d57bd10ac3448f095b6a40bd3d1feaf1742 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sat, 27 Sep 2025 19:46:31 +0800 Subject: [PATCH 024/132] Fix ProfilesSelectWindow --- v2rayN/v2rayN.Desktop/Views/ProfilesSelectWindow.axaml.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v2rayN/v2rayN.Desktop/Views/ProfilesSelectWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/ProfilesSelectWindow.axaml.cs index d597e567..ac562758 100644 --- a/v2rayN/v2rayN.Desktop/Views/ProfilesSelectWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/ProfilesSelectWindow.axaml.cs @@ -3,13 +3,13 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; -using Avalonia.ReactiveUI; using Avalonia.VisualTree; using ReactiveUI; +using v2rayN.Desktop.Base; namespace v2rayN.Desktop.Views; -public partial class ProfilesSelectWindow : ReactiveWindow +public partial class ProfilesSelectWindow : WindowBase { private static Config _config; From 7f07279a4c20b1f15a765fe66f8496d96b7ff94b Mon Sep 17 00:00:00 2001 From: Wydy <1937986+wydy@users.noreply.github.com> Date: Sun, 28 Sep 2025 19:08:29 +0800 Subject: [PATCH 025/132] Update pac (#7991) --- v2rayN/ServiceLib/Sample/pac | 336 +++++++++++++++-------------------- 1 file changed, 145 insertions(+), 191 deletions(-) diff --git a/v2rayN/ServiceLib/Sample/pac b/v2rayN/ServiceLib/Sample/pac index 1378c49f..f4f5939b 100644 --- a/v2rayN/ServiceLib/Sample/pac +++ b/v2rayN/ServiceLib/Sample/pac @@ -7,12 +7,12 @@ var rules = [ "030buy.com", "0rz.tw", "1-apple.com.tw", - "10.tt", "1000giri.net", "100ke.org", "10beasts.net", "10conditionsoflove.com", "10musume.com", + "111666.best", "123rf.com", "12bet.com", "12vpn.com", @@ -29,7 +29,6 @@ var rules = [ "177pic.info", "17t17p.com", "18board.com", - "18board.info", "18comic.org", "18comic.vip", "18hmanga.click", @@ -42,20 +41,18 @@ var rules = [ "1984bbs.com", "1984bbs.org", "1991way.com", - "1998cdp.org", "1bao.org", "1dumb.com", "1e100.net", "1eew.com", "1lib.domains", + "1lib.sk", "1mobile.com", - "1mobile.tw", "1point3acres.com", "1pondo.tv", "2-hand.info", "2000fun.com", "2008xianzhang.info", - "2017.hk", "2021hkcharter.com", "2047.name", "2047.one", @@ -68,7 +65,6 @@ var rules = [ "233abc.com", "233v2.com", "24hrs.ca", - "24smile.org", "25u.com", "2du5.com", "2lipstube.com", @@ -87,11 +83,11 @@ var rules = [ "3ren.ca", "3tui.net", "404museum.com", - "43110.cf", "466453.com", "4bluestones.biz", "4chan.com", "4dq.com", + "4everland.io", "4everproxy.com", "4gtv.tv", "4irc.com", @@ -109,7 +105,7 @@ var rules = [ "5278.cc", "5299.tv", "55comic.com", - "5aimiku.com", + "5657.com.tw", "5i01.com", "5isotoi5.org", "5maodang.com", @@ -124,6 +120,7 @@ var rules = [ "666kb.com", "666pool.cn", "69shu.com", + "69shuba.com", "69shuba.cx", "6do.news", "6do.world", @@ -146,8 +143,7 @@ var rules = [ "8news.com.tw", "8world.com", "8z1.net", - "9001700.com", - "908taiwan.org", + "91dasai.com", "91jinman.com", "91porn.com", "91porny.com", @@ -169,16 +165,13 @@ var rules = [ "abc.net.au", "abc.xyz", "abchinese.com", - "abclite.net", "abebooks.co.uk", "abebooks.com", "ablwang.com", "aboluowang.com", - "about.google", "about.me", - "aboutgfw.com", + "abplive.com", "abs.edu", - "ac.jp", "acast.com", "accim.org", "accountkit.com", @@ -226,7 +219,6 @@ var rules = [ "agro.hk", "ai-kan.net", "ai-wen.net", - "ai.google", "aiosearch.com", "aiph.net", "airasia.com", @@ -308,9 +300,9 @@ var rules = [ "amnesty.tw", "amnestyusa.org", "amnyemachen.org", - "amoiist.com", "ampproject.org", "amtb-taipei.org", + "amuletmc.com", "anchor.fm", "anchorfree.com", "ancsconf.org", @@ -363,8 +355,8 @@ var rules = [ "aol.co.uk", "aol.com", "aolnews.com", + "aomedia.org", "aomiwang.com", - "ap.org", "apartmentratings.com", "apartments.com", "apat1989.org", @@ -384,6 +376,7 @@ var rules = [ "aplusvpn.com", "appadvice.com", "appbrain.com", + "appdefensealliance.dev", "appdownloader.net", "appledaily.com", "appledaily.com.hk", @@ -412,12 +405,14 @@ var rules = [ "arethusa.su", "arlingtoncemetery.mil", "army.mil", + "arstechnica.net", "art4tibet1998.org", "arte.tv", "artofpeacefoundation.org", "artstation.com", "artsy.net", "arunachalforests.gov.in", + "arvanstorage.ir", "asacp.org", "asdfg.jp", "asg.to", @@ -425,10 +420,7 @@ var rules = [ "asiaharvest.org", "asianage.com", "asianews.it", - "asianfreeforum.com", "asiansexdiary.com", - "asianspiss.com", - "asianwomensfilm.de", "asiaone.com", "asiatgp.com", "asiatimes.com", @@ -447,13 +439,11 @@ var rules = [ "atgfw.org", "athenaeizou.com", "atlanta168.com", - "atlaspost.com", "atnext.com", "audacy.com", "audionow.com", "authorizeddns.net", "authorizeddns.org", - "authorizeddns.us", "autodraw.com", "av-e-body.com", "av.com", @@ -480,7 +470,6 @@ var rules = [ "azerbaycan.tv", "azerimix.com", "azirevpn.com", - "azubu.tv", "azurewebsites.net", "b-cdn.net", "b-ok.cc", @@ -525,7 +514,6 @@ var rules = [ "bartvpn.com", "bastillepost.com", "bayvoice.net", - "baywords.com", "bb-chat.tv", "bbc.co.uk", "bbc.com", @@ -583,6 +571,7 @@ var rules = [ "bet365.com", "betaclouds.net", "betfair.com", + "betterhash.net", "betternet.co", "bettervpn.com", "bettween.com", @@ -616,6 +605,7 @@ var rules = [ "biliworld.com", "billypan.com", "binance.com", + "binance.org", "binance.us", "binancezh.cc", "bing.com", @@ -633,7 +623,6 @@ var rules = [ "bitfinex.com", "bitget.com", "bithumb.com", - "bitinka.com.ar", "bitmex.com", "bitshare.com", "bitsnoop.com", @@ -648,6 +637,7 @@ var rules = [ "bl-doujinsouko.com", "blacked.com", "blacklogic.com", + "blackmagicdesign.com", "blackvpn.com", "blewpass.com", "blingblingsquad.net", @@ -660,7 +650,6 @@ var rules = [ "blockless.com", "blocktempo.com", "blog.de", - "blog.google", "blog.jp", "blogblog.com", "blogcatalog.com", @@ -756,17 +745,18 @@ var rules = [ "bodog88.com", "bolehvpn.net", "bonbonme.com", - "bonbonsex.com", "bonfoundation.org", "bongacams.com", "boobstagram.com", "book.com.tw", "bookdepository.com", "bookepub.com", + "booklive.jp", + "bookmeter.com", "books.com.tw", "booktopia.com.au", "bookwalker.com.tw", - "boomssr.com", + "bookwalker.jp", "bootstrapcdn.com", "borgenmagazine.com", "bot.nu", @@ -796,11 +786,9 @@ var rules = [ "breaking911.com", "breakingtweets.com", "breakwall.net", - "briefdream.com", "briian.com", "brill.com", "brizzly.com", - "brkmd.com", "broadbook.com", "broadpressinc.com", "brockbbs.com", @@ -811,6 +799,8 @@ var rules = [ "bsky.network", "bsky.social", "bt2mag.com", + "bt4g.org", + "bt4gprx.com", "bt95.com", "btaia.com", "btbit.net", @@ -826,6 +816,7 @@ var rules = [ "btguard.com", "btku.me", "btku.org", + "btloader.com", "btspread.com", "btsynckeys.com", "budaedu.org", @@ -868,6 +859,7 @@ var rules = [ "c-est-simple.com", "c-span.org", "c-spanvideo.org", + "c.gle", "c100tibet.org", "c2cx.com", "c3pool.com", @@ -881,15 +873,12 @@ var rules = [ "cactusvpn.com", "cafepress.com", "cahr.org.tw", - "caijinglengyan.com", "calameo.com", "calebelston.com", "calendarz.com", "calgarychinese.ca", "calgarychinese.com", "calgarychinese.net", - "calibre-ebook.com", - "caltech.edu", "cam4.com", "cam4.jp", "cam4.sg", @@ -1021,7 +1010,6 @@ var rules = [ "chicagoncmtv.com", "china-mmm.jp.net", "china-mmm.net", - "china-mmm.sa.com", "china-review.com.ua", "china-week.com", "china101.com", @@ -1104,7 +1092,6 @@ var rules = [ "chrlawyers.hk", "chrome.com", "chromecast.com", - "chromeenterprise.google", "chromeexperiments.com", "chromercise.com", "chromestatus.com", @@ -1118,7 +1105,9 @@ var rules = [ "chushigangdrug.ch", "ci-en.jp", "cia.gov", + "cici.com", "ciciai.com", + "ciciaicdn.com", "cienen.com", "cineastentreff.de", "cipfg.org", @@ -1139,7 +1128,6 @@ var rules = [ "civilhrfront.org", "civiliangunner.com", "civilmedia.tw", - "civisec.org", "civitai.com", "cixiaoya.club", "cjb.net", @@ -1162,8 +1150,8 @@ var rules = [ "clips4sale.com", "cloakpoint.com", "cloudcone.com", + "cloudflare-dns.com", "cloudflare-ipfs.com", - "cloudfront.net", "cloudfunctions.net", "cloudokyo.cloud", "club1069.com", @@ -1192,17 +1180,15 @@ var rules = [ "cnpolitics.org", "cnproxy.com", "cnyes.com", - "co.tv", "coat.co.jp", "cobinhood.com", "cochina.co", "cochina.org", "code1984.com", - "codeplex.com", "codeshare.io", "codeskulptor.org", "cofacts.tw", - "coin2co.in", + "coffeemanga.to", "coinbase.com", "coinbene.com", "coinegg.com", @@ -1217,12 +1203,10 @@ var rules = [ "colacloud.net", "collateralmurder.com", "collateralmurder.org", - "com.google", "com.uk", "comedycentral.com", "comefromchina.com", "comic-mega.me", - "comico.tw", "commandarms.com", "comments.app", "commentshk.com", @@ -1251,10 +1235,13 @@ var rules = [ "cotweet.com", "counter.social", "coursehero.com", + "covenantswatch.org.tw", "coze.com", "cpj.org", + "cpu-monkey.com", "cq99.us", "crackle.com", + "crashlytics.com", "crazypool.org", "crazys.cc", "crazyshit.com", @@ -1264,7 +1251,6 @@ var rules = [ "creaders.net", "creadersnet.com", "creativelab5.com", - "crisisresponse.google", "cristyli.com", "crocotube.com", "crossfire.co.kr", @@ -1287,6 +1273,7 @@ var rules = [ "ct.org.tw", "ctao.org", "ctfriend.net", + "ctinews.com", "ctitv.com.tw", "ctowc.org", "cts.com.tw", @@ -1312,7 +1299,6 @@ var rules = [ "cz.cc", "d-fukyu.com", "d.cash", - "d0z.net", "d100.net", "d2bay.com", "d2pass.com", @@ -1407,10 +1393,11 @@ var rules = [ "delicious.com", "democrats.org", "demosisto.hk", + "deno.com", + "deno.dev", "depositphotos.com", "derekhsu.homeip.net", "desc.se", - "design.google", "desipro.de", "dessci.com", "destroy-china.jp", @@ -1435,7 +1422,6 @@ var rules = [ "digitalnomadsproject.org", "diigo.com", "dilber.se", - "dingchin.com.tw", "dipity.com", "directcreative.com", "discoins.com", @@ -1474,7 +1460,6 @@ var rules = [ "dmm.com", "dns-dns.com", "dns-stuff.com", - "dns.google", "dns04.com", "dns05.com", "dns1.us", @@ -1496,10 +1481,8 @@ var rules = [ "dok-forum.net", "dolc.de", "dolf.org.hk", - "dollf.com", "domain.club.tw", "domain.glass", - "domains.google", "domaintoday.com.au", "donga.com", "dongtaiwang.com", @@ -1508,6 +1491,7 @@ var rules = [ "donmai.us", "dontfilter.us", "dontmovetochina.com", + "doom9.org", "doosho.com", "doourbest.org", "dorjeshugden.com", @@ -1518,10 +1502,8 @@ var rules = [ "doubibackup.com", "doubiyunbackup.com", "doublethinklab.org", - "doubmirror.cf", "douchi.space", "dougscripts.com", - "douhokanko.net", "doujincafe.com", "dowei.org", "dowjones.com", @@ -1545,14 +1527,12 @@ var rules = [ "dsmtp.com", "dssott.com", "dstk.dk", - "dtdns.net", "dtiblog.com", "dtic.mil", "dtwang.org", "duanzhihu.com", "dubox.com", "duck.com", - "duckdns.org", "duckduckgo.com", "duckload.com", "duckmylife.com", @@ -1602,6 +1582,7 @@ var rules = [ "e-traderland.net", "e-zone.com.hk", "e123.hk", + "e621.net", "earlytibet.com", "earthcam.com", "earthvpn.com", @@ -1621,7 +1602,6 @@ var rules = [ "ebookee.com", "ebtcbank.com", "ecfa.org.tw", - "echainhost.com", "echofon.com", "ecimg.tw", "eckosia.org", @@ -1635,6 +1615,7 @@ var rules = [ "edmontonservice.com", "edns.biz", "edoors.com", + "edrdg.org", "edubridge.com", "edupro.org", "edx-cdn.org", @@ -1678,8 +1659,6 @@ var rules = [ "enlighten.org.tw", "entermap.com", "entnt.com", - "environment.google", - "epa.gov.tw", "epac.to", "episcopalchurch.org", "epochhk.com", @@ -1748,7 +1727,6 @@ var rules = [ "evozi.com", "evschool.net", "exam.gov.tw", - "exblog.co.jp", "exblog.jp", "exchristian.hk", "excite.co.jp", @@ -1766,7 +1744,6 @@ var rules = [ "extmatrix.com", "extrabux.com", "extremetube.com", - "exx.com", "ey.gov.tw", "eyevio.jp", "eyny.com", @@ -1877,7 +1854,6 @@ var rules = [ "fc2cn.com", "fc2web.com", "fda.gov.tw", - "fdbox.com", "fdc64.de", "fdc64.jp", "fdc64.org", @@ -1913,6 +1889,7 @@ var rules = [ "filmingfortibet.org", "filthdump.com", "financetwitter.com", + "financialexpress.com", "finchvpn.com", "findbook.tw", "findmespot.com", @@ -1977,7 +1954,6 @@ var rules = [ "fountmedia.io", "fourthinternational.org", "foxbusiness.com", - "foxdie.us", "foxgay.com", "foxsub.com", "foxtang.com", @@ -2086,6 +2062,8 @@ var rules = [ "ftx.com", "fucd.com", "fuchsia.dev", + "fuckccp.com", + "fuckccp.xyz", "fuckcnnic.net", "fuckgfw.org", "fuckgfw233.org", @@ -2101,7 +2079,7 @@ var rules = [ "furbo.org", "furhhdl.org", "furinkan.com", - "furl.net", + "furrybar.com", "futurechinaforum.org", "futuremessage.org", "fux.com", @@ -2147,6 +2125,7 @@ var rules = [ "gardennetworks.com", "gardennetworks.org", "gartlive.com", + "garudalinux.org", "gate-project.com", "gate.io", "gatecoin.com", @@ -2191,7 +2170,6 @@ var rules = [ "getchu.com", "getcloak.com", "getfoxyproxy.org", - "getfreedur.com", "getgom.com", "geti2p.net", "getiton.com", @@ -2221,6 +2199,7 @@ var rules = [ "ghanely.me", "ghidra-sre.org", "ghostpath.com", + "ghproxy.com", "ghut.org", "giantessnight.com", "gifree.com", @@ -2239,6 +2218,8 @@ var rules = [ "githubassets.com", "githubcopilot.com", "githubusercontent.com", + "gitlab.com", + "gitlab.net", "gizlen.net", "gjczz.com", "glarity.app", @@ -2264,6 +2245,7 @@ var rules = [ "gmiddle.net", "gmll.org", "gmodules.com", + "gmp4.com", "gmx.net", "gnci.org.hk", "gnews.org", @@ -2345,6 +2327,7 @@ var rules = [ "google.cl", "google.cm", "google.cn", + "google.co", "google.co.ao", "google.co.bw", "google.co.ck", @@ -2531,7 +2514,6 @@ var rules = [ "googlehosted.com", "googleideas.com", "googleinsidesearch.com", - "googlelabs.com", "googlelocal.nl", "googlemail.com", "googlemaps.sv", @@ -2540,7 +2522,6 @@ var rules = [ "googleplay.com", "googleplus.com", "googlescholar.com", - "googlesile.com", "googlesource.com", "googlesyndication.com", "googleusercontent.com", @@ -2555,8 +2536,10 @@ var rules = [ "got-game.org", "gotdns.ch", "gotgeeks.com", + "gotquestions.org", "gotrusted.com", "gotw.ca", + "gov.ir", "gov.taipei", "gov.tw", "gr8domain.biz", @@ -2575,7 +2558,6 @@ var rules = [ "great-roc.org", "greatfire.org", "greatfirewall.biz", - "greatfirewallofchina.net", "greatfirewallofchina.org", "greatroc.org", "greatroc.tw", @@ -2591,7 +2573,6 @@ var rules = [ "grok.com", "grotty-monday.com", "ground.news", - "grow.google", "gs-discuss.com", "gsearch.media", "gstatic.com", @@ -2606,7 +2587,6 @@ var rules = [ "guancha.org", "guaneryu.com", "guangming.com.my", - "guangnianvpn.com", "guardster.com", "guishan.org", "gumroad.com", @@ -2693,7 +2673,6 @@ var rules = [ "helpeachpeople.com", "helplinfen.com", "helpster.de", - "helpuyghursnow.org", "helpzhuling.org", "henduohao.com", "hentai.to", @@ -2705,12 +2684,12 @@ var rules = [ "heritage.org", "heroku.com", "herokuapp.com", + "herominers.com", "heungkongdiscuss.com", "hexieshe.com", "hexieshe.xyz", "hexxeh.net", "heyuedi.com", - "heywire.com", "heyzo.com", "hgamefree.info", "hgseav.com", @@ -2769,7 +2748,6 @@ var rules = [ "hkcmi.edu", "hkcnews.com", "hkcoc.com", - "hkctu.org.hk", "hkdailynews.com.hk", "hkday.net", "hkdc.us", @@ -2783,13 +2761,10 @@ var rules = [ "hkgalden.com", "hkgolden.com", "hkgpao.com", - "hkgreenradio.org", "hkheadline.com", "hkhkhk.com", "hkhrc.org.hk", - "hkhrm.org.hk", "hkip.org.uk", - "hkja.org.hk", "hkjc.com", "hkjp.org", "hklft.com", @@ -2804,7 +2779,6 @@ var rules = [ "hkusu.net", "hkvwet.com", "hkwcc.org.hk", - "hkzone.org", "hmoegirl.com", "hmonghot.com", "hmv.co.jp", @@ -2839,6 +2813,7 @@ var rules = [ "hotair.com", "hotav.tv", "hotcoin.com", + "hotcool.tw", "hotels.cn", "hotfrog.com.tw", "hotgoo.com", @@ -2930,7 +2905,6 @@ var rules = [ "huyandex.com", "hwadzan.tw", "hwayue.org.tw", - "hwinfo.com", "hxwk.org", "hxwq.org", "hybrid-analysis.com", @@ -2952,7 +2926,6 @@ var rules = [ "iav19.com", "iavian.net", "ibiblio.org", - "ibit.am", "iblist.com", "iblogserv-f.net", "ibros.org", @@ -2965,7 +2938,6 @@ var rules = [ "icij.org", "icl-fi.org", "icoco.com", - "iconfactory.net", "iconpaper.org", "icu-project.org", "idaiwan.com", @@ -2973,14 +2945,13 @@ var rules = [ "identi.ca", "idiomconnection.com", "idlcoyote.com", + "idope.se", "idouga.com", "idreamx.com", - "idsam.com", "idv.tw", "ieasy5.com", "ied2k.net", "ienergy1.com", - "iepl.us", "ifanqiang.com", "ifcss.org", "ifjc.org", @@ -3055,7 +3026,6 @@ var rules = [ "illusionfactory.com", "ilove80.be", "ilovelongtoes.com", - "im.tv", "im88.tw", "imageab.com", "imagefap.com", @@ -3088,6 +3058,7 @@ var rules = [ "incloak.com", "incredibox.fr", "independent.co.uk", + "india.com", "indiablooms.com", "indianarrative.com", "indiandefensenews.in", @@ -3098,7 +3069,7 @@ var rules = [ "indsr.org.tw", "info-graf.fr", "informer.com", - "ingress.com", + "infura.io", "inherit.live", "initiativesforchina.org", "inkbunny.net", @@ -3122,7 +3093,6 @@ var rules = [ "inthenameofconfuciusmovie.com", "invidio.us", "inxian.com", - "iownyour.biz", "iownyour.org", "ipalter.com", "ipdefenseforum.com", @@ -3133,6 +3103,7 @@ var rules = [ "iphonetaiwan.org", "iphonix.fr", "ipicture.ru", + "ipify.org", "ipjetable.net", "ipobar.com", "ipoock.com", @@ -3142,9 +3113,10 @@ var rules = [ "iptv.com.tw", "iptvbin.com", "ipvanish.com", - "iqiyi.com", + "irangov.ir", "iredmail.org", "irib.ir", + "irna.ir", "ironpython.net", "ironsocket.com", "is-a-hunter.com", @@ -3182,6 +3154,7 @@ var rules = [ "itemdb.com", "itemfix.com", "ithome.com.tw", + "itiger.com", "itsaol.com", "itshidden.com", "itsky.it", @@ -3222,6 +3195,7 @@ var rules = [ "javakiba.org", "javbus.co", "javbus.com", + "javbus.sbs", "javdb.com", "javfinder.ai", "javfor.me", @@ -3236,6 +3210,7 @@ var rules = [ "javmoo.xyz", "javseen.com", "javtag.com", + "javtrailers.com", "javzoo.com", "javzz.com", "jbtalks.cc", @@ -3330,7 +3305,6 @@ var rules = [ "jwmusic.org", "jwplayer.com", "jyxf.net", - "k-doujin.net", "ka-wai.com", "kadokawa.co.jp", "kagyu.org", @@ -3379,7 +3353,6 @@ var rules = [ "kichiku-doujinko.com", "kik.com", "killwall.com", - "kimy.com.tw", "kindle4rss.com", "kindleren.com", "kingdomsalvation.org", @@ -3402,6 +3375,7 @@ var rules = [ "knowledgerush.com", "knowyourmeme.com", "ko-fi.com", + "kobe-np.co.jp", "kobo.com", "kobobooks.com", "kodingen.com", @@ -3416,6 +3390,7 @@ var rules = [ "kpkuang.org", "kqes.net", "kraken.com", + "krtc.com.tw", "krtco.com.tw", "ksdl.org", "ksnews.com.tw", @@ -3430,7 +3405,6 @@ var rules = [ "kurtmunger.com", "kusocity.com", "kwcg.ca", - "kwok7.com", "kwongwah.com.my", "kxsw.life", "kyofun.com", @@ -3459,7 +3433,6 @@ var rules = [ "laomiu.com", "laowang.vip", "laoyang.info", - "laptoplockdown.com", "laqingdan.net", "larsgeorge.com", "lastcombat.com", @@ -3469,6 +3442,8 @@ var rules = [ "lausan.hk", "law.com", "lbank.info", + "ldplayer.net", + "ldplayer.tw", "le-vpn.com", "leafyvpn.net", "lecloud.net", @@ -3485,15 +3460,11 @@ var rules = [ "lematin.ch", "lemonde.fr", "lenwhite.com", - "leorockwell.com", "lerosua.org", - "lers.google", "lesoir.be", "lester850.info", "letou.com", "letscorp.net", - "letsencrypt.org", - "levyhsu.com", "lflink.com", "lflinkup.com", "lflinkup.net", @@ -3549,7 +3520,6 @@ var rules = [ "litenews.hk", "lithium.com", "liu-xiaobo.org", - "liudejun.com", "liuhanyu.com", "liujianshu.com", "liuxiaobo.net", @@ -3569,7 +3539,6 @@ var rules = [ "lkcn.net", "llss.me", "lmsys.org", - "lncn.org", "load.to", "lobsangwangyal.com", "localbitcoins.com", @@ -3608,6 +3577,8 @@ var rules = [ "lsxszzg.com", "ltn.com.tw", "luckydesigner.space", + "luckymobile.ca", + "ludepress.com", "luke54.com", "luke54.org", "lupm.org", @@ -3641,6 +3612,7 @@ var rules = [ "mail.ru", "mailchimp.com", "maildns.xyz", + "mainichi.jp", "maiplus.com", "maizhong.org", "makemymood.com", @@ -3689,17 +3661,17 @@ var rules = [ "matome-plus.com", "matome-plus.net", "matrix.org", - "matsushimakaede.com", "matters.news", "matters.town", "mattwilcox.net", - "maturejp.com", + "maxai.co", "maxing.jp", "mayimayi.com", "mcadforums.com", "mcaf.ee", "mcfog.com", "mcreasite.com", + "mcusercontent.com", "md-t.org", "me.com", "me.me", @@ -3754,6 +3726,7 @@ var rules = [ "metacafe.com", "metacubex.one", "metafilter.com", + "metamask.io", "metart.com", "metarthunter.com", "meteorshowersonline.com", @@ -3770,6 +3743,7 @@ var rules = [ "mh4u.org", "mhradio.org", "mi.com", + "miami-airport.com", "michaelmarketl.com", "microsoft.com", "microvpn.com", @@ -3808,6 +3782,7 @@ var rules = [ "miniforum.org", "miningpoolhub.com", "ministrybooks.org", + "minjian-danganguan.org", "minzhuhua.net", "minzhuzhanxian.com", "minzhuzhongguo.org", @@ -3818,6 +3793,7 @@ var rules = [ "mirrormedia.com.tw", "mirrormedia.mg", "missav.com", + "missav.ws", "mist.vip", "mit.edu", "mitao.com.tw", @@ -3849,18 +3825,18 @@ var rules = [ "moeerolibrary.com", "moegirl.org", "moeshare.cc", + "moeyy.xyz", "mofa.gov.tw", "mofaxiehui.com", "mofos.com", "mog.com", "mohu.club", - "mohu.ml", "mohu.rocks", + "moj.gov.tw", "mojim.com", "mol.gov.tw", "molihua.org", "momoshop.com.tw", - "monar.ch", "mondex.org", "money-link.com.tw", "moneydj.com", @@ -3886,7 +3862,6 @@ var rules = [ "mos.ru", "mosucloud.site", "motherless.com", - "motiyun.com", "motor4ik.ru", "mousebreaker.com", "movements.org", @@ -3912,6 +3887,7 @@ var rules = [ "msn.com.tw", "mstdn.social", "mswe1.org", + "mt.co.kr", "mthruf.com", "mtw.tl", "mtzfile.pw", @@ -3938,7 +3914,6 @@ var rules = [ "my-private-network.co.uk", "my-proxy.com", "my03.com", - "my903.com", "myactimes.com", "myanniu.com", "myaudiocast.com", @@ -3964,7 +3939,6 @@ var rules = [ "myfreepaysite.com", "myfreshnet.com", "myftp.info", - "myftp.name", "myip.com", "myiphide.com", "myiphider.com", @@ -3998,15 +3972,14 @@ var rules = [ "mywww.biz", "myz.info", "naacoalition.org", - "nabble.com", "naitik.net", + "naixi.net", "nakido.com", "nakuz.com", "nalandabodhi.org", "nalandawest.org", "namgyal.org", "namgyalmonastery.org", - "namsisi.com", "nanhuyt.com", "nanopool.org", "nanyang.com", @@ -4027,6 +4000,7 @@ var rules = [ "nationsonline.org", "nationwide.com", "naughtyamerica.com", + "naver.com", "naver.jp", "navy.mil", "naweeklytimes.com", @@ -4047,12 +4021,10 @@ var rules = [ "nekoslovakia.net", "nengcard.com", "neo-miracle.com", + "neoforged.net", "neowin.net", - "nepusoku.com", "nesnode.com", - "net-fits.pro", "netalert.me", - "netbig.com", "netbirds.com", "netcolony.com", "netfirms.com", @@ -4075,6 +4047,7 @@ var rules = [ "newchen.com", "newgrounds.com", "newhighlandvision.com", + "newindianexpress.com", "newipnow.com", "newlandmagazine.com.au", "newmitbbs.com", @@ -4118,27 +4091,29 @@ var rules = [ "nflximg.net", "nflxso.net", "nflxvideo.net", + "nftstorage.link", "ng.mil", "nga.mil", "ngensis.com", - "ngodupdongchung.com", "nhentai.net", "nhi.gov.tw", "nhk-ondemand.jp", - "nic.google", "nic.gov", "nicovideo.jp", "nighost.org", "nightlife141.com", "nightswatch.top", "nike.com", + "nikke-en.com", + "nikke-jp.com", + "nikke-kr.com", "nikkei.com", "ninecommentaries.com", "ning.com", "ninjacloak.com", "ninjaproxy.ninja", "nintendium.com", - "ninth.biz", + "nirsoft.net", "nitter.cc", "nitter.net", "niu.moe", @@ -4156,7 +4131,6 @@ var rules = [ "nobodycanstop.us", "nodeseek.com", "nodesnoop.com", - "nofile.io", "nokogiri.org", "nokola.com", "noodlevpn.com", @@ -4168,6 +4142,7 @@ var rules = [ "nordstromrack.com", "nordvpn.com", "nos.nl", + "note.com", "notepad-plus-plus.org", "notion.site", "nottinghampost.com", @@ -4212,7 +4187,6 @@ var rules = [ "ntdtv.ru", "ntdtvla.com", "ntrfun.com", - "ntsna.gov.tw", "ntu.edu.tw", "nu.nl", "nubiles.net", @@ -4225,7 +4199,6 @@ var rules = [ "nutsvpn.work", "nuuvem.com", "nuvid.com", - "nuzcom.com", "nvdst.com", "nvquan.org", "nvtongzhisheng.org", @@ -4247,7 +4220,6 @@ var rules = [ "nytimes.com", "nytimes.map.fastly.net", "nytimg.com", - "nytlog.com", "nytstyle.com", "nzchinese.com", "nzchinese.net.nz", @@ -4285,10 +4257,10 @@ var rules = [ "okayfreedom.com", "okcoin.com", "okex.com", + "okinawatimes.co.jp", "okk.tw", "okpool.me", "okx.com", - "olabloga.pl", "old-cat.net", "olehdtv.com", "olelive.com", @@ -4326,21 +4298,20 @@ var rules = [ "onmypc.info", "onmypc.net", "onmypc.org", - "onmypc.us", "onthehunt.com", "ontrac.com", - "oopsforum.com", + "oojj.de", "open-assistant.io", "open.com.hk", "openai.com", "openallweb.com", "opendemocracy.net", + "opendesktop.org", "opendn.xyz", "openervpn.in", "openid.net", "openleaks.org", "opensea.io", - "opensource.google", "openstreetmap.org", "opentech.fund", "openvpn.net", @@ -4350,8 +4321,6 @@ var rules = [ "opera-mini.net", "opera.com", "opus-gaming.com", - "oraclecloud.com", - "orchidbbs.com", "organcare.org.tw", "organharvestinvestigation.net", "organiccrap.com", @@ -4361,9 +4330,9 @@ var rules = [ "orient-doll.com", "orientaldaily.com.my", "orn.jp", - "orzdream.com", "orzistic.org", "osfoora.com", + "osm.tw", "otcbtc.com", "otnd.org", "otto.de", @@ -4409,6 +4378,7 @@ var rules = [ "paljorpublications.com", "palmislife.com", "paltalk.com", + "pancakeswap.finance", "pandafan.pub", "pandapow.co", "pandapow.net", @@ -4432,7 +4402,6 @@ var rules = [ "partypoker.com", "passion.com", "passiontimes.hk", - "passwords.google", "paste.ee", "pastebin.com", "pastie.org", @@ -4462,7 +4431,6 @@ var rules = [ "peace.ca", "peacefire.org", "peacehall.com", - "pearlher.org", "peeasian.com", "peing.net", "pekingduck.org", @@ -4485,6 +4453,7 @@ var rules = [ "perplexity.ai", "persecutionblog.com", "persiankitty.com", + "pewresearch.org", "pfd.org.hk", "phapluan.org", "phayul.com", @@ -4502,6 +4471,7 @@ var rules = [ "picacomic.com", "picacomiccn.com", "picasaweb.com", + "picgo.net", "picidae.net", "picturedip.com", "picturesocial.com", @@ -4598,6 +4568,7 @@ var rules = [ "pornhub.com", "pornhubdeutsch.net", "pornhubpremium.com", + "pornmate.com", "pornmm.net", "pornoxo.com", "pornrapidshare.com", @@ -4616,7 +4587,6 @@ var rules = [ "post76.com", "post852.com", "postadult.com", - "postimg.org", "potato.im", "potatso.com", "potvpn.com", @@ -4629,20 +4599,23 @@ var rules = [ "pp.ru", "ppy.sh", "prayforchina.net", + "prcleader.org", "premeforwindows7.com", "premproxy.com", "presentation.new", "presentationzen.com", + "president.ir", "presidentlee.tw", + "pressreader.com", "prestige-av.com", "prettyvirgin.com", - "pride.google", "primevideo.com", "printfriendly.com", "prism-break.org", "prisoneralert.com", "pritunl.com", "privacybox.de", + "privacyguides.org", "private.com", "privateinternetaccess.com", "privatepaste.com", @@ -4668,7 +4641,6 @@ var rules = [ "proxydns.com", "proxylist.org.uk", "proxynetwork.org.uk", - "proxypy.net", "proxyroad.com", "proxytunnel.net", "proxz.com", @@ -4702,13 +4674,13 @@ var rules = [ "pure18.com", "pureapk.com", "pureconcepts.net", + "puredns.org", "pureinsight.org", "purepdf.com", "purevpn.com", "purplelotus.org", "pursuestar.com", "pushchinawall.com", - "pussthecat.org", "pussyspace.com", "putihome.org", "putlocker.com", @@ -4737,7 +4709,7 @@ var rules = [ "qiwen.lu", "qixianglu.cn", "qkshare.com", - "qmzdd.com", + "qmp4.com", "qoos.com", "qpoe.com", "qq.co.za", @@ -4745,7 +4717,6 @@ var rules = [ "qtrac.eu", "qtweeter.com", "quannengshen.org", - "quantumbooter.net", "questvisual.com", "quitccp.net", "quitccp.org", @@ -4776,10 +4747,10 @@ var rules = [ "radiotime.com", "radiovaticana.org", "radiovncr.com", + "radmin-vpn.com", "rael.org", "raggedbanner.com", "raidcall.com.tw", - "raidtalk.com.tw", "rainbowplan.org", "raindrop.io", "raizoji.or.jp", @@ -4803,11 +4774,9 @@ var rules = [ "ratx.com", "rawgit.com", "rawgithub.com", - "raxcdn.com", "razyboard.com", "rcinet.ca", "rd.com", - "rdio.com", "reabble.com", "read01.com", "read100.com", @@ -4832,6 +4801,7 @@ var rules = [ "redchinacn.org", "redd.it", "reddit.com", + "reddithelp.com", "redditlist.com", "redditmedia.com", "redditspace.com", @@ -4840,7 +4810,6 @@ var rules = [ "redtube.com", "referer.us", "reflectivecode.com", - "registry.google", "reimu.net", "relaxbbs.com", "relay.com.tw", @@ -4849,8 +4818,6 @@ var rules = [ "religioustolerance.org", "renminbao.com", "renyurenquan.org", - "rerouted.org", - "research.google", "resilio.com", "resistchina.org", "retweeteffect.com", @@ -4868,8 +4835,6 @@ var rules = [ "rferl.org", "rfi.fr", "rfi.my", - "rightbtc.com", - "rightster.com", "rigpa.org", "riku.me", "rileyguide.com", @@ -4900,7 +4865,6 @@ var rules = [ "rotten.com", "rou.video", "roucdn.link", - "rpglogs.com", "rsdlmonitor.com", "rsf-chinese.org", "rsf.org", @@ -4917,6 +4881,7 @@ var rules = [ "ruanyifeng.com", "rukor.org", "rule34.xxx", + "rule34video.com", "rumble.com", "runbtx.com", "rushbee.com", @@ -4932,9 +4897,15 @@ var rules = [ "s-nbcnews.com", "s1heng.com", "s1s1s1.com", + "s3-ap-northeast-1.amazonaws.com", + "s3-ap-northeast-2.amazonaws.com", "s3-ap-southeast-1.amazonaws.com", "s3-ap-southeast-2.amazonaws.com", + "s3-eu-central-1.amazonaws.com", "s3.amazonaws.com", + "s3.ap-northeast-2.amazonaws.com", + "s3.eu-central-1.amazonaws.com", + "s3.us-east-1.amazonaws.com", "s4miniarchive.com", "s8forum.com", "saboom.com", @@ -4946,10 +4917,8 @@ var rules = [ "safechat.com", "safeguarddefenders.com", "safervpn.com", - "safety.google", "sagernet.org", "saintyculture.com", - "saiq.me", "sakura-paris.org", "sakuralive.com", "sakya.org", @@ -4974,7 +4943,6 @@ var rules = [ "savetibetstore.org", "saveuighur.org", "savevid.com", - "say2.info", "sbme.me", "sbs.com.au", "scasino.com", @@ -4982,6 +4950,7 @@ var rules = [ "sciencemag.org", "sciencenets.com", "scieron.com", + "sclub.com.tw", "scmp.com", "scmpchinese.com", "scramble.io", @@ -5023,7 +4992,6 @@ var rules = [ "sethwklein.net", "setn.com", "settv.com.tw", - "setty.com.tw", "sevenload.com", "sex-11.com", "sex.com", @@ -5036,7 +5004,6 @@ var rules = [ "sexidude.com", "sexinsex.net", "sextvx.com", - "sexxxy.biz", "sf.net", "sfileydy.com", "sfshibao.com", @@ -5054,7 +5021,6 @@ var rules = [ "shadowsocks.com.hk", "shadowsocks.nu", "shadowsocks.org", - "shadowsocks9.com", "shafaqna.com", "shahit.biz", "shambalapost.com", @@ -5086,7 +5052,6 @@ var rules = [ "shicheng.org", "shiksha.com", "shiksha.ws", - "shinychan.com", "shipcamouflage.com", "shireyishunjian.com", "shitaotv.org", @@ -5102,7 +5067,6 @@ var rules = [ "showtime.jp", "showwe.tw", "shutterstock.com", - "shvoong.com", "shwchurch.org", "shwchurch3.com", "siddharthasintent.org", @@ -5119,9 +5083,9 @@ var rules = [ "simplecd.org", "simpleproductivityblog.com", "simpleswap.io", + "simplex.chat", "sina.com", "sina.com.hk", - "sina.com.tw", "sinchew.com.my", "singaporepools.com.sg", "singfortibet.com", @@ -5209,7 +5173,6 @@ var rules = [ "soc.mil", "social.edu.ci", "socialblade.com", - "socialwhale.com", "socks-proxy.net", "sockscap64.com", "sockslist.net", @@ -5220,9 +5183,8 @@ var rules = [ "softether.org", "softfamous.com", "softlayer.net", - "softnology.biz", "softonic.cn", - "softsmirror.cf", + "softonic.com", "softwarebychuck.com", "sogclub.com", "sogoo.org", @@ -5256,7 +5218,6 @@ var rules = [ "soubory.com", "soul-plus.net", "soulcaliburhentai.net", - "soumo.info", "soundcloud.com", "soundofhope.kr", "soundofhope.org", @@ -5325,6 +5286,7 @@ var rules = [ "stackoverflow.com", "stacksocial.com", "stage64.hk", + "standard.co.uk", "standupfortibet.org", "standwithhk.org", "stanford.edu", @@ -5334,6 +5296,7 @@ var rules = [ "startuplivingchina.com", "stat.gov.tw", "state.gov", + "statearmor.org", "static-economist.com", "statically.io", "staticflickr.com", @@ -5361,11 +5324,11 @@ var rules = [ "stoporganharvesting.org", "stoptibetcrisis.net", "storagenewsletter.com", - "stories.google", "storify.com", "storj.io", "storm.mg", "stormmediagroup.com", + "storry.tv", "stoweboyd.com", "straitstimes.com", "stranabg.com", @@ -5382,9 +5345,11 @@ var rules = [ "strongwindpress.com", "student.tw", "studentsforafreetibet.org", + "studybuddhism.com", "stumbleupon.com", "stupidvideos.com", "stweetly.com", + "subhd.tv", "substack.com", "successfn.com", "sueddeutsche.de", @@ -5392,7 +5357,6 @@ var rules = [ "sugobbs.com", "sugumiru18.com", "suissl.com", - "sulian.me", "summify.com", "sumrando.com", "sun1911.com", @@ -5407,7 +5371,6 @@ var rules = [ "suoluo.org", "supchina.com", "superfreevpn.com", - "superokayama.com", "superpages.com", "supervpn.net", "superzooi.com", @@ -5419,7 +5382,6 @@ var rules = [ "surfshark.com", "suroot.com", "surrenderat20.net", - "sustainability.google", "suyangg.com", "svsfx.com", "swagbucks.com", @@ -5437,7 +5399,6 @@ var rules = [ "syosetu.com", "sysresccd.org", "sytes.net", - "syx86.cn", "syx86.com", "szbbs.net", "szetowah.org.hk", @@ -5484,6 +5445,7 @@ var rules = [ "taiwanus.net", "taiwanyes.com", "talk853.com", + "talkatone.com", "talkboxapp.com", "talkcc.com", "talkonly.net", @@ -5492,6 +5454,7 @@ var rules = [ "tanc.org", "tangben.com", "tangren.us", + "tanks.gg", "taoism.net", "taolun.info", "tapanwap.com", @@ -5505,13 +5468,11 @@ var rules = [ "taweet.com", "tbcollege.org", "tbi.org.hk", - "tbicn.org", "tbjyt.org", - "tbpic.info", "tbrc.org", "tbs-rainbow.org", + "tbs.co.jp", "tbsec.org", - "tbsmalaysia.org", "tbsn.org", "tbsseattle.org", "tbssqh.org", @@ -5544,8 +5505,8 @@ var rules = [ "teepr.com", "tehrantimes.com", "telecomspace.com", + "telega.one", "telegra.ph", - "telegram-cdn.org", "telegram.dog", "telegram.me", "telegram.org", @@ -5553,6 +5514,7 @@ var rules = [ "telegramdownload.com", "telegraph.co.uk", "telesco.pe", + "tellapart.com", "tellme.pw", "tenacy.com", "tenor.com", @@ -5566,6 +5528,7 @@ var rules = [ "tfhub.dev", "tfiflve.com", "tg-me.com", + "tg.dev", "tgstat.com", "thaicn.com", "thb.gov.tw", @@ -5590,11 +5553,11 @@ var rules = [ "thedw.us", "theepochtimes.com", "thefacebook.com", - "thefrontier.hk", "thegay.com", "thegioitinhoc.vn", "thegly.com", "theguardian.com", + "thehansindia.com", "thehindu.com", "thehots.info", "thehousenews.com", @@ -5606,7 +5569,6 @@ var rules = [ "theporndude.com", "theportalwiki.com", "theprint.in", - "thereallove.kr", "therock.net.nz", "thesaturdaypaper.com.au", "thespeeder.com", @@ -5620,7 +5582,6 @@ var rules = [ "thetibetpost.com", "thetrotskymovie.com", "thetvdb.com", - "thevivekspot.com", "thewgo.org", "thewirechina.com", "theync.com", @@ -5634,6 +5595,7 @@ var rules = [ "thomasbernhard.org", "thongdreams.com", "threadreaderapp.com", + "threads.com", "threads.net", "threatchaos.com", "throughnightsfire.com", @@ -5738,10 +5700,12 @@ var rules = [ "ticket.com.tw", "tigervpn.com", "tiktok.com", + "tiktokcdn-eu.com", "tiktokcdn-us.com", "tiktokcdn.com", "tiktokv.com", "tiktokv.us", + "tiktokw.us", "tiltbrush.com", "timdir.com", "time.com", @@ -5752,7 +5716,6 @@ var rules = [ "tiney.com", "tineye.com", "tingtalk.me", - "tintuc101.com", "tiny.cc", "tinychat.com", "tinypaste.com", @@ -5764,13 +5727,11 @@ var rules = [ "tl.gd", "tma.co.jp", "tmagazine.com", - "tmdfish.com", "tmi.me", "tmpp.org", "tnaflix.com", - "tngrnow.com", - "tngrnow.net", "tnp.org", + "tnt-ea.com", "to-porno.com", "togetter.com", "toh.info", @@ -5783,7 +5744,6 @@ var rules = [ "tomonews.net", "tomp3.cc", "tongil.or.kr", - "tono-oka.jp", "tonyyan.net", "toodoc.com", "toonel.net", @@ -5867,7 +5827,6 @@ var rules = [ "tuidang.net", "tuidang.org", "tuidang.se", - "tuitui.info", "tuitwit.com", "tukaani.org", "tumblr.com", @@ -5896,11 +5855,9 @@ var rules = [ "turkistantimes.com", "turntable.fm", "tushycash.com", - "tutanota.com", "tuvpn.com", "tuzaijidi.com", "tv.com", - "tv.google", "tvants.com", "tvb.com", "tvboxnow.com", @@ -5968,11 +5925,8 @@ var rules = [ "twisternow.com", "twistory.net", "twit2d.com", - "twitbrowser.net", - "twitcause.com", "twitch.tv", "twitchcdn.net", - "twitgether.com", "twitgoo.com", "twitiq.com", "twitlonger.com", @@ -6009,6 +5963,7 @@ var rules = [ "twttr.com", "twurl.nl", "twyac.org", + "tx.me", "txxx.com", "tycool.com", "typekit.net", @@ -6031,6 +5986,7 @@ var rules = [ "udn.com.tw", "udnbkk.com", "udndata.com", + "udomain.hk", "uforadio.com.tw", "ufreevpn.com", "ugo.com", @@ -6077,6 +6033,7 @@ var rules = [ "untraceable.us", "unwire.hk", "uocn.org", + "upbit.com", "updatestar.com", "upghsbc.com", "upholdjustice.org", @@ -6130,7 +6087,6 @@ var rules = [ "uyghuraa.org", "uyghuramerican.org", "uyghurbiz.org", - "uyghurcanadian.ca", "uyghurcanadiansociety.org", "uyghurcongress.org", "uyghurensemble.co.uk", @@ -6148,7 +6104,6 @@ var rules = [ "v2ray.com", "v2raya.org", "v2raycn.com", - "v2raytech.com", "valeursactuelles.com", "van001.com", "van698.com", @@ -6203,7 +6158,6 @@ var rules = [ "virginia.edu", "virtualrealporn.com", "visibletweets.com", - "visiontimes.com", "visualstudio.com", "vital247.org", "viu.com", @@ -6283,12 +6237,12 @@ var rules = [ "vpnworldwide.com", "vporn.com", "vpser.net", + "vpsxb.net", "vraiesagesse.net", "vrchat.com", "vrmtr.com", "vrporn.com", "vrsmash.com", - "vs.com", "vtunnel.com", "vuku.cc", "vultryhw.com", @@ -6297,6 +6251,7 @@ var rules = [ "w-pool.com", "w.wiki", "w3.org", + "w3s.link", "waffle1999.com", "wahas.com", "waigaobu.com", @@ -6304,6 +6259,7 @@ var rules = [ "wailaike.net", "wainao.me", "waiwaier.com", + "walletconnect.com", "wallhaven.cc", "wallmama.com", "wallornot.org", @@ -6323,7 +6279,6 @@ var rules = [ "want-daily.com", "wanz-factory.com", "wapedia.mobi", - "warehouse333.com", "warroom.org", "waselpro.com", "washeng.net", @@ -6364,7 +6319,6 @@ var rules = [ "webworkerdaily.com", "wechatlawsuit.com", "weebly.com", - "weekmag.info", "wefightcensorship.org", "wefong.com", "weiboleak.com", @@ -6380,7 +6334,6 @@ var rules = [ "wengewang.com", "wengewang.org", "wenhui.ch", - "wenweipo.com", "wenxuecity.com", "wenyunchao.com", "wenzhao.ca", @@ -6395,7 +6348,6 @@ var rules = [ "wezhiyong.org", "wezone.net", "wforum.com", - "wha.la", "whatblocked.com", "whatbrowser.org", "whats.new", @@ -6446,9 +6398,7 @@ var rules = [ "williamhill.com", "willw.net", "wilsoncenter.org", - "windowsphoneme.com", "windscribe.com", - "windy.com", "wingamestore.com", "wingy.site", "winning11.com", @@ -6488,7 +6438,6 @@ var rules = [ "workerempowerment.org", "workers.dev", "workersthebig.net", - "workflow.is", "worldcat.org", "worldjournal.com", "worldpopulationreview.com", @@ -6529,6 +6478,8 @@ var rules = [ "wwitv.com", "www1.biz", "wwwhost.biz", + "wxw.cat", + "wxw.moe", "wzyboy.im", "x-art.com", "x-berry.com", @@ -6569,6 +6520,7 @@ var rules = [ "xiaoma.org", "xiaomi.eu", "xiaxiaoqiang.net", + "xicons.org", "xiezhua.com", "xihua.es", "xinbao.de", @@ -6581,7 +6533,6 @@ var rules = [ "xinyubbs.net", "xiongpian.com", "xiuren.org", - "xixicui.icu", "xizang-zhiye.org", "xjp.cc", "xjtravelguide.com", @@ -6610,7 +6561,6 @@ var rules = [ "xsden.info", "xsden.org", "xskywalker.com", - "xskywalker.net", "xt.com", "xt.pub", "xtube.com", @@ -6633,7 +6583,6 @@ var rules = [ "xxx.xxx", "xxxfuckmom.com", "xxxx.com.au", - "xxxy.biz", "xxxy.info", "xxxymovies.com", "xys.org", @@ -6647,6 +6596,7 @@ var rules = [ "yahoo.com.hk", "yahoo.com.tw", "yahoo.net", + "yahooinc.com", "yahoosandbox.com", "yakbutterblues.com", "yam.com", @@ -6657,6 +6607,7 @@ var rules = [ "yandex.ru", "yanghengjun.com", "yangjianli.com", + "yangzhi.org", "yasni.co.uk", "yasukuni.or.jp", "yayabay.com", @@ -6686,9 +6637,11 @@ var rules = [ "yinlei.org", "yipub.com", "yizhihongxing.com", + "ylive.jp", "yobit.net", "yobt.com", "yobt.tv", + "yodobashi.com", "yogichen.org", "yolasite.com", "yomiuri.co.jp", @@ -6713,7 +6666,6 @@ var rules = [ "yourtrap.com", "yousendit.com", "youshun12.com", - "youthforfreechina.org", "youthnetradio.org", "youthwant.com.tw", "youtu.be", @@ -6748,8 +6700,13 @@ var rules = [ "yyjlymb.xyz", "yysub.net", "yzzk.com", + "z-lib.fm", + "z-lib.fo", + "z-lib.gd", + "z-lib.gl", "z-lib.io", "z-lib.org", + "z-library.sk", "zacebook.com", "zalmos.com", "zamimg.com", @@ -6786,11 +6743,9 @@ var rules = [ "zhongguo.ca", "zhongguorenquan.org", "zhongguotese.net", - "zhongmeng.org", "zhongzidi.com", "zhoushuguang.com", "zhreader.com", - "zhuangbi.me", "zhuanxing.cn", "zhuatieba.com", "zhuichaguoji.org", @@ -6808,6 +6763,7 @@ var rules = [ "zmedia.com.tw", "zmw.cn", "zodgame.us", + "zodgame.xyz", "zoho.com", "zomobo.net", "zonaeuropa.com", @@ -6829,11 +6785,9 @@ var rules = [ "zuobiao.me", "zuola.com", "zvereff.com", - "zynaima.com", "zynamics.com", "zyns.com", "zyxel.com", - "zyzc9.com", "zzcartoon.com", "zzcloud.me", "zzux.com" From ade2db3903699ca3a29880d941b88352e185f2f1 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sun, 28 Sep 2025 19:12:17 +0800 Subject: [PATCH 026/132] Code clean --- v2rayN/ServiceLib/Manager/AppManager.cs | 2 -- v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs | 2 +- v2rayN/v2rayN.Desktop/App.axaml.cs | 1 - v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs | 2 +- 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/v2rayN/ServiceLib/Manager/AppManager.cs b/v2rayN/ServiceLib/Manager/AppManager.cs index a0518d21..2f5ccb64 100644 --- a/v2rayN/ServiceLib/Manager/AppManager.cs +++ b/v2rayN/ServiceLib/Manager/AppManager.cs @@ -1,5 +1,3 @@ -using System.Reactive; - namespace ServiceLib.Manager; public sealed class AppManager diff --git a/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs b/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs index ade01d3c..4c072712 100644 --- a/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/CheckUpdateViewModel.cs @@ -338,6 +338,6 @@ public class CheckUpdateViewModel : MyReactiveObject { return; } - found.Remarks = model.Remarks; + found.Remarks = model.Remarks; } } diff --git a/v2rayN/v2rayN.Desktop/App.axaml.cs b/v2rayN/v2rayN.Desktop/App.axaml.cs index a3468a01..9f4d605f 100644 --- a/v2rayN/v2rayN.Desktop/App.axaml.cs +++ b/v2rayN/v2rayN.Desktop/App.axaml.cs @@ -1,4 +1,3 @@ -using System.Reactive; using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; diff --git a/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs index 976c156d..f59eccb1 100644 --- a/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/StatusBarView.axaml.cs @@ -19,7 +19,7 @@ public partial class StatusBarView : ReactiveUserControl InitializeComponent(); _config = AppManager.Instance.Config; - + ViewModel = StatusBarViewModel.Instance; ViewModel?.InitUpdateView(UpdateViewHandler); From 5d6c5da9d92081d849eace39d2b9526c0655d961 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sun, 28 Sep 2025 19:12:58 +0800 Subject: [PATCH 027/132] up 7.15.0 --- v2rayN/Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2rayN/Directory.Build.props b/v2rayN/Directory.Build.props index c9f59177..69df0c7e 100644 --- a/v2rayN/Directory.Build.props +++ b/v2rayN/Directory.Build.props @@ -1,7 +1,7 @@ - 7.14.12 + 7.15.0 From e970372a9f56b4160dbfe63c301e8a5c2b038a95 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Wed, 1 Oct 2025 16:47:22 +0800 Subject: [PATCH 028/132] Fix some minor UI bugs (#8053) --- v2rayN/ServiceLib/Resx/ResUI.Designer.cs | 20 +++++++++---------- v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx | 6 +++--- v2rayN/ServiceLib/Resx/ResUI.hu.resx | 6 +++--- v2rayN/ServiceLib/Resx/ResUI.resx | 6 +++--- v2rayN/ServiceLib/Resx/ResUI.ru.resx | 6 +++--- v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx | 6 +++--- v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx | 6 +++--- .../ViewModels/DNSSettingViewModel.cs | 7 +++++++ .../Views/DNSSettingWindow.axaml | 4 +++- .../Views/DNSSettingWindow.axaml.cs | 19 +++++++++--------- .../v2rayN.Desktop/Views/SubEditWindow.axaml | 9 +++++---- v2rayN/v2rayN/Views/DNSSettingWindow.xaml | 6 +++--- v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs | 15 +++++++------- v2rayN/v2rayN/Views/SubEditWindow.xaml | 2 +- 14 files changed, 63 insertions(+), 55 deletions(-) diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index 7e848cf0..19ca4f0d 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -2913,6 +2913,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Bootstrap DNS (sing-box) 的本地化字符串。 + /// + public static string TbSBBootstrapDNS { + get { + return ResourceManager.GetString("TbSBBootstrapDNS", resourceCulture); + } + } + /// /// 查找类似 sing-box Direct Resolution Strategy 的本地化字符串。 /// @@ -2932,16 +2941,7 @@ namespace ServiceLib.Resx { } /// - /// 查找类似 sing-box DoH Resolver Server 的本地化字符串。 - /// - public static string TbSBDoHResolverServer { - get { - return ResourceManager.GetString("TbSBDoHResolverServer", resourceCulture); - } - } - - /// - /// 查找类似 Fallback DNS Resolution, Suggest IP 的本地化字符串。 + /// 查找类似 Fallback DNS Resolution, Require IP 的本地化字符串。 /// public static string TbSBFallbackDNSResolve { get { diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index f79901eb..7be84774 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -1425,11 +1425,11 @@ Resolve Outbound Domains - - sing-box DoH Resolver Server + + Bootstrap DNS (sing-box) - Fallback DNS Resolution, Suggest IP + Fallback DNS Resolution, Require IP xray Freedom Resolution Strategy diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx index e801c796..a4701f0b 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -1425,11 +1425,11 @@ Resolve Outbound Domains - - sing-box DoH Resolver Server + + Bootstrap DNS (sing-box) - Fallback DNS Resolution, Suggest IP + Fallback DNS Resolution, Require IP xray Freedom Resolution Strategy diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx index fc4b218f..94e03415 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.resx @@ -1425,11 +1425,11 @@ Resolve Outbound Domains - - sing-box DoH Resolver Server + + Bootstrap DNS (sing-box) - Fallback DNS Resolution, Suggest IP + Fallback DNS Resolution, Require IP xray Freedom Resolution Strategy diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx index 04e16eb8..c72c83cf 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1425,11 +1425,11 @@ Разрешать домены для исходящих соединений - - Сервер DoH-резолвера (sing-box) + + Bootstrap DNS (sing-box) - Резервное DNS-разрешение (рекомендуется указывать IP) + Fallback DNS Resolution, Require IP Стратегия резолвинга Freedom (Xray) diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx index ced2d74b..0d5092d0 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -1422,11 +1422,11 @@ 解析出站域名 - - sing-box DoH 解析服务器 + + Bootstrap DNS (sing-box) - 兜底解析其他 DNS 域名,建议设为 ip + 回退 DNS 解析,需指定为 IP xray freedom 解析策略 diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx index 720b7269..4ad280b0 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -1422,11 +1422,11 @@ Resolve Outbound Domains - - sing-box DoH Resolver Server + + Bootstrap DNS (sing-box) - Fallback DNS Resolution, Suggest IP + Fallback DNS Resolution, Require IP xray Freedom Resolution Strategy diff --git a/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs b/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs index 9ca2d407..4f996918 100644 --- a/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/DNSSettingViewModel.cs @@ -1,4 +1,5 @@ using System.Reactive; +using System.Reactive.Linq; using ReactiveUI; using ReactiveUI.Fody.Helpers; @@ -32,6 +33,8 @@ public class DNSSettingViewModel : MyReactiveObject [Reactive] public bool RayCustomDNSEnableCompatible { get; set; } [Reactive] public bool SBCustomDNSEnableCompatible { get; set; } + [ObservableAsProperty] public bool IsSimpleDNSEnabled { get; } + public ReactiveCommand SaveCmd { get; } public ReactiveCommand ImportDefConfig4V2rayCompatibleCmd { get; } public ReactiveCommand ImportDefConfig4SingboxCompatibleCmd { get; } @@ -55,6 +58,10 @@ public class DNSSettingViewModel : MyReactiveObject await Task.CompletedTask; }); + this.WhenAnyValue(x => x.RayCustomDNSEnableCompatible, x => x.SBCustomDNSEnableCompatible) + .Select(x => !(x.Item1 && x.Item2)) + .ToPropertyEx(this, x => x.IsSimpleDNSEnabled); + _ = Init(); } diff --git a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml index d8eed12e..cfc36aec 100644 --- a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml @@ -37,6 +37,7 @@ @@ -103,7 +104,7 @@ Grid.Column="0" Margin="{StaticResource Margin4}" VerticalAlignment="Center" - Text="{x:Static resx:ResUI.TbSBDoHResolverServer}" /> + Text="{x:Static resx:ResUI.TbSBBootstrapDNS}" /> diff --git a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs index 2718df95..ce63afa0 100644 --- a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs @@ -1,4 +1,5 @@ using System.Reactive.Disposables; +using System.Reactive.Linq; using Avalonia.Controls; using Avalonia.Interactivity; using ReactiveUI; @@ -67,16 +68,14 @@ public partial class DNSSettingWindow : WindowBase this.BindCommand(ViewModel, vm => vm.ImportDefConfig4V2rayCompatibleCmd, v => v.btnImportDefConfig4V2rayCompatible).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.ImportDefConfig4SingboxCompatibleCmd, v => v.btnImportDefConfig4SingboxCompatible).DisposeWith(disposables); - this.WhenAnyValue( - x => x.ViewModel.RayCustomDNSEnableCompatible, - x => x.ViewModel.SBCustomDNSEnableCompatible, - (ray, sb) => ray && sb - ).BindTo(this.FindControl("txtBasicDNSSettingsInvalid"), t => t.IsVisible); - this.WhenAnyValue( - x => x.ViewModel.RayCustomDNSEnableCompatible, - x => x.ViewModel.SBCustomDNSEnableCompatible, - (ray, sb) => ray && sb - ).BindTo(this.FindControl("txtAdvancedDNSSettingsInvalid"), t => t.IsVisible); + this.WhenAnyValue(x => x.ViewModel.IsSimpleDNSEnabled) + .Select(b => !b) + .BindTo(this.FindControl("txtBasicDNSSettingsInvalid"), t => t.IsVisible); + this.WhenAnyValue(x => x.ViewModel.IsSimpleDNSEnabled) + .Select(b => !b) + .BindTo(this.FindControl("txtAdvancedDNSSettingsInvalid"), t => t.IsVisible); + this.Bind(ViewModel, vm => vm.IsSimpleDNSEnabled, v => v.gridBasicDNSSettings.IsEnabled).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.IsSimpleDNSEnabled, v => v.gridAdvancedDNSSettings.IsEnabled).DisposeWith(disposables); }); } diff --git a/v2rayN/v2rayN.Desktop/Views/SubEditWindow.axaml b/v2rayN/v2rayN.Desktop/Views/SubEditWindow.axaml index 7ebca48f..211cff40 100644 --- a/v2rayN/v2rayN.Desktop/Views/SubEditWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/SubEditWindow.axaml @@ -75,6 +75,7 @@ Width="{StaticResource IconButtonWidth}" Height="{StaticResource IconButtonHeight}" Margin="{StaticResource MarginLr8}" + HorizontalAlignment="Left" Theme="{DynamicResource BorderlessButton}"> @@ -208,8 +209,8 @@ Grid.Row="9" Grid.Column="2" Margin="{StaticResource Margin4}" - Content="{x:Static resx:ResUI.TbSelectProfile}" - Click="BtnSelectPrevProfile_Click" /> + Click="BtnSelectPrevProfile_Click" + Content="{x:Static resx:ResUI.TbSelectProfile}" /> + Click="BtnSelectNextProfile_Click" + Content="{x:Static resx:ResUI.TbSelectProfile}" /> - + @@ -131,7 +131,7 @@ Margin="{StaticResource Margin8}" VerticalAlignment="Center" Style="{StaticResource ToolbarTextBlock}" - Text="{x:Static resx:ResUI.TbSBDoHResolverServer}" /> + Text="{x:Static resx:ResUI.TbSBBootstrapDNS}" /> - + diff --git a/v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs b/v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs index b5624662..d72e98bb 100644 --- a/v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs @@ -1,4 +1,5 @@ using System.Reactive.Disposables; +using System.Reactive.Linq; using System.Windows; using ReactiveUI; @@ -65,18 +66,16 @@ public partial class DNSSettingWindow this.BindCommand(ViewModel, vm => vm.ImportDefConfig4V2rayCompatibleCmd, v => v.btnImportDefConfig4V2rayCompatible).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.ImportDefConfig4SingboxCompatibleCmd, v => v.btnImportDefConfig4SingboxCompatible).DisposeWith(disposables); - this.WhenAnyValue( - x => x.ViewModel.RayCustomDNSEnableCompatible, - x => x.ViewModel.SBCustomDNSEnableCompatible, - (ray, sb) => ray && sb ? Visibility.Visible : Visibility.Collapsed) + this.WhenAnyValue(x => x.ViewModel.IsSimpleDNSEnabled) + .Select(b => b ? Visibility.Collapsed : Visibility.Visible) .BindTo(this, x => x.txtBasicDNSSettingsInvalid.Visibility) .DisposeWith(disposables); - this.WhenAnyValue( - x => x.ViewModel.RayCustomDNSEnableCompatible, - x => x.ViewModel.SBCustomDNSEnableCompatible, - (ray, sb) => ray && sb ? Visibility.Visible : Visibility.Collapsed) + this.WhenAnyValue(x => x.ViewModel.IsSimpleDNSEnabled) + .Select(b => b ? Visibility.Collapsed : Visibility.Visible) .BindTo(this, x => x.txtAdvancedDNSSettingsInvalid.Visibility) .DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.IsSimpleDNSEnabled, v => v.gridBasicDNSSettings.IsEnabled).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.IsSimpleDNSEnabled, v => v.gridAdvancedDNSSettings.IsEnabled).DisposeWith(disposables); }); WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme); } diff --git a/v2rayN/v2rayN/Views/SubEditWindow.xaml b/v2rayN/v2rayN/Views/SubEditWindow.xaml index cd665cf8..62059e60 100644 --- a/v2rayN/v2rayN/Views/SubEditWindow.xaml +++ b/v2rayN/v2rayN/Views/SubEditWindow.xaml @@ -117,7 +117,7 @@ From 5b12c36da58aed67924ce8b18eec9860691d77b1 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 1 Oct 2025 19:49:28 +0800 Subject: [PATCH 029/132] Optimize and improve, encapsulate ProcessService --- v2rayN/ServiceLib/Manager/CoreAdminManager.cs | 54 ++--- v2rayN/ServiceLib/Manager/CoreManager.cs | 112 ++++------ v2rayN/ServiceLib/Services/ProcessService.cs | 194 ++++++++++++++++++ .../ServiceLib/Services/SpeedtestService.cs | 22 +- 4 files changed, 255 insertions(+), 127 deletions(-) create mode 100644 v2rayN/ServiceLib/Services/ProcessService.cs diff --git a/v2rayN/ServiceLib/Manager/CoreAdminManager.cs b/v2rayN/ServiceLib/Manager/CoreAdminManager.cs index 90b47106..4143e78a 100644 --- a/v2rayN/ServiceLib/Manager/CoreAdminManager.cs +++ b/v2rayN/ServiceLib/Manager/CoreAdminManager.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using System.Text; using CliWrap; using CliWrap.Buffered; @@ -31,7 +30,7 @@ public class CoreAdminManager await _updateFunc?.Invoke(notify, msg); } - public async Task RunProcessAsLinuxSudo(string fileName, CoreInfo coreInfo, string configPath) + public async Task RunProcessAsLinuxSudo(string fileName, CoreInfo coreInfo, string configPath) { StringBuilder sb = new(); sb.AppendLine("#!/bin/bash"); @@ -39,50 +38,25 @@ public class CoreAdminManager sb.AppendLine($"sudo -S {cmdLine}"); var shFilePath = await FileManager.CreateLinuxShellFile("run_as_sudo.sh", sb.ToString(), true); - Process proc = new() - { - StartInfo = new() - { - FileName = shFilePath, - Arguments = "", - WorkingDirectory = Utils.GetBinConfigPath(), - UseShellExecute = false, - RedirectStandardInput = true, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true, - StandardOutputEncoding = Encoding.UTF8, - StandardErrorEncoding = Encoding.UTF8, - } - }; + var procService = new ProcessService( + fileName: shFilePath, + arguments: "", + workingDirectory: Utils.GetBinConfigPath(), + displayLog: true, + redirectInput: true, + environmentVars: null, + updateFunc: _updateFunc + ); - void dataHandler(object sender, DataReceivedEventArgs e) - { - if (e.Data.IsNotEmpty()) - { - _ = UpdateFunc(false, e.Data + Environment.NewLine); - } - } + await procService.StartAsync(AppManager.Instance.LinuxSudoPwd); - proc.OutputDataReceived += dataHandler; - proc.ErrorDataReceived += dataHandler; - - proc.Start(); - proc.BeginOutputReadLine(); - proc.BeginErrorReadLine(); - - await Task.Delay(10); - await proc.StandardInput.WriteLineAsync(AppManager.Instance.LinuxSudoPwd); - - await Task.Delay(100); - if (proc is null or { HasExited: true }) + if (procService is null or { HasExited: true }) { throw new Exception(ResUI.FailedToRunCore); } + _linuxSudoPid = procService.Id; - _linuxSudoPid = proc.Id; - - return proc; + return procService; } public async Task KillProcessAsLinuxSudo() diff --git a/v2rayN/ServiceLib/Manager/CoreManager.cs b/v2rayN/ServiceLib/Manager/CoreManager.cs index 695508c2..04f34d75 100644 --- a/v2rayN/ServiceLib/Manager/CoreManager.cs +++ b/v2rayN/ServiceLib/Manager/CoreManager.cs @@ -1,6 +1,3 @@ -using System.Diagnostics; -using System.Text; - namespace ServiceLib.Manager; /// @@ -11,8 +8,8 @@ public class CoreManager private static readonly Lazy _instance = new(() => new()); public static CoreManager Instance => _instance.Value; private Config _config; - private Process? _process; - private Process? _processPre; + private ProcessService? _processService; + private ProcessService? _processPreService; private bool _linuxSudo = false; private Func? _updateFunc; private const string _tag = "CoreHandler"; @@ -89,13 +86,13 @@ public class CoreManager await CoreStart(node); await CoreStartPreService(node); - if (_process != null) + if (_processService != null) { await UpdateFunc(true, $"{node.GetSummary()}"); } } - public async Task LoadCoreConfigSpeedtest(List selecteds) + public async Task LoadCoreConfigSpeedtest(List selecteds) { var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls) ? ECoreType.sing_box : ECoreType.Xray; var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false)); @@ -104,28 +101,22 @@ public class CoreManager await UpdateFunc(false, result.Msg); if (result.Success != true) { - return -1; + return null; } await UpdateFunc(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"))); await UpdateFunc(false, configPath); var coreInfo = CoreInfoManager.Instance.GetCoreInfo(coreType); - var proc = await RunProcess(coreInfo, fileName, true, false); - if (proc is null) - { - return -1; - } - - return proc.Id; + return await RunProcess(coreInfo, fileName, true, false); } - public async Task LoadCoreConfigSpeedtest(ServerTestItem testItem) + public async Task LoadCoreConfigSpeedtest(ServerTestItem testItem) { var node = await AppManager.Instance.GetProfileItem(testItem.IndexId); if (node is null) { - return -1; + return null; } var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false)); @@ -133,18 +124,12 @@ public class CoreManager var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, node, testItem, configPath); if (result.Success != true) { - return -1; + return null; } var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType); var coreInfo = CoreInfoManager.Instance.GetCoreInfo(coreType); - var proc = await RunProcess(coreInfo, fileName, true, false); - if (proc is null) - { - return -1; - } - - return proc.Id; + return await RunProcess(coreInfo, fileName, true, false); } public async Task CoreStop() @@ -157,16 +142,18 @@ public class CoreManager _linuxSudo = false; } - if (_process != null) + if (_processService != null) { - await ProcUtils.ProcessKill(_process, Utils.IsWindows()); - _process = null; + await _processService.StopAsync(); + _processService.Dispose(); + _processService = null; } - if (_processPre != null) + if (_processPreService != null) { - await ProcUtils.ProcessKill(_processPre, Utils.IsWindows()); - _processPre = null; + await _processPreService.StopAsync(); + _processPreService.Dispose(); + _processPreService = null; } } catch (Exception ex) @@ -188,12 +175,12 @@ public class CoreManager { return; } - _process = proc; + _processService = proc; } private async Task CoreStartPreService(ProfileItem node) { - if (_process != null && !_process.HasExited) + if (_processService != null && !_processService.HasExited) { var coreType = AppManager.Instance.GetCoreType(node, node.ConfigType); var itemSocks = await ConfigHandler.GetPreSocksItem(_config, node, coreType); @@ -210,7 +197,7 @@ public class CoreManager { return; } - _processPre = proc; + _processPreService = proc; } } } @@ -225,7 +212,7 @@ public class CoreManager #region Process - private async Task RunProcess(CoreInfo? coreInfo, string configPath, bool displayLog, bool mayNeedSudo) + private async Task RunProcess(CoreInfo? coreInfo, string configPath, bool displayLog, bool mayNeedSudo) { var fileName = CoreInfoManager.Instance.GetCoreExecFile(coreInfo, out var msg); if (fileName.IsNullOrEmpty()) @@ -256,55 +243,34 @@ public class CoreManager } } - private async Task RunProcessNormal(string fileName, CoreInfo? coreInfo, string configPath, bool displayLog) + private async Task RunProcessNormal(string fileName, CoreInfo? coreInfo, string configPath, bool displayLog) { - Process proc = new() - { - StartInfo = new() - { - FileName = fileName, - Arguments = string.Format(coreInfo.Arguments, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath).AppendQuotes() : configPath), - WorkingDirectory = Utils.GetBinConfigPath(), - UseShellExecute = false, - RedirectStandardOutput = displayLog, - RedirectStandardError = displayLog, - CreateNoWindow = true, - StandardOutputEncoding = displayLog ? Encoding.UTF8 : null, - StandardErrorEncoding = displayLog ? Encoding.UTF8 : null, - } - }; + var environmentVars = new Dictionary(); foreach (var kv in coreInfo.Environment) { - proc.StartInfo.Environment[kv.Key] = string.Format(kv.Value, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath).AppendQuotes() : configPath); + environmentVars[kv.Key] = string.Format(kv.Value, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath).AppendQuotes() : configPath); } - if (displayLog) - { - void dataHandler(object sender, DataReceivedEventArgs e) - { - if (e.Data.IsNotEmpty()) - { - _ = UpdateFunc(false, e.Data + Environment.NewLine); - } - } - proc.OutputDataReceived += dataHandler; - proc.ErrorDataReceived += dataHandler; - } - proc.Start(); + var procService = new ProcessService( + fileName: fileName, + arguments: string.Format(coreInfo.Arguments, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath).AppendQuotes() : configPath), + workingDirectory: Utils.GetBinConfigPath(), + displayLog: displayLog, + redirectInput: false, + environmentVars: environmentVars, + updateFunc: _updateFunc + ); - if (displayLog) - { - proc.BeginOutputReadLine(); - proc.BeginErrorReadLine(); - } + await procService.StartAsync(); await Task.Delay(100); - AppManager.Instance.AddProcess(proc.Handle); - if (proc is null or { HasExited: true }) + AppManager.Instance.AddProcess(procService.Handle); + if (procService is null or { HasExited: true }) { throw new Exception(ResUI.FailedToRunCore); } - return proc; + + return procService; } #endregion Process diff --git a/v2rayN/ServiceLib/Services/ProcessService.cs b/v2rayN/ServiceLib/Services/ProcessService.cs new file mode 100644 index 00000000..6b964831 --- /dev/null +++ b/v2rayN/ServiceLib/Services/ProcessService.cs @@ -0,0 +1,194 @@ +using System.Diagnostics; +using System.Text; + +namespace ServiceLib.Services; + +public class ProcessService : IDisposable +{ + private readonly Process _process; + private readonly Func? _updateFunc; + private bool _isDisposed; + + public int Id => _process.Id; + public IntPtr Handle => _process.Handle; + public bool HasExited => _process.HasExited; + + public ProcessService( + string fileName, + string arguments, + string workingDirectory, + bool displayLog, + bool redirectInput, + Dictionary? environmentVars, + Func? updateFunc) + { + _updateFunc = updateFunc; + + _process = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = fileName, + Arguments = arguments, + WorkingDirectory = workingDirectory, + UseShellExecute = false, + RedirectStandardInput = redirectInput, + RedirectStandardOutput = displayLog, + RedirectStandardError = displayLog, + CreateNoWindow = true, + StandardOutputEncoding = displayLog ? Encoding.UTF8 : null, + StandardErrorEncoding = displayLog ? Encoding.UTF8 : null, + }, + EnableRaisingEvents = true + }; + + if (environmentVars != null) + { + foreach (var kv in environmentVars) + { + _process.StartInfo.Environment[kv.Key] = kv.Value; + } + } + + if (displayLog) + { + RegisterEventHandlers(); + } + } + + public async Task StartAsync(string pwd = null) + { + _process.Start(); + + if (_process.StartInfo.RedirectStandardOutput) + { + _process.BeginOutputReadLine(); + _process.BeginErrorReadLine(); + } + + if (_process.StartInfo.RedirectStandardInput) + { + await Task.Delay(10); + await _process.StandardInput.WriteLineAsync(pwd); + } + } + + public async Task StopAsync() + { + if (_process.HasExited) + { + return; + } + + try + { + if (_process.StartInfo.RedirectStandardOutput) + { + try + { + _process.CancelOutputRead(); + } + catch + { + throw; + } + try + { + _process.CancelErrorRead(); + } + catch + { + throw; + } + } + + try + { + if (Utils.IsNonWindows()) + { + _process.Kill(true); + } + } + catch + { + throw; + } + + try + { + _process.Kill(); + } + catch + { + throw; + } + + await Task.Delay(100); + } + catch (Exception ex) + { + await _updateFunc?.Invoke(true, ex.Message); + } + } + + private void RegisterEventHandlers() + { + void dataHandler(object sender, DataReceivedEventArgs e) + { + if (e.Data.IsNotEmpty()) + { + _ = _updateFunc?.Invoke(false, e.Data + Environment.NewLine); + } + } + + _process.OutputDataReceived += dataHandler; + _process.ErrorDataReceived += dataHandler; + + _process.Exited += (s, e) => + { + try + { + _process.OutputDataReceived -= dataHandler; + _process.ErrorDataReceived -= dataHandler; + } + catch + { + } + }; + } + + public void Dispose() + { + if (_isDisposed) + { + return; + } + + try + { + if (!_process.HasExited) + { + try + { + _process.CancelOutputRead(); + } + catch { } + try + { + _process.CancelErrorRead(); + } + catch { } + + _process.Kill(); + } + + _process.Dispose(); + } + catch (Exception ex) + { + _updateFunc?.Invoke(true, ex.Message); + } + + _isDisposed = true; + } +} diff --git a/v2rayN/ServiceLib/Services/SpeedtestService.cs b/v2rayN/ServiceLib/Services/SpeedtestService.cs index 5266f4d0..1fff0dbe 100644 --- a/v2rayN/ServiceLib/Services/SpeedtestService.cs +++ b/v2rayN/ServiceLib/Services/SpeedtestService.cs @@ -182,11 +182,11 @@ public class SpeedtestService(Config config, Func updateF private async Task RunRealPingAsync(List selecteds, string exitLoopKey) { - var pid = -1; + ProcessService processService = null; try { - pid = await CoreManager.Instance.LoadCoreConfigSpeedtest(selecteds); - if (pid < 0) + processService = await CoreManager.Instance.LoadCoreConfigSpeedtest(selecteds); + if (processService is null) { return false; } @@ -216,10 +216,7 @@ public class SpeedtestService(Config config, Func updateF } finally { - if (pid > 0) - { - await ProcUtils.ProcessKill(pid); - } + await processService?.StopAsync(); } return true; } @@ -244,11 +241,11 @@ public class SpeedtestService(Config config, Func updateF tasks.Add(Task.Run(async () => { - var pid = -1; + ProcessService processService = null; try { - pid = await CoreManager.Instance.LoadCoreConfigSpeedtest(it); - if (pid < 0) + processService = await CoreManager.Instance.LoadCoreConfigSpeedtest(it); + if (processService is null) { await UpdateFunc(it.IndexId, "", ResUI.FailedToRunCore); } @@ -275,10 +272,7 @@ public class SpeedtestService(Config config, Func updateF } finally { - if (pid > 0) - { - await ProcUtils.ProcessKill(pid); - } + await processService?.StopAsync(); concurrencySemaphore.Release(); } })); From 12cc09d0c9994c99f00fb5f709d7d87146ae3682 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 1 Oct 2025 20:17:26 +0800 Subject: [PATCH 030/132] Bug fix --- v2rayN/ServiceLib/Services/ProcessService.cs | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/v2rayN/ServiceLib/Services/ProcessService.cs b/v2rayN/ServiceLib/Services/ProcessService.cs index 6b964831..db3a95ac 100644 --- a/v2rayN/ServiceLib/Services/ProcessService.cs +++ b/v2rayN/ServiceLib/Services/ProcessService.cs @@ -88,18 +88,12 @@ public class ProcessService : IDisposable { _process.CancelOutputRead(); } - catch - { - throw; - } + catch { } try { _process.CancelErrorRead(); } - catch - { - throw; - } + catch { } } try @@ -109,19 +103,13 @@ public class ProcessService : IDisposable _process.Kill(true); } } - catch - { - throw; - } + catch { } try { _process.Kill(); } - catch - { - throw; - } + catch { } await Task.Delay(100); } From d7c5161431c5293fb500681374857c58d5642f40 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Thu, 2 Oct 2025 19:55:49 +0800 Subject: [PATCH 031/132] Optimize and improve --- v2rayN/ServiceLib/Common/ProcUtils.cs | 112 ------------------ .../Common/{Job.cs => WindowsJob.cs} | 6 +- v2rayN/ServiceLib/Manager/AppManager.cs | 2 +- v2rayN/ServiceLib/Services/ProcessService.cs | 1 + 4 files changed, 5 insertions(+), 116 deletions(-) rename v2rayN/ServiceLib/Common/{Job.cs => WindowsJob.cs} (98%) diff --git a/v2rayN/ServiceLib/Common/ProcUtils.cs b/v2rayN/ServiceLib/Common/ProcUtils.cs index 561278ce..2819f2fb 100644 --- a/v2rayN/ServiceLib/Common/ProcUtils.cs +++ b/v2rayN/ServiceLib/Common/ProcUtils.cs @@ -67,116 +67,4 @@ public static class ProcUtils Logging.SaveLog(_tag, ex); } } - - public static async Task ProcessKill(int pid) - { - try - { - await ProcessKill(Process.GetProcessById(pid), false); - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - } - - public static async Task ProcessKill(Process? proc, bool review) - { - if (proc is null) - { - return; - } - - GetProcessKeyInfo(proc, review, out var procId, out var fileName, out var processName); - - try - { - if (Utils.IsNonWindows()) - { - proc?.Kill(true); - } - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - - try - { - proc?.Kill(); - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - - try - { - proc?.Close(); - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - - try - { - proc?.Dispose(); - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - - await Task.Delay(300); - await ProcessKillByKeyInfo(review, procId, fileName, processName); - } - - private static void GetProcessKeyInfo(Process? proc, bool review, out int? procId, out string? fileName, out string? processName) - { - procId = null; - fileName = null; - processName = null; - if (!review) - { - return; - } - try - { - procId = proc?.Id; - fileName = proc?.MainModule?.FileName; - processName = proc?.ProcessName; - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - } - - private static async Task ProcessKillByKeyInfo(bool review, int? procId, string? fileName, string? processName) - { - if (review && procId != null && fileName != null) - { - try - { - var lstProc = Process.GetProcessesByName(processName); - foreach (var proc2 in lstProc) - { - if (proc2.Id == procId) - { - Logging.SaveLog($"{_tag}, KillProcess not completing the job, procId"); - await ProcessKill(proc2, false); - } - if (proc2.MainModule != null && proc2.MainModule?.FileName == fileName) - { - Logging.SaveLog($"{_tag}, KillProcess not completing the job, fileName"); - } - } - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - } - } } diff --git a/v2rayN/ServiceLib/Common/Job.cs b/v2rayN/ServiceLib/Common/WindowsJob.cs similarity index 98% rename from v2rayN/ServiceLib/Common/Job.cs rename to v2rayN/ServiceLib/Common/WindowsJob.cs index fe968d2d..f7fd2d74 100644 --- a/v2rayN/ServiceLib/Common/Job.cs +++ b/v2rayN/ServiceLib/Common/WindowsJob.cs @@ -7,11 +7,11 @@ namespace ServiceLib.Common; * http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net */ - public sealed class Job : IDisposable + public sealed class WindowsJob : IDisposable { private IntPtr handle = IntPtr.Zero; - public Job() + public WindowsJob() { handle = CreateJobObject(IntPtr.Zero, null); var extendedInfoPtr = IntPtr.Zero; @@ -94,7 +94,7 @@ namespace ServiceLib.Common; } } - ~Job() + ~WindowsJob() { Dispose(false); } diff --git a/v2rayN/ServiceLib/Manager/AppManager.cs b/v2rayN/ServiceLib/Manager/AppManager.cs index 2f5ccb64..16aa4253 100644 --- a/v2rayN/ServiceLib/Manager/AppManager.cs +++ b/v2rayN/ServiceLib/Manager/AppManager.cs @@ -8,7 +8,7 @@ public sealed class AppManager private Config _config; private int? _statePort; private int? _statePort2; - private Job? _processJob; + private WindowsJob? _processJob; public static AppManager Instance => _instance.Value; public Config Config => _config; diff --git a/v2rayN/ServiceLib/Services/ProcessService.cs b/v2rayN/ServiceLib/Services/ProcessService.cs index db3a95ac..06e27d52 100644 --- a/v2rayN/ServiceLib/Services/ProcessService.cs +++ b/v2rayN/ServiceLib/Services/ProcessService.cs @@ -178,5 +178,6 @@ public class ProcessService : IDisposable } _isDisposed = true; + GC.SuppressFinalize(this); } } From 22f0d04f011e34ce174e1d4e15a2fac0ef148efd Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 3 Oct 2025 14:13:03 +0800 Subject: [PATCH 032/132] Fix https://github.com/2dust/v2rayN/issues/8060 --- v2rayN/ServiceLib/Common/Utils.cs | 14 ++++++++++---- v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs | 2 +- v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs | 2 +- v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs | 2 +- v2rayN/ServiceLib/Manager/AppManager.cs | 16 ---------------- v2rayN/ServiceLib/Manager/CoreManager.cs | 17 ++++++++++++++++- 6 files changed, 29 insertions(+), 24 deletions(-) diff --git a/v2rayN/ServiceLib/Common/Utils.cs b/v2rayN/ServiceLib/Common/Utils.cs index 358d4e9a..85182a60 100644 --- a/v2rayN/ServiceLib/Common/Utils.cs +++ b/v2rayN/ServiceLib/Common/Utils.cs @@ -85,13 +85,19 @@ public class Utils /// Base64 Encode /// /// + /// /// - public static string Base64Encode(string plainText) + public static string Base64Encode(string plainText, bool removePadding = false) { try { var plainTextBytes = Encoding.UTF8.GetBytes(plainText); - return Convert.ToBase64String(plainTextBytes); + var base64 = Convert.ToBase64String(plainTextBytes); + if (removePadding) + { + base64 = base64.TrimEnd('='); + } + return base64; } catch (Exception ex) { @@ -112,7 +118,7 @@ public class Utils { if (plainText.IsNullOrEmpty()) { - return ""; + return string.Empty; } plainText = plainText.Trim() @@ -947,7 +953,7 @@ public class Utils if (SetUnixFileMode(fileName)) { Logging.SaveLog($"Successfully set the file execution permission, {fileName}"); - return ""; + return string.Empty; } if (fileName.Contains(' ')) diff --git a/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs b/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs index 814d753d..decda17f 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs @@ -27,7 +27,7 @@ public class FmtHandler catch (Exception ex) { Logging.SaveLog(_tag, ex); - return ""; + return string.Empty; } } diff --git a/v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs index 2c9898e9..2ec9769f 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/ShadowsocksFmt.cs @@ -42,7 +42,7 @@ public class ShadowsocksFmt : BaseFmt // item.port); //url = Utile.Base64Encode(url); //new Sip002 - var pw = Utils.Base64Encode($"{item.Security}:{item.Id}"); + var pw = Utils.Base64Encode($"{item.Security}:{item.Id}", true); return ToUri(EConfigType.Shadowsocks, item.Address, item.Port, pw, null, remark); } diff --git a/v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs b/v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs index 6110d784..dbecdade 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/SocksFmt.cs @@ -33,7 +33,7 @@ public class SocksFmt : BaseFmt remark = "#" + Utils.UrlEncode(item.Remarks); } //new - var pw = Utils.Base64Encode($"{item.Security}:{item.Id}"); + var pw = Utils.Base64Encode($"{item.Security}:{item.Id}", true); return ToUri(EConfigType.SOCKS, item.Address, item.Port, pw, null, remark); } diff --git a/v2rayN/ServiceLib/Manager/AppManager.cs b/v2rayN/ServiceLib/Manager/AppManager.cs index 16aa4253..fb2a3f39 100644 --- a/v2rayN/ServiceLib/Manager/AppManager.cs +++ b/v2rayN/ServiceLib/Manager/AppManager.cs @@ -8,7 +8,6 @@ public sealed class AppManager private Config _config; private int? _statePort; private int? _statePort2; - private WindowsJob? _processJob; public static AppManager Instance => _instance.Value; public Config Config => _config; @@ -136,21 +135,6 @@ public sealed class AppManager return localPort + (int)protocol; } - public void AddProcess(nint processHandle) - { - if (Utils.IsWindows()) - { - _processJob ??= new(); - try - { - _processJob?.AddProcess(processHandle); - } - catch - { - } - } - } - #endregion Config #region SqliteHelper diff --git a/v2rayN/ServiceLib/Manager/CoreManager.cs b/v2rayN/ServiceLib/Manager/CoreManager.cs index 04f34d75..07876db8 100644 --- a/v2rayN/ServiceLib/Manager/CoreManager.cs +++ b/v2rayN/ServiceLib/Manager/CoreManager.cs @@ -8,6 +8,7 @@ public class CoreManager private static readonly Lazy _instance = new(() => new()); public static CoreManager Instance => _instance.Value; private Config _config; + private WindowsJob? _processJob; private ProcessService? _processService; private ProcessService? _processPreService; private bool _linuxSudo = false; @@ -264,14 +265,28 @@ public class CoreManager await procService.StartAsync(); await Task.Delay(100); - AppManager.Instance.AddProcess(procService.Handle); + if (procService is null or { HasExited: true }) { throw new Exception(ResUI.FailedToRunCore); } + AddProcessJob(procService.Handle); return procService; } + private void AddProcessJob(nint processHandle) + { + if (Utils.IsWindows()) + { + _processJob ??= new(); + try + { + _processJob?.AddProcess(processHandle); + } + catch { } + } + } + #endregion Process } From 513662d89a4b3e4456f50aed07844bf2388c55c7 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Sat, 4 Oct 2025 15:18:37 +0800 Subject: [PATCH 033/132] Use editable ComboBox instead of AutoCompleteBox (#8067) * Update Avalonia * Use editable ComboBox instead of AutoCompleteBox --- v2rayN/Directory.Packages.props | 12 ++++---- v2rayN/v2rayN.Desktop/Common/AvaUtils.cs | 7 ++--- .../Views/DNSSettingWindow.axaml | 28 +++++++++---------- .../Views/DNSSettingWindow.axaml.cs | 14 +++++----- .../Views/OptionSettingWindow.axaml | 20 +++++++------ .../Views/RoutingRuleDetailsWindow.axaml | 16 +++++------ .../Views/RoutingRuleDetailsWindow.axaml.cs | 1 + 7 files changed, 50 insertions(+), 48 deletions(-) diff --git a/v2rayN/Directory.Packages.props b/v2rayN/Directory.Packages.props index 9dcfbe61..e9d2f2b3 100644 --- a/v2rayN/Directory.Packages.props +++ b/v2rayN/Directory.Packages.props @@ -6,10 +6,10 @@ - - - - + + + + @@ -19,9 +19,9 @@ - + - + diff --git a/v2rayN/v2rayN.Desktop/Common/AvaUtils.cs b/v2rayN/v2rayN.Desktop/Common/AvaUtils.cs index 87c974d4..f7f03ab3 100644 --- a/v2rayN/v2rayN.Desktop/Common/AvaUtils.cs +++ b/v2rayN/v2rayN.Desktop/Common/AvaUtils.cs @@ -1,6 +1,7 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Input; +using Avalonia.Input.Platform; using Avalonia.Media.Imaging; using Avalonia.Platform; @@ -18,7 +19,7 @@ internal class AvaUtils return null; } - return await clipboard.GetTextAsync(); + return await clipboard.TryGetTextAsync(); } catch { @@ -33,9 +34,7 @@ internal class AvaUtils var clipboard = TopLevel.GetTopLevel(visual)?.Clipboard; if (clipboard == null) return; - var dataObject = new DataObject(); - dataObject.Set(DataFormats.Text, strData); - await clipboard.SetDataObjectAsync(dataObject); + await clipboard.SetTextAsync(strData); } catch { diff --git a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml index cfc36aec..f8fc1ec0 100644 --- a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml @@ -56,13 +56,13 @@ Margin="{StaticResource Margin4}" VerticalAlignment="Center" Text="{x:Static resx:ResUI.TbDomesticDNS}" /> - + IsEditable="True" /> - + IsEditable="True" /> - + IsEditable="True" /> - + IsEditable="True" /> - + IsEditable="True" /> - + IsEditable="True" /> @@ -435,11 +435,11 @@ Margin="{StaticResource Margin4}" VerticalAlignment="Center" Text="{x:Static resx:ResUI.TbSettingsDomainDNSAddress}" /> - + IsEditable="True" /> diff --git a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs index ce63afa0..ec3ad883 100644 --- a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs @@ -40,15 +40,15 @@ public partial class DNSSettingWindow : WindowBase this.Bind(ViewModel, vm => vm.AddCommonHosts, v => v.togAddCommonHosts.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.FakeIP, v => v.togFakeIP.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.BlockBindingQuery, v => v.togBlockBindingQuery.IsChecked).DisposeWith(disposables); - //this.Bind(ViewModel, vm => vm.DirectDNS, v => v.cmbDirectDNS.Text).DisposeWith(disposables); - //this.Bind(ViewModel, vm => vm.RemoteDNS, v => v.cmbRemoteDNS.Text).DisposeWith(disposables); - //this.Bind(ViewModel, vm => vm.SingboxOutboundsResolveDNS, v => v.cmbSBResolverDNS.Text).DisposeWith(disposables); - //this.Bind(ViewModel, vm => vm.SingboxFinalResolveDNS, v => v.cmbSBFinalResolverDNS.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.DirectDNS, v => v.cmbDirectDNS.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.RemoteDNS, v => v.cmbRemoteDNS.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SingboxOutboundsResolveDNS, v => v.cmbSBResolverDNS.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SingboxFinalResolveDNS, v => v.cmbSBFinalResolverDNS.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.RayStrategy4Freedom, v => v.cmbRayFreedomDNSStrategy.SelectedItem).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SingboxStrategy4Direct, v => v.cmbSBDirectDNSStrategy.SelectedItem).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SingboxStrategy4Proxy, v => v.cmbSBRemoteDNSStrategy.SelectedItem).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.Hosts, v => v.txtHosts.Text).DisposeWith(disposables); - //this.Bind(ViewModel, vm => vm.DirectExpectedIPs, v => v.cmbDirectExpectedIPs.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.DirectExpectedIPs, v => v.cmbDirectExpectedIPs.Text).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); @@ -57,11 +57,11 @@ public partial class DNSSettingWindow : WindowBase this.Bind(ViewModel, vm => vm.UseSystemHostsCompatible, v => v.togUseSystemHostsCompatible.IsChecked).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.DomainStrategy4FreedomCompatible, v => v.cmbdomainStrategy4FreedomCompatible.SelectedItem).DisposeWith(disposables); - //this.Bind(ViewModel, vm => vm.DomainDNSAddressCompatible, v => v.cmbdomainDNSAddressCompatible.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.DomainDNSAddressCompatible, v => v.cmbdomainDNSAddressCompatible.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.NormalDNSCompatible, v => v.txtnormalDNSCompatible.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.DomainStrategy4Freedom2Compatible, v => v.cmbdomainStrategy4OutCompatible.SelectedItem).DisposeWith(disposables); - //this.Bind(ViewModel, vm => vm.DomainDNSAddress2Compatible, v => v.cmbdomainDNSAddress2Compatible.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.DomainDNSAddress2Compatible, v => v.cmbdomainDNSAddress2Compatible.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.NormalDNS2Compatible, v => v.txtnormalDNS2Compatible.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.TunDNS2Compatible, v => v.txttunDNS2Compatible.Text).DisposeWith(disposables); diff --git a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml index 3c6e7231..e8d870bf 100644 --- a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml @@ -502,12 +502,13 @@ Margin="{StaticResource Margin4}" VerticalAlignment="Center" Text="{x:Static resx:ResUI.TbSettingsCurrentFontFamily}" /> - + Margin="{StaticResource Margin4}" + IsEditable="True" /> - + Margin="{StaticResource Margin4}" + IsEditable="True" /> - + Margin="{StaticResource Margin4}" + IsEditable="True" /> - + Margin="{StaticResource Margin4}" + IsEditable="True" /> - + IsEditable="True" /> + VerticalAlignment="Center" + Orientation="Horizontal"> diff --git a/v2rayN/v2rayN.Desktop/Views/ClashProxiesView.axaml b/v2rayN/v2rayN.Desktop/Views/ClashProxiesView.axaml index 974d5192..a4ef8315 100644 --- a/v2rayN/v2rayN.Desktop/Views/ClashProxiesView.axaml +++ b/v2rayN/v2rayN.Desktop/Views/ClashProxiesView.axaml @@ -50,27 +50,21 @@ diff --git a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml index 2f7ad807..8c2cc90c 100644 --- a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml +++ b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml @@ -25,28 +25,22 @@ - + diff --git a/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml b/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml index a0f5af1b..7a6cf085 100644 --- a/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml +++ b/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml @@ -32,39 +32,30 @@ diff --git a/v2rayN/v2rayN.Desktop/Views/SubEditWindow.axaml b/v2rayN/v2rayN.Desktop/Views/SubEditWindow.axaml index 211cff40..79216921 100644 --- a/v2rayN/v2rayN.Desktop/Views/SubEditWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/SubEditWindow.axaml @@ -72,13 +72,15 @@ + + - + Classes="IconButton Success" + ToolTip.Tip="{x:Static resx:ResUI.menuFastRealPing}"> + + + + + - + + diff --git a/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml.cs index 09d0952c..754f6aa6 100644 --- a/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml.cs @@ -80,6 +80,7 @@ public partial class ProfilesView : ReactiveUserControl this.BindCommand(ViewModel, vm => vm.SpeedServerCmd, v => v.menuSpeedServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.SortServerResultCmd, v => v.menuSortServerResult).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.RemoveInvalidServerResultCmd, v => v.menuRemoveInvalidServerResult).DisposeWith(disposables); + this.BindCommand(ViewModel, vm => vm.FastRealPingCmd, v => v.btnFastRealPing).DisposeWith(disposables); //servers export this.BindCommand(ViewModel, vm => vm.Export2ClientConfigCmd, v => v.menuExport2ClientConfig).DisposeWith(disposables); diff --git a/v2rayN/v2rayN/Views/ProfilesView.xaml b/v2rayN/v2rayN/Views/ProfilesView.xaml index 495dfb1b..1a33966b 100644 --- a/v2rayN/v2rayN/Views/ProfilesView.xaml +++ b/v2rayN/v2rayN/Views/ProfilesView.xaml @@ -57,16 +57,6 @@ - + + + + vm.SpeedServerCmd, v => v.menuSpeedServer).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.SortServerResultCmd, v => v.menuSortServerResult).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.RemoveInvalidServerResultCmd, v => v.menuRemoveInvalidServerResult).DisposeWith(disposables); + this.BindCommand(ViewModel, vm => vm.FastRealPingCmd, v => v.btnFastRealPing).DisposeWith(disposables); //servers export this.BindCommand(ViewModel, vm => vm.Export2ClientConfigCmd, v => v.menuExport2ClientConfig).DisposeWith(disposables); From ecf42cb85d667d29f3588fe43109cdb60234b9a1 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 23 Oct 2025 09:09:26 +0800 Subject: [PATCH 080/132] Fix dns (#8174) --- v2rayN/ServiceLib/Models/V2rayConfig.cs | 1 + .../Services/CoreConfig/V2ray/V2rayDnsService.cs | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/v2rayN/ServiceLib/Models/V2rayConfig.cs b/v2rayN/ServiceLib/Models/V2rayConfig.cs index 0f4fd348..22dcf9f4 100644 --- a/v2rayN/ServiceLib/Models/V2rayConfig.cs +++ b/v2rayN/ServiceLib/Models/V2rayConfig.cs @@ -215,6 +215,7 @@ public class Dns4Ray public class DnsServer4Ray { public string? address { get; set; } + public int? port { get; set; } public List? domains { get; set; } public bool? skipFallback { get; set; } public List? expectedIPs { get; set; } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs index 45f0c28a..d3cff6cc 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs @@ -79,9 +79,18 @@ public partial class CoreConfigV2rayService static object CreateDnsServer(string dnsAddress, List domains, List? expectedIPs = null) { + var (domain, scheme, port, path) = Utils.ParseUrl(dnsAddress); + var domainFinal = dnsAddress; + int? portFinal = null; + if (scheme.IsNullOrEmpty() || scheme.Contains("udp", StringComparison.OrdinalIgnoreCase) || scheme.Contains("tcp", StringComparison.OrdinalIgnoreCase)) + { + domainFinal = domain; + portFinal = port > 0 ? port : null; + } var dnsServer = new DnsServer4Ray { - address = dnsAddress, + address = domainFinal, + port = portFinal, skipFallback = true, domains = domains.Count > 0 ? domains : null, expectedIPs = expectedIPs?.Count > 0 ? expectedIPs : null From bbedc4dbb11472e56f9fe766fbedcee4fc95f872 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 23 Oct 2025 09:10:21 +0800 Subject: [PATCH 081/132] Fix (#8175) --- .../Singbox/SingboxOutboundService.cs | 2 +- .../V2ray/V2rayConfigTemplateService.cs | 25 ++++++++++++++----- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs index 097df4c3..84111652 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs @@ -294,7 +294,7 @@ public partial class CoreConfigSingboxService var tls = new Tls4Sbox() { enabled = true, - record_fragment = _config.CoreBasicItem.EnableFragment, + record_fragment = _config.CoreBasicItem.EnableFragment ? true : null, server_name = server_name, insecure = Utils.ToBool(node.AllowInsecure.IsNullOrEmpty() ? _config.CoreBasicItem.DefAllowInsecure.ToString().ToLower() : node.AllowInsecure), alpn = node.GetAlpn(), diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs index f53308d1..ed023126 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs @@ -86,8 +86,8 @@ public partial class CoreConfigV2rayService } } - // Handle outbounds - append instead of override - var customOutboundsNode = fullConfigTemplateNode["outbounds"] is JsonArray outbounds ? outbounds : new JsonArray(); + var customOutboundsNode = new JsonArray(); + foreach (var outbound in v2rayConfig.outbounds) { if (outbound.protocol.ToLower() is "blackhole" or "dns" or "freedom") @@ -97,14 +97,27 @@ public partial class CoreConfigV2rayService continue; } } - else if ((outbound.streamSettings?.sockopt?.dialerProxy.IsNullOrEmpty() == true) && (!fullConfigTemplate.ProxyDetour.IsNullOrEmpty()) && !(Utils.IsPrivateNetwork(outbound.settings?.servers?.FirstOrDefault()?.address ?? string.Empty) || Utils.IsPrivateNetwork(outbound.settings?.vnext?.FirstOrDefault()?.address ?? string.Empty))) + else if (!fullConfigTemplate.ProxyDetour.IsNullOrEmpty()) { - outbound.streamSettings ??= new StreamSettings4Ray(); - outbound.streamSettings.sockopt ??= new Sockopt4Ray(); - outbound.streamSettings.sockopt.dialerProxy = fullConfigTemplate.ProxyDetour; + var outboundAddress = outbound.settings?.servers?.FirstOrDefault()?.address ?? outbound.settings?.vnext?.FirstOrDefault()?.address ?? string.Empty; + if (!Utils.IsPrivateNetwork(outboundAddress)) + { + outbound.streamSettings ??= new StreamSettings4Ray(); + outbound.streamSettings.sockopt ??= new Sockopt4Ray(); + outbound.streamSettings.sockopt.dialerProxy = fullConfigTemplate.ProxyDetour; + } } customOutboundsNode.Add(JsonUtils.DeepCopy(outbound)); } + + if (fullConfigTemplateNode["outbounds"] is JsonArray templateOutbounds) + { + foreach (var outbound in templateOutbounds) + { + customOutboundsNode.Add(outbound?.DeepClone()); + } + } + fullConfigTemplateNode["outbounds"] = customOutboundsNode; return await Task.FromResult(JsonUtils.Serialize(fullConfigTemplateNode)); From f28fa31c145c56f1024881797343b439533ad013 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 23 Oct 2025 17:57:47 +0800 Subject: [PATCH 082/132] Fix tcp dns (#8179) --- .../Services/CoreConfig/V2ray/V2rayDnsService.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs index d3cff6cc..7170c5f0 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs @@ -82,11 +82,16 @@ public partial class CoreConfigV2rayService var (domain, scheme, port, path) = Utils.ParseUrl(dnsAddress); var domainFinal = dnsAddress; int? portFinal = null; - if (scheme.IsNullOrEmpty() || scheme.Contains("udp", StringComparison.OrdinalIgnoreCase) || scheme.Contains("tcp", StringComparison.OrdinalIgnoreCase)) + if (scheme.IsNullOrEmpty() || scheme.StartsWith("udp", StringComparison.OrdinalIgnoreCase)) { domainFinal = domain; portFinal = port > 0 ? port : null; } + else if (scheme.StartsWith("tcp", StringComparison.OrdinalIgnoreCase)) + { + domainFinal = scheme + "://" + domain; + portFinal = port > 0 ? port : null; + } var dnsServer = new DnsServer4Ray { address = domainFinal, From 7c76308c936fd4ca7ba9d1a7afbffe20eef633f8 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Thu, 23 Oct 2025 17:58:02 +0800 Subject: [PATCH 083/132] Fix (#8180) --- .../CoreConfig/V2ray/V2rayConfigTemplateService.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs index ed023126..986e1966 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs @@ -97,9 +97,12 @@ public partial class CoreConfigV2rayService continue; } } - else if (!fullConfigTemplate.ProxyDetour.IsNullOrEmpty()) + else if ((!fullConfigTemplate.ProxyDetour.IsNullOrEmpty()) + && ((outbound.streamSettings?.sockopt?.dialerProxy.IsNullOrEmpty() ?? true) == true)) { - var outboundAddress = outbound.settings?.servers?.FirstOrDefault()?.address ?? outbound.settings?.vnext?.FirstOrDefault()?.address ?? string.Empty; + var outboundAddress = outbound.settings?.servers?.FirstOrDefault()?.address + ?? outbound.settings?.vnext?.FirstOrDefault()?.address + ?? string.Empty; if (!Utils.IsPrivateNetwork(outboundAddress)) { outbound.streamSettings ??= new StreamSettings4Ray(); From b4c20e7b812ef5c5ff34f29def8ab4c51b655649 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Thu, 23 Oct 2025 19:03:16 +0800 Subject: [PATCH 084/132] Bug fix https://github.com/2dust/v2rayN/discussions/8168 --- v2rayN/ServiceLib/Handler/ConfigHandler.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index 2840fab7..8b5acff2 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -1221,6 +1221,7 @@ public static class ConfigHandler CoreType = coreType, ConfigType = EConfigType.PolicyGroup, Remarks = remark, + IsSub = false }; if (!subId.IsNullOrEmpty()) { From 83d4a9c18e6dc5aa8de9ef11285548643882c40c Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 24 Oct 2025 14:22:05 +0800 Subject: [PATCH 085/132] Update Directory.Packages.props --- v2rayN/Directory.Packages.props | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/v2rayN/Directory.Packages.props b/v2rayN/Directory.Packages.props index a3dde2c1..a339af4c 100644 --- a/v2rayN/Directory.Packages.props +++ b/v2rayN/Directory.Packages.props @@ -6,13 +6,13 @@ - - - - + + + + - + From 1a0f50a41e0f6ef15af64885edc67b841627ca5e Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 24 Oct 2025 14:22:35 +0800 Subject: [PATCH 086/132] up 7.15.6 --- v2rayN/Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2rayN/Directory.Build.props b/v2rayN/Directory.Build.props index 4c25065d..bea02572 100644 --- a/v2rayN/Directory.Build.props +++ b/v2rayN/Directory.Build.props @@ -1,7 +1,7 @@ - 7.15.5 + 7.15.6 From 51409a3e28a9004f393fac8d45cade5339c2f975 Mon Sep 17 00:00:00 2001 From: JieXu Date: Fri, 24 Oct 2025 19:38:40 +0800 Subject: [PATCH 087/132] =?UTF-8?q?Add=20French=20support=20|=20Ajouter=20?= =?UTF-8?q?le=20support=20du=20fran=C3=A7ais=20|=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=B3=95=E8=AF=AD=E6=94=AF=E6=8C=81=20(#8186)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add files via upload * Add files via upload * Update Global.cs * Add French resource file to project * Update ResUI.fr.resx * Update ResUI.fr.resx * Update ResUI.fr.resx * Delete v2rayN/AmazTool/Resx/Resource.fr.resx --- v2rayN/ServiceLib/Global.cs | 1 + v2rayN/ServiceLib/Resx/ResUI.fr.resx | 1599 ++++++++++++++++++++++++++ v2rayN/ServiceLib/ServiceLib.csproj | 5 +- 3 files changed, 1604 insertions(+), 1 deletion(-) create mode 100644 v2rayN/ServiceLib/Resx/ResUI.fr.resx diff --git a/v2rayN/ServiceLib/Global.cs b/v2rayN/ServiceLib/Global.cs index 2327de48..492aa124 100644 --- a/v2rayN/ServiceLib/Global.cs +++ b/v2rayN/ServiceLib/Global.cs @@ -427,6 +427,7 @@ public class Global "zh-Hant", "en", "fa-Ir", + "fr", "ru", "hu" ]; diff --git a/v2rayN/ServiceLib/Resx/ResUI.fr.resx b/v2rayN/ServiceLib/Resx/ResUI.fr.resx new file mode 100644 index 00000000..2222d8c4 --- /dev/null +++ b/v2rayN/ServiceLib/Resx/ResUI.fr.resx @@ -0,0 +1,1599 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Lien de partage exporté vers le presse-papiers avec succès + + + Veuillez vérifier d’abord la config + + + Format de configuration incorrect + + + Attention : la configuration personnalisée dépend entièrement de vos paramètres et peut ne pas activer toutes les fonctions. Pour utiliser le proxy système, changez le port d’écoute manuellement. + + + Téléchargement commencé... + + + Échec de la conversion de la configuration + + + Échec de la génération du fichier de configuration par défaut + + + Échec de l’obtention de la configuration par défaut + + + Échec de l’importation de la configuration personnalisée + + + Échec de la lecture de la configuration + + + Veuillez saisir un port au format correct + + + Veuillez saisir le port d’écoute local + + + Veuillez saisir le mot de passe + + + Veuillez saisir l’adresse + + + Veuillez saisir l’ID utilisateur + + + Configuration incorrecte, veuillez vérifier + + + Initialiser la configuration + + + {0} {1} est déjà à jour. + + + {0} {1} est déjà à jour. + + + Adresse + + + Méthode de chiffrement + + + Port + + + Type + + + Grp abonn. + + + Téléchargement du jour + + + Téléversement du jour + + + Total téléchargé + + + Total téléversé + + + Protoc. Transp. + + + Téléchargement du Core réussi + + + Échec de l’importation du contenu d’abonnement + + + Contenu d’abonnement récupéré avec succès + + + Aucun abonnement valide défini + + + Analyse de {0} réussie + + + Début de la récupération du contenu d’abonnement + + + Début de la mise à jour de {0}... + + + Contenu d’abonnement invalide + + + Décompression en cours...... + + + Fin de la mise à jour des abonnements + + + Début de la mise à jour des abonnements + + + Mise à jour du Core réussie + + + Mise à jour du Core réussie ! Redémarrage du service... + + + Protocole non VMess ni SS + + + Fichier Core introuvable dans le dossier ({0}) (nom de fichier : {1}). Veuillez le télécharger puis le placer dans le dossier. Adresse de téléchargement : {2} + + + Analyse terminée, aucun QR code valide trouvé + + + Échec de l’opération, veuillez vérifier et réessayer + + + Veuillez saisir un alias + + + Veuillez sélectionner une méthode de chiffrement + + + Veuillez sélectionner un protocole + + + Sélectionnez d’abord une configuration + + + Déduplication des configurations terminée. Quantité initiale : {0}, quantité actuelle : {1}. + + + Confirmer la suppression ? + + + Le fichier de configuration client est enregistré dans : {0} + + + Démarrage du service ({0})... + + + Configuration réussie. {0} + + + Configuration personnalisée importée avec succès + + + {0} configurations importées depuis le presse-papiers + + + Lien de partage importé par analyse avec succès + + + Latence actuelle : {0} ms, {1} + + + Opération réussie + + + Veuillez d’abord sélectionner une règle + + + Confirmer la suppression de la règle ? + + + {0}, au moins un champ est obligatoire. + + + Alias + + + Adresse optionnelle (URL) + + + Quantité + + + Veuillez saisir l’URL + + + Ajouter les règles ? Choisir "Oui" pour ajouter, "Non" pour tout remplacer. + + + Téléchargement du fichier Geo : {0} réussi + + + Information + + + Icône personnalisée + + + Veuillez saisir un DNS personnalisé valide + + + *chemin ws/httpupgrade/xhttp + + + *chemin h2 + + + *clé de chiffrement QUIC + + + *nom de service gRPC + + + *hôte http, séparés par des virgules (,) + + + *hôte ws/httpupgrade/xhttp + + + *hôte h2, séparés par des virgules (,) + + + *méthode de chiffrement QUIC + + + *type de camouflage tcp + + + *type de camouflage kcp + + + *type de camouflage QUIC + + + *mode gRPC + + + TLS + + + *graine Kcp + + + Échec de l’enregistrement du raccourci global {0}, raison : {1} + + + Raccourci global {0} enregistré avec succès + + + Tous + + + Parcourir pour importer la configuration + + + Test en cours... + + + Réseau local + + + Local + + + Filtre : Entrée pour exécuter + + + Rechercher mises à jour + + + Fermer + + + Quitter + + + Paramètres des raccourcis globaux + + + Aide + + + Paramètres config + + + Promotion + + + Redémarrer + + + Paramètres de routage + + + Fichiers de config + + + Paramètres + + + Mettre à jour l’abonnement actuel (sans proxy) + + + Mettre à jour l’abonnement actuel (via le proxy) + + + Groupe abonnements + + + Paramètres des groupes d’abonnement + + + Mettre à jour tous les abonnements (sans proxy) + + + Mettre à jour tous les abonnements (via le proxy) + + + Proxy système + + + Effacer le proxy système + + + Ne pas modifier le proxy système + + + Mode PAC + + + Configuration auto proxy système + + + Couleur + + + Langue (redémarrage requis) + + + Importer des liens depuis le presse-papiers (Ctrl+V) + + + Scanner le QR code à l’écran (Ctrl+S) + + + Cloner la sélection + + + Supprimer les doublons + + + Supprimer la sélection (multi-sélection) (Delete) + + + Définir comme actif (Entrée) + + + Effacer toutes les statistiques de service + + + Tester la latence de connexion réelle (multi-sélect) (Ctrl+R) + + + Trier selon les résultats de test + + + Tester la vitesse (multi-sélection) (Ctrl+T) + + + Tester la latence Tcping (multi-sélection) (Ctrl+O) + + + Exporter la configuration complète sélectionnée + + + Exporter les liens de partage vers le presse-papiers (multi-sélection) (Ctrl+C) + + + Ajouter une configuration personnalisée + + + Ajouter [Shadowsocks] + + + Ajouter [SOCKS] + + + Ajouter [Trojan] + + + Ajouter [VLESS] + + + Ajouter [VMess] + + + Tout sélectionner (Ctrl+A) + + + Tout effacer + + + Copier (Ctrl+C) + + + Tout copier + + + Tout sélect (Ctrl+A) + + + Ajouter + + + Supprimer + + + Éditer + + + Partager + + + Activer MAJ + + + Tri + + + User-Agent (optionnel) + + + Annuler + + + OK + + + Méthode de transport sous-jacente (transport) + + + Adresse (address) + + + Ignorer la vérification du certificat (allowInsecure) + + + ALPN + + + ID supplémentaire (alterId) + + + Empreinte + + + Type de camouflage (type) + + + ID utilisateur (id) + + + Protocole (network) + + + Chemin (path) + + + Port (port) + + + Alias (remarks) + + + Domaine de camouflage (host) + + + Méthode de chiffrement (security) + + + SNI + + + Sécurité couche transport (TLS) + + + *tcp par défaut ; un mauvais choix empêchera la connexion + + + Type de Core + + + Contrôle de flux (flow) + + + Générer + + + Mot de passe (password) + + + Mot de passe (optionnel) + + + ID utilisateur (id) + + + Mode chiffrement (encryption) + + + Nom d’utilisateur (optionnel) + + + Mode chiffrement (encryption) + + + Port Socks + + + *Valeur du port Socks pour la configuration personnalisée (facultatif). Si défini, Xray/sing-box (Tun) démarrera un service Socks en amont supplémentaire pour fournir le routage sélectif et l’affichage de la vitesse. + + + Parcourir + + + Éditer + + + Paramètres avancés du proxy, choix du protocole (optionnel) + + + Autoriser les connexions depuis le LAN + + + Masquer la fenêtre au démarrage + + + Intervalle de mise à jour automatique des fichiers Geo (heures) + + + Core : paramètres de base + + + DNS personnalisé v2ray + + + Core : paramètres KCP + + + Paramètres du type de Core + + + Ignorer la vérif. du certificat (Dangereux) + + + domainStrategy de Freedom (sortant) + + + Auto-ajuster la largeur des colonnes après maj. des abonnements + + + Vérifier les mises à jour pré-version (à activer avec prudence) + + + Exceptions + + + Exceptions : pour les adresses commençant par les caractères ci-dessous, ne pas utiliser le proxy. Séparez par des points-virgules (;). + + + Afficher la vitesse en temps réel (redémarrage requis) + + + À la déduplication, garder l’élément au plus petit numéro + + + Activer les journaux + + + Niveau de journalisation + + + Activer Mux multiplex + + + Paramètres v2rayN + + + Mot de passe d’authentification + + + DNS personnalisés (plusieurs possibles, séparés par des virgules) + + + Lever la restriction de proxy en boucle locale pour les applications Win10 UWP + + + Activer le sniffing de trafic + + + Port d’écoute mixte local + + + Lancer au démarrage (peut échouer) + + + Activer les statistiques de trafic (redémarrage requis) + + + URL de conversion d’abonnement (optionnel) + + + Paramètres du proxy système + + + Limite du nombre de configurations affichées dans le menu du plateau + + + Activer l’UDP + + + Nom d’utilisateur d’authentification + + + Effacer le proxy système + + + Afficher l’interface principale + + + Paramètres des raccourcis globaux + + + Appuyez directement sur les touches pour définir ; prendra effet après redémarrage + + + Ne pas modifier le proxy système + + + Réinitialiser + + + Configurer automatiquement le proxy système + + + Mode PAC + + + Partager (Ctrl+F) + + + Routage + + + Exécuter sans droits administrateur + + + Exécuter en tant qu’administrateur + + + Déplacer tout en bas (B) + + + Descendre (D) + + + Déplacer tout en haut (T) + + + Monter (U) + + + Filtre (regex pris en charge) + + + Site officiel de {0} + + + Ajouter un jeu de règles + + + Importer 1-clic du jeu de règles + + + Suppr. règles sélectionnées (Delete) + + + Définir comme règles actives (Entrée) + + + Stratégie résolution domaine + + + Liste des jeux de règles prédef. + + + *Règles de routage définies, séparées par des virgules (,); remplacez la virgule dans les expressions régulières par <COMMA> + + + Importer règles du presse-papiers + + + Importer règles depuis fichier + + + Importer depuis URL d’abonnement + + + Paramètres du jeu de règles + + + Ajouter une règle + + + Exporter les règles sélectionnées vers le presse-papiers + + + Liste des règles + + + Supprimer les règles sélectionnées (Delete) + + + Paramètres détaillés des règles de routage + + + Tri automatique par domaine, IP et nom de processus lors de l’enregistrement + + + Documentation détaillée des règles + + + Saisie de DnsObject prise en charge (format JSON), cliquer pour voir la documentation + + + Laissez vide pour les groupes ordinaires + + + Paramètres de routage modifiés + + + Paramètres du proxy système modifiés + + + Routage uniquement (routeOnly) + + + Ne pas utiliser le proxy pour les adresses locales (Intranet) + + + Tester latence et vitesse multithread en un clic (Ctrl+E) + + + Latence (ms) + + + Vitesse (Mo/s) + + + Échec d’exécution du Core, veuillez consulter les messages + + + Filtrage par alias (regex) + + + Afficher les journaux + + + Activer Tun + + + Ouvrir un nouveau port pour le LAN + + + Paramètres du mode Tun + + + Déplacer vers un groupe d’abonnement + + + Activer le tri par glisser-déposer des configurations (redémarrage requis) + + + Actualisation automatique + + + Ignorer le test + + + Éditer (Ctrl+D) + + + Double-cliquer sur l’interface principale pour activer + + + Test terminé + + + Empreinte TLS par défaut (fingerprint) + + + Agent utilisateur (User-Agent) + + + Valable uniquement pour les protocoles tcp/http et ws + + + Police actuelle (redémarrage requis) + + + Copiez les fichiers de police TTF/TTC dans le dossier guiFonts, effet après redémarrage + + + Port PAC = +3 ; port API Xray = +4 ; port API mihomo = +5 ; + + + Définir cette option avec des privilèges administrateur pour obtenir les droits admin au démarrage + + + Taille de police + + + Valeur délai d'expiration test vitesse unique + + + URL du fichier de test de vitesse + + + Déplacer vers haut/bas + + + PublicKey + + + ShortId + + + SpiderX + + + Activer l’accélération matérielle (redémarrage requis) + + + En attente du test (appuyer sur Échap pour arrêter)... + + + Désactivez ceci en cas de coupures anormales + + + Mise à jour désactivée, abonnement ignoré + + + Redémarrer en tant qu’administrateur + + + Adresses suppl. (URL), séparées par des virgules ; la conversion d’abonnement sera désactivée + + + Intervalle maj. auto (min) + + + Activer l’enregistrement des journaux dans un fichier + + + Type cible conv. d’abonnement + + + Laisser vide si aucune conversion n’est nécessaire + + + Paramètres DNS + + + DNS personnalisé sing-box + + + Veuillez renseigner la structure JSON DNS ; cliquer pour voir la documentation + + + Cliquez pour importer la configuration DNS par défaut + + + Stratégie résolution domaine (sing-box) + + + Protocole de multiplexage Mux (sing-box) + + + Nom complet du processus (mode Tun) + + + IP ou IP CIDR + + + Domaine + + + Ajouter [Hysteria2] + + + Bande passante maximale Hysteria (Up/Down) + + + Utiliser les hosts du système + + + Ajouter [TUIC] + + + Algo contrôle congestion + + + Alias de configuration du proxy amont + + + Alias de configuration du proxy aval + + + Assurez-vous que l’alias config existe et est unique + + + Routage automatique + + + Routage strict + + + Pile de protocoles + + + MTU + + + Activer un port d’écoute supplémentaire + + + Activer IPv6 + + + Ajouter [WireGuard] + + + PrivateKey + + + Reserved (2,3,4) + + + Address (IPv4,IPv6) + + + Mot de passe d’obfuscation (obfs password) + + + (Domaine ou IP ou nom de processus) avec Port et Protocole et InboundTag => OutboundTag + + + Défilement automatique vers la fin + + + Adresse de test de connexion réelle + + + Ne vérifier l’existence de l’alias qu’à la maj. des abonnements + + + Arrêt du test en cours... + + + *Autorité gRPC + + + Ajouter [HTTP] + + + En conflit avec le proxy amont de groupe + + + Activer le fragmentation (Fragment) + + + Activer le fichier de cache de sing-box (fichiers de reˋgles) + + + Set de règles sing-box perso + + + Opération réussie. Veuillez redémarrer l’app via le menu Paramètres. + + + Ouvrir l’emplacement du fichier + + + Tri + + + Chaîne de routage + + + Par défaut + + + Latence + + + Vitesse de téléchargement + + + Trafic téléchargé + + + Hôte + + + Nom + + + Réseau + + + Heure + + + Type + + + Vitesse d’envoi + + + Trafic envoyé + + + Connexions en cours + + + Fermer la connexion + + + Fermer toutes les connexions + + + Proxies actuels + + + Mode règle + + + Direct + + + Global + + + Suivre la configuration d’origine + + + Règle + + + Test de latence + + + Test de latence partiel actuel + + + Actualiser + + + Définir comme actif (Entrée) + + + Stratégie de résolution par défaut des sortants + + + Orientation de la mise en page principale (redémarrage requis) + + + Adresse de résolution de domaine pour sortants + + + Ajuster auto. la largeur des colonnes + + + Exporter les liens de partage en Base64 vers le presse-papiers (multi-sélection) + + + Exporter la configuration complète sélectionnée vers le presse-papiers + + + Afficher ou masquer l’interface principale + + + Port Socks config personnalisée + + + Sauvegarder et restaurer + + + Sauvegarder localement + + + Restaurer localement + + + Sauvegarder à distance (WebDAV) + + + Restaurer à distance (WebDAV) + + + Local + + + Distant (WebDAV) + + + Adresse du serveur WebDAV + + + Compte WebDAV + + + Mot de passe WebDAV + + + Vérification de disponibilité WebDAV + + + Nom du dossier distant (optionnel) + + + Fichier de sauvegarde invalide + + + Filtre d’hôte + + + Actif + + + Source des fichiers Geo (optionnel) + + + Source des fichiers de jeux de règles sing-box (optionnel) + + + L’application d’outil de mise à niveau est introuvable + + + Source des jeux de règles de routage (optionnel) + + + Réglages régionaux prédéfinis + + + Région par défaut + + + Russie + + + Iran + + + Les utilisateurs de Chine peuvent ignorer + + + Scanner le QR code dans l’image + + + Adresse (URL) invalide + + + N’utilisez pas d’adresse d’abonnement HTTP non sécurisée + + + Installer la police sur le système, choisir ou saisir son nom, effet après redémarrage + + + Voulez-vous vraiment quitter ? + + + Mémo + + + Mot de passe sudo système + + + Le mot de passe sera vérifié en ligne de commande. En cas d’échec ou de dysfonctionnement, redémarrez l’application. Il n’est pas stocké et doit être saisi à chaque redémarrage. + + + *Mode XHTTP + + + JSON brut XHTTP Extra, format : { XHTTPObject } + + + Masquer dans la barre d’état lors de la fermeture de la fenêtre + + + Niveau de concurrence lors des tests multithread + + + Exceptions : ne pas utiliser le proxy pour ces adresses, séparées par des virgules (,). + + + Type de détection de trafic + + + Activer un second port d’écoute local + + + Socks : port local ; Socks2 : deuxième port local ; Socks3 : port LAN + + + Thème + + + Copier la commande proxy terminal vers le presse-papiers + + + Recommencer le test des éléments échoués, {0} restants. Appuyez sur Échap pour arrêter... + + + Selon le résultat des tests + + + Supprimer les éléments invalides selon résultats tests + + + {0} résultats de test invalides supprimés. + + + Plage de ports sautés + + + Écrase le port ; pour plusieurs groupes, séparez par des virgules (,) + + + Générer un groupe de stratégie depuis plusieurs profils + + + Xray aléatoire (multi-sélection) + + + Xray équilibrage (tourniquet) multi-sélection + + + Xray latence minimale (multi-sélection) + + + Xray le plus stable (multi-sélection) + + + sing-box latence minimale (multi-sélection) + + + Exporter + + + Adresse de test d’informations de connexion actuelles + + + Vous pouvez saisir un alias de configuration ; assurez-vous qu’il existe et qu’il est unique + + + Mot de passe incorrect, veuillez réessayer. + + + Mldsa65Verify + + + Ajouter [Anytls] + + + DNS distant + + + DNS direct + + + Via le proxy ; assurez-vous que le serveur distant est disponible + + + Stratégie de résolution xray freedom + + + Stratégie de résolution directe sing-box + + + Stratégie de résolution distante sing-box + + + Ajouter des hôtes DNS courants + + + FakeIP + + + Bloquer les requêtes SVCB et HTTPS + + + Hôtes DNS : (« domaine1 ip1 ip2 » une ligne par entrée) + + + Paramètres DNS de base + + + Paramètres DNS avancés + + + Valider les IP des domaines de la région concernée + + + Après configuration, les IP renvoyées pour les domaines régionaux (p. ex. geosite:cn) seront validées ; seules les IP attendues seront retournées + + + Activer le DNS personnalisé + + + DNS personnalisé activé ; la configuration de cette page sera ignorée + + + Une fois activé, bloque les requêtes ECH et de disponibilité HTTP/3 + + + Veuillez saisir un modèle de configuration valide + + + Paramètres du modèle de config complet + + + Activer le modèle de config. complet + + + Modèle de configuration complet v2ray + + + Ajoute seulement la configuration sortante, routing.balancers et routing.rules.outboundTag. Voir la documentation. + + + N’ajoutez pas de sorties pour protocoles non-proxy. + + + Définir le tag de proxy amont + + + Modèle de configuration complet sing-box + + + Ajoute uniquement la configuration des sortants et des endpoints ; cliquer pour voir la documentation + + + Cette fonction s’adresse aux utilisateurs avancés et aux besoins spécifiques. Une fois activée, les paramètres de base du Core, du DNS et du routage sont ignorés. Vous devez vous assurer que la configuration des ports du proxy système, des statistiques de trafic, etc., est correcte — tout est à votre charge. + + + Début de l’analyse et du traitement du contenu d’abonnement + + + Sélectionner une configuration + + + Actif globalement par défaut, avec filtrage FakeIP intégré ; ne fonctionne que dans sing-box + + + Veuillez ajouter au moins une configuration + + + Groupe de stratégie + + + Chaîne de proxy + + + Latence minimale + + + Aléatoire + + + Round Robin + + + Le plus stable + + + Type de groupe de stratégie + + + Ajouter un groupe de stratégie + + + Ajouter une chaîne de proxy + + + Ajouter un enfant + + + Supprimer l’enfant + + + Liste des enfants + + + Basculement (failover) + + + sing-box basculement (multi-sélection) + + + Xray basculement (multi-sélection) + + + Le cœur « {0} » ne prend pas en charge le type de réseau « {1} ». + + + Le cœur « {0} » ne prend pas en charge le protocole « {1} » avec le mode de transport « {2} ». + + + Le cœur « {0} » ne prend pas en charge le protocole « {1} ». + + + Chaîne de proxy : + + + Règle de routage sortante : + + + Groupe de stratégie : + + + L’alias « {0} » n’existe pas. + + + Le groupe « {0} » est vide. Veuillez ajouter au moins une configuration. + + + La propriété {0} est invalide, veuillez vérifier + + + Le groupe {0} ne peut pas se référencer lui-même ni créer de référence circulaire + + + Protocole « {0} » non pris en charge. + + + Si le système n’a pas de zone de notif., n’activez pas cette option + + + Type de règle + + + Des règles peuvent être définies séparément pour Routing et DNS ; ALL les applique aux deux + + + DNS d’amorçage + + + Résoudre le nom du serveur DNS ; doit être spécifié en IP + + + Test 1-clic de latence réelle + + diff --git a/v2rayN/ServiceLib/ServiceLib.csproj b/v2rayN/ServiceLib/ServiceLib.csproj index 457b065d..8cfbf8ca 100644 --- a/v2rayN/ServiceLib/ServiceLib.csproj +++ b/v2rayN/ServiceLib/ServiceLib.csproj @@ -57,6 +57,9 @@ Designer PublicResXFileCodeGenerator + + PublicResXFileCodeGenerator + PublicResXFileCodeGenerator @@ -79,4 +82,4 @@ - \ No newline at end of file + From d3b1810eab136ea129cb761331b519753ce8ccc5 Mon Sep 17 00:00:00 2001 From: DHR60 Date: Sat, 25 Oct 2025 10:27:31 +0800 Subject: [PATCH 088/132] Update Directory.Packages.props (#8191) --- v2rayN/Directory.Packages.props | 6 +++--- v2rayN/v2rayN.Desktop/GlobalUsings.cs | 4 ++-- v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj | 2 +- v2rayN/v2rayN/GlobalUsings.cs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/v2rayN/Directory.Packages.props b/v2rayN/Directory.Packages.props index a339af4c..b9caf59f 100644 --- a/v2rayN/Directory.Packages.props +++ b/v2rayN/Directory.Packages.props @@ -9,16 +9,16 @@ - + - + - + diff --git a/v2rayN/v2rayN.Desktop/GlobalUsings.cs b/v2rayN/v2rayN.Desktop/GlobalUsings.cs index c9a2bc45..6a2f1d3b 100644 --- a/v2rayN/v2rayN.Desktop/GlobalUsings.cs +++ b/v2rayN/v2rayN.Desktop/GlobalUsings.cs @@ -3,7 +3,7 @@ global using System.Collections.Generic; global using System.Globalization; global using System.IO; global using System.Linq; -global using System.Reactive.Disposables; +global using System.Reactive.Disposables.Fluent; global using System.Reactive.Linq; global using System.Text; global using System.Threading; @@ -17,7 +17,7 @@ global using Avalonia.Markup.Xaml; global using Avalonia.Media; global using Avalonia.Media.Imaging; global using Avalonia.Platform; -global using Avalonia.ReactiveUI; +global using ReactiveUI.Avalonia; global using Avalonia.Styling; global using Avalonia.Threading; global using ReactiveUI; diff --git a/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj b/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj index f6ff5735..6212ce12 100644 --- a/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj +++ b/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj @@ -15,7 +15,7 @@ - + diff --git a/v2rayN/v2rayN/GlobalUsings.cs b/v2rayN/v2rayN/GlobalUsings.cs index 21d8dbab..a8fa461d 100644 --- a/v2rayN/v2rayN/GlobalUsings.cs +++ b/v2rayN/v2rayN/GlobalUsings.cs @@ -6,7 +6,7 @@ global using System.Diagnostics; global using System.Globalization; global using System.IO; global using System.Linq; -global using System.Reactive.Disposables; +global using System.Reactive.Disposables.Fluent; global using System.Reactive.Linq; global using System.Runtime.InteropServices; global using System.Text; From c8d89e3dce85e772e16d31fe797d5538f9e59b40 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sat, 25 Oct 2025 20:10:42 +0800 Subject: [PATCH 089/132] Adjusted the items in the configuration right-click menu --- v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx | 2 +- .../v2rayN.Desktop/Views/ProfilesView.axaml | 16 ++++++---- v2rayN/v2rayN/Views/ProfilesView.xaml | 29 ++++++++++--------- 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx index 095d8594..96c470c6 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -427,7 +427,7 @@ 路由设置 - 配置文件 + 配置项 设置 diff --git a/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml b/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml index 14a2d174..6dc18155 100644 --- a/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml +++ b/v2rayN/v2rayN.Desktop/Views/ProfilesView.axaml @@ -75,6 +75,15 @@ + + - - - - - + diff --git a/v2rayN/v2rayN/Views/ProfilesView.xaml b/v2rayN/v2rayN/Views/ProfilesView.xaml index 1a33966b..f66db569 100644 --- a/v2rayN/v2rayN/Views/ProfilesView.xaml +++ b/v2rayN/v2rayN/Views/ProfilesView.xaml @@ -87,6 +87,15 @@ ToolTip="{x:Static resx:ResUI.menuFastRealPing}"> + - + Header="{x:Static resx:ResUI.menuRemoveInvalidServerResult}" /> + - - - - + Date: Sun, 26 Oct 2025 10:13:49 +0800 Subject: [PATCH 090/132] Remove AppImage. Update package.sh (#8201) * Refactor AppRun script generation in packaging * Update minimum kernel version requirement to 6.13 * Update minimum kernel version requirement to 5.14 * Revise runtime dependencies with version constraints Updated runtime dependencies for package-rhel.sh to include version constraints and additional requirements. * Modify package dependencies in package-debian.sh Updated package dependencies to include libc6, fontconfig, coreutils, and bash. * Remove AppImage packaging and upload steps Removed AppImage packaging and upload steps from the workflow. * Delete package-appimage.sh * Simplify environment checks in Utils.cs Removed checks for APPIMAGE environment variable and mount path. * Update v2rayN.slnx * Remove package scripts from v2rayN solution Removed references to package-appimage.sh and pkg2appimage.yml from the solution file. --- .github/workflows/build-linux.yml | 15 ------- package-appimage.sh | 67 ------------------------------- package-debian.sh | 2 +- package-rhel.sh | 9 ++++- v2rayN/ServiceLib/Common/Utils.cs | 10 ----- v2rayN/v2rayN.sln | 2 - v2rayN/v2rayN.slnx | 2 - 7 files changed, 8 insertions(+), 99 deletions(-) delete mode 100644 package-appimage.sh diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index d5f9b1c5..0911b25c 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -68,21 +68,6 @@ jobs: file_glob: true prerelease: true - - name: Package AppImage - if: github.event.inputs.release_tag != '' - run: | - chmod a+x package-appimage.sh - ./package-appimage.sh - - - name: Upload AppImage to release - uses: svenstaro/upload-release-action@v2 - if: github.event.inputs.release_tag != '' - with: - file: ${{ github.workspace }}/v2rayN*.AppImage - tag: ${{ github.event.inputs.release_tag }} - file_glob: true - prerelease: true - # release zip archive - name: Package release zip archive if: github.event.inputs.release_tag != '' diff --git a/package-appimage.sh b/package-appimage.sh deleted file mode 100644 index 6a8bfcca..00000000 --- a/package-appimage.sh +++ /dev/null @@ -1,67 +0,0 @@ -#!/bin/bash -set -euo pipefail - -# Install deps -sudo apt update -y -sudo apt install -y libfuse2 wget file - -# Get tools -wget -qO appimagetool https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage -chmod +x appimagetool - -# x86_64 AppDir -APPDIR_X64="AppDir-x86_64" -rm -rf "$APPDIR_X64" -mkdir -p "$APPDIR_X64/usr/lib/v2rayN" "$APPDIR_X64/usr/bin" "$APPDIR_X64/usr/share/applications" "$APPDIR_X64/usr/share/pixmaps" -cp -rf "$OutputPath64"/* "$APPDIR_X64/usr/lib/v2rayN" || true -[ -f "$APPDIR_X64/usr/lib/v2rayN/v2rayN.png" ] && cp "$APPDIR_X64/usr/lib/v2rayN/v2rayN.png" "$APPDIR_X64/usr/share/pixmaps/v2rayN.png" || true -[ -f "$APPDIR_X64/usr/lib/v2rayN/v2rayN.png" ] && cp "$APPDIR_X64/usr/lib/v2rayN/v2rayN.png" "$APPDIR_X64/v2rayN.png" || true - -printf '%s\n' '#!/bin/sh' 'HERE="$(dirname "$(readlink -f "$0")")"' 'cd "$HERE/usr/lib/v2rayN"' 'exec "$HERE/usr/lib/v2rayN/v2rayN" "$@"' > "$APPDIR_X64/AppRun" -chmod +x "$APPDIR_X64/AppRun" -ln -sf usr/lib/v2rayN/v2rayN "$APPDIR_X64/usr/bin/v2rayN" -cat > "$APPDIR_X64/v2rayN.desktop" < "$APPDIR_ARM64/AppRun" -chmod +x "$APPDIR_ARM64/AppRun" -ln -sf usr/lib/v2rayN/v2rayN "$APPDIR_ARM64/usr/bin/v2rayN" -cat > "$APPDIR_ARM64/v2rayN.desktop" <= 2.34), fontconfig (>= 2.13.1), desktop-file-utils (>= 0.26), xdg-utils (>= 1.1.3), coreutils (>= 8.32), bash (>= 5.1) Description: A GUI client for Windows and Linux, support Xray core and sing-box-core and others EOF diff --git a/package-rhel.sh b/package-rhel.sh index ea537c62..8384e765 100644 --- a/package-rhel.sh +++ b/package-rhel.sh @@ -614,8 +614,13 @@ ExclusiveArch: aarch64 x86_64 Source0: __PKGROOT__.tar.gz # Runtime dependencies (Avalonia / X11 / Fonts / GL) -Requires: libX11, libXrandr, libXcursor, libXi, libXext, libxcb, libXrender, libXfixes, libXinerama, libxkbcommon -Requires: fontconfig, freetype, cairo, pango, mesa-libEGL, mesa-libGL, xdg-utils +Requires: freetype, cairo, pango, openssl, mesa-libEGL, mesa-libGL +Requires: glibc >= 2.34 +Requires: fontconfig >= 2.13.1 +Requires: desktop-file-utils >= 0.26 +Requires: xdg-utils >= 1.1.3 +Requires: coreutils >= 8.32 +Requires: bash >= 5.1 %description v2rayN Linux for Red Hat Enterprise Linux diff --git a/v2rayN/ServiceLib/Common/Utils.cs b/v2rayN/ServiceLib/Common/Utils.cs index 5ca96a88..13e550f0 100644 --- a/v2rayN/ServiceLib/Common/Utils.cs +++ b/v2rayN/ServiceLib/Common/Utils.cs @@ -994,11 +994,6 @@ public class Utils return false; } - if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("APPIMAGE"))) - { - return true; - } - var exePath = GetExePath(); var baseDir = string.IsNullOrEmpty(exePath) ? StartupPath() : Path.GetDirectoryName(exePath) ?? ""; var p = baseDir.Replace('\\', '/'); @@ -1008,11 +1003,6 @@ public class Utils return false; } - if (p.Contains("/.mount_", StringComparison.Ordinal)) - { - return true; - } - if (p.StartsWith("/opt/v2rayN", StringComparison.OrdinalIgnoreCase)) { return true; diff --git a/v2rayN/v2rayN.sln b/v2rayN/v2rayN.sln index 85f985fe..4e9ee76e 100644 --- a/v2rayN/v2rayN.sln +++ b/v2rayN/v2rayN.sln @@ -26,11 +26,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GitHub Action", "GitHub Act ..\.github\workflows\build-osx.yml = ..\.github\workflows\build-osx.yml ..\.github\workflows\build-windows-desktop.yml = ..\.github\workflows\build-windows-desktop.yml ..\.github\workflows\build-windows.yml = ..\.github\workflows\build-windows.yml - ..\package-appimage.sh = ..\package-appimage.sh ..\package-debian.sh = ..\package-debian.sh ..\package-osx.sh = ..\package-osx.sh ..\package-release-zip.sh = ..\package-release-zip.sh - ..\pkg2appimage.yml = ..\pkg2appimage.yml ..\.github\workflows\winget-publish.yml = ..\.github\workflows\winget-publish.yml EndProjectSection EndProject diff --git a/v2rayN/v2rayN.slnx b/v2rayN/v2rayN.slnx index 05c0ea96..1550478b 100644 --- a/v2rayN/v2rayN.slnx +++ b/v2rayN/v2rayN.slnx @@ -6,11 +6,9 @@ - - From 22c233f0cdffe9177e60509bb3b34c06cf253550 Mon Sep 17 00:00:00 2001 From: Aron Yang Date: Sun, 26 Oct 2025 11:09:23 +0800 Subject: [PATCH 091/132] Fix TUN mode cleanup on Linux/macOS (#8202) --- v2rayN/ServiceLib/Manager/CoreAdminManager.cs | 2 +- v2rayN/ServiceLib/Sample/kill_as_sudo_linux_sh | 18 +++++++++++++++--- v2rayN/ServiceLib/Sample/kill_as_sudo_osx_sh | 14 ++++++++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/v2rayN/ServiceLib/Manager/CoreAdminManager.cs b/v2rayN/ServiceLib/Manager/CoreAdminManager.cs index 0d52f1c7..dbd19d38 100644 --- a/v2rayN/ServiceLib/Manager/CoreAdminManager.cs +++ b/v2rayN/ServiceLib/Manager/CoreAdminManager.cs @@ -34,7 +34,7 @@ public class CoreAdminManager StringBuilder sb = new(); sb.AppendLine("#!/bin/bash"); var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}"; - sb.AppendLine($"sudo -S {cmdLine}"); + sb.AppendLine($"exec sudo -S -- {cmdLine}"); var shFilePath = await FileManager.CreateLinuxShellFile("run_as_sudo.sh", sb.ToString(), true); var procService = new ProcessService( diff --git a/v2rayN/ServiceLib/Sample/kill_as_sudo_linux_sh b/v2rayN/ServiceLib/Sample/kill_as_sudo_linux_sh index 7f62a532..21e17408 100644 --- a/v2rayN/ServiceLib/Sample/kill_as_sudo_linux_sh +++ b/v2rayN/ServiceLib/Sample/kill_as_sudo_linux_sh @@ -28,15 +28,15 @@ fi kill_children() { local parent=$1 local children=$(ps -o pid --no-headers --ppid "$parent") - + # Output information about processes being terminated echo "Processing children of PID: $parent..." - + # Process each child for child in $children; do # Recursively find and kill child's children first kill_children "$child" - + # Force kill the child process echo "Terminating child process: $child" kill -9 "$child" 2>/dev/null || true @@ -47,6 +47,18 @@ echo "============================================" echo "Starting termination of process $PID and all its children" echo "============================================" +# Try graceful termination first +echo "Attempting graceful termination (SIGTERM) of PID: $PID" +kill -15 "$PID" 2>/dev/null || true +sleep 1 +# If still running, fall back to kill_children +if ps -p $PID > /dev/null; then + echo "Process $PID did not exit after SIGTERM; proceeding with forced termination of its children and itself" +else + echo "Process $PID exited cleanly after SIGTERM" + exit 0 +fi + # Find and kill all child processes kill_children "$PID" diff --git a/v2rayN/ServiceLib/Sample/kill_as_sudo_osx_sh b/v2rayN/ServiceLib/Sample/kill_as_sudo_osx_sh index 94011d6f..61160fa2 100644 --- a/v2rayN/ServiceLib/Sample/kill_as_sudo_osx_sh +++ b/v2rayN/ServiceLib/Sample/kill_as_sudo_osx_sh @@ -42,6 +42,20 @@ echo "============================================" echo "Starting termination of process $PID and all its descendants" echo "============================================" +# Try graceful termination first +echo "Attempting graceful termination (SIGTERM) of PID: $PID" +kill -15 "$PID" 2>/dev/null || true +sleep 1 + +# If still running, fall back to kill_descendants +# Use the macOS-native 'kill -0' check +if kill -0 $PID 2>/dev/null; then + echo "Process $PID did not exit after SIGTERM; proceeding with forced termination of its descendants and itself" +else + echo "Process $PID exited cleanly after SIGTERM" + exit 0 +fi + # Find and kill all descendant processes kill_descendants "$PID" From 3c4865982b363baf358f0a9b334c51e40337cea0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Oct 2025 13:55:57 +0800 Subject: [PATCH 092/132] Bump actions/upload-artifact from 4.6.2 to 5.0.0 (#8211) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.6.2 to 5.0.0. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4.6.2...v5.0.0) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: 5.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-linux.yml | 4 ++-- .github/workflows/build-osx.yml | 2 +- .github/workflows/build-windows-desktop.yml | 2 +- .github/workflows/build-windows.yml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 0911b25c..3f858e30 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -45,7 +45,7 @@ jobs: dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64 - name: Upload build artifacts - uses: actions/upload-artifact@v4.6.2 + uses: actions/upload-artifact@v5.0.0 with: name: v2rayN-linux path: | @@ -104,7 +104,7 @@ jobs: - name: Upload RPM artifacts if: github.event.inputs.release_tag != '' - uses: actions/upload-artifact@v4.6.2 + uses: actions/upload-artifact@v5.0.0 with: name: v2rayN-rpm path: | diff --git a/.github/workflows/build-osx.yml b/.github/workflows/build-osx.yml index 97c002c7..5825ac9d 100644 --- a/.github/workflows/build-osx.yml +++ b/.github/workflows/build-osx.yml @@ -45,7 +45,7 @@ jobs: dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64 - name: Upload build artifacts - uses: actions/upload-artifact@v4.6.2 + uses: actions/upload-artifact@v5.0.0 with: name: v2rayN-macos path: | diff --git a/.github/workflows/build-windows-desktop.yml b/.github/workflows/build-windows-desktop.yml index 3b28599d..65b86126 100644 --- a/.github/workflows/build-windows-desktop.yml +++ b/.github/workflows/build-windows-desktop.yml @@ -45,7 +45,7 @@ jobs: dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 --self-contained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPathArm64 - name: Upload build artifacts - uses: actions/upload-artifact@v4.6.2 + uses: actions/upload-artifact@v5.0.0 with: name: v2rayN-windows-desktop path: | diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index fea3aa70..31b5a90c 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -46,7 +46,7 @@ jobs: - name: Upload build artifacts - uses: actions/upload-artifact@v4.6.2 + uses: actions/upload-artifact@v5.0.0 with: name: v2rayN-windows path: | From bf1fb0f92e3cf50ad9205f11ccdf07f72dcee585 Mon Sep 17 00:00:00 2001 From: JieXu Date: Wed, 29 Oct 2025 09:21:37 +0800 Subject: [PATCH 093/132] RPM file remove x86-64-v1 Support. Update French translation. (#8216) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update ResUI.fr.resx * Update build-linux.yml * Update package-rhel.sh * 更新 build-linux.yml * Update ResUI.fr.resx * Update ResUI.fr.resx * Update ResUI.fr.resx * Update ResUI.fr.resx * Update ResUI.fr.resx * Update ResUI.fr.resx * Update ResUI.fr.resx * Update ResUI.fr.resx --- .github/workflows/build-linux.yml | 89 +++++++++++++++++++--------- package-rhel.sh | 17 ++++++ v2rayN/ServiceLib/Resx/ResUI.fr.resx | 44 +++++++------- 3 files changed, 99 insertions(+), 51 deletions(-) diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 3f858e30..e030e465 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -9,6 +9,12 @@ on: push: branches: - master + tags: + - 'v*' + - 'V*' + +permissions: + contents: write env: OutputArch: "linux-64" @@ -21,7 +27,6 @@ jobs: strategy: matrix: configuration: [Release] - runs-on: ubuntu-24.04 steps: @@ -31,18 +36,18 @@ jobs: submodules: 'recursive' fetch-depth: '0' - - name: Setup + - name: Setup .NET uses: actions/setup-dotnet@v5.0.0 with: dotnet-version: '8.0.x' - name: Build run: | - cd v2rayN - dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-x64 --self-contained=true -o $OutputPath64 - dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-arm64 --self-contained=true -o $OutputPathArm64 - dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-x64 --self-contained=true -p:PublishTrimmed=true -o $OutputPath64 - dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64 + cd v2rayN + dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-x64 --self-contained=true -o "$OutputPath64" + dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-arm64 --self-contained=true -o "$OutputPathArm64" + dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-x64 --self-contained=true -p:PublishTrimmed=true -o "$OutputPath64" + dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 --self-contained=true -p:PublishTrimmed=true -o "$OutputPathArm64" - name: Upload build artifacts uses: actions/upload-artifact@v5.0.0 @@ -56,8 +61,8 @@ jobs: if: github.event.inputs.release_tag != '' run: | chmod 755 package-debian.sh - ./package-debian.sh $OutputArch $OutputPath64 ${{ github.event.inputs.release_tag }} - ./package-debian.sh $OutputArchArm $OutputPathArm64 ${{ github.event.inputs.release_tag }} + ./package-debian.sh "$OutputArch" "$OutputPath64" "${{ github.event.inputs.release_tag }}" + ./package-debian.sh "$OutputArchArm" "$OutputPathArm64" "${{ github.event.inputs.release_tag }}" - name: Upload deb to release uses: svenstaro/upload-release-action@v2 @@ -73,8 +78,8 @@ jobs: if: github.event.inputs.release_tag != '' run: | chmod 755 package-release-zip.sh - ./package-release-zip.sh $OutputArch $OutputPath64 - ./package-release-zip.sh $OutputArchArm $OutputPathArm64 + ./package-release-zip.sh "$OutputArch" "$OutputPath64" + ./package-release-zip.sh "$OutputArchArm" "$OutputPathArm64" - name: Upload zip archive to release uses: svenstaro/upload-release-action@v2 @@ -85,36 +90,62 @@ jobs: file_glob: true prerelease: true - # release RHEL package - - name: Package RPM (RHEL-family) - if: github.event.inputs.release_tag != '' + rpm: + needs: build + if: | + (github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag != '') || + (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')) + runs-on: ubuntu-24.04 + container: + image: quay.io/almalinuxorg/10-base:latest + options: --platform=linux/amd64/v2 + env: + RELEASE_TAG: ${{ github.event.inputs.release_tag != '' && github.event.inputs.release_tag || github.ref_name }} + + steps: + - name: Prepare tools (Red Hat) run: | - chmod 755 package-rhel.sh - # Build for both x86_64 and aarch64 in one go (explicit version passed; no --buildfrom) - ./package-rhel.sh "${{ github.event.inputs.release_tag }}" --arch all + dnf -y makecache + dnf -y install epel-release + dnf -y install sudo git rpm-build rpmdevtools dnf-plugins-core rsync findutils tar gzip unzip which + + - name: Checkout repo (for scripts) + uses: actions/checkout@v5.0.0 + with: + submodules: 'recursive' + fetch-depth: '0' + + - name: Restore build artifacts + uses: actions/download-artifact@v4 + with: + name: v2rayN-linux + path: ${{ github.workspace }}/v2rayN/Release + + - name: Ensure script permissions + run: chmod 755 package-rhel.sh + + - name: Package RPM (RHEL-family) + run: ./package-rhel.sh "${RELEASE_TAG}" --arch all - name: Collect RPMs into workspace - if: github.event.inputs.release_tag != '' run: | - mkdir -p "${{ github.workspace }}/dist/rpm" - rsync -av "$HOME/rpmbuild/RPMS/" "${{ github.workspace }}/dist/rpm/" - # Rename to requested filenames - find "${{ github.workspace }}/dist/rpm" -name "v2rayN-*-1.x86_64.rpm" -exec mv {} "${{ github.workspace }}/dist/rpm/v2rayN-linux-rhel-x64.rpm" \; || true - find "${{ github.workspace }}/dist/rpm" -name "v2rayN-*-1.aarch64.rpm" -exec mv {} "${{ github.workspace }}/dist/rpm/v2rayN-linux-rhel-arm64.rpm" \; || true + mkdir -p "$GITHUB_WORKSPACE/dist/rpm" + rsync -av "$HOME/rpmbuild/RPMS/" "$GITHUB_WORKSPACE/dist/rpm/" || true + find "$GITHUB_WORKSPACE/dist/rpm" -name "v2rayN-*-1*.x86_64.rpm" -exec mv {} "$GITHUB_WORKSPACE/dist/rpm/v2rayN-linux-rhel-64.rpm" \; || true + find "$GITHUB_WORKSPACE/dist/rpm" -name "v2rayN-*-1*.aarch64.rpm" -exec mv {} "$GITHUB_WORKSPACE/dist/rpm/v2rayN-linux-rhel-arm64.rpm" \; || true + echo "==== Dist tree ====" + ls -R "$GITHUB_WORKSPACE/dist/rpm" || true - name: Upload RPM artifacts - if: github.event.inputs.release_tag != '' uses: actions/upload-artifact@v5.0.0 with: name: v2rayN-rpm - path: | - ${{ github.workspace }}/dist/rpm/**/*.rpm + path: dist/rpm/**/*.rpm - name: Upload RPMs to release uses: svenstaro/upload-release-action@v2 - if: github.event.inputs.release_tag != '' with: - file: ${{ github.workspace }}/dist/rpm/**/*.rpm - tag: ${{ github.event.inputs.release_tag }} + file: dist/rpm/**/*.rpm + tag: ${{ env.RELEASE_TAG }} file_glob: true prerelease: true diff --git a/package-rhel.sh b/package-rhel.sh index 8384e765..b93ea82c 100644 --- a/package-rhel.sh +++ b/package-rhel.sh @@ -19,6 +19,23 @@ else exit 1 fi +# ======================== Kernel version check (require >= 6.11) ======================= +MIN_KERNEL_MAJOR=6 +MIN_KERNEL_MINOR=11 +KERNEL_FULL=$(uname -r) +KERNEL_MAJOR=$(echo "$KERNEL_FULL" | cut -d. -f1) +KERNEL_MINOR=$(echo "$KERNEL_FULL" | cut -d. -f2) + +echo "[INFO] Detected kernel version: $KERNEL_FULL" + +if (( KERNEL_MAJOR < MIN_KERNEL_MAJOR )) || { (( KERNEL_MAJOR == MIN_KERNEL_MAJOR )) && (( KERNEL_MINOR < MIN_KERNEL_MINOR )); }; then + echo "[ERROR] Kernel $KERNEL_FULL is too old. Requires Linux >= ${MIN_KERNEL_MAJOR}.${MIN_KERNEL_MINOR}." + echo "Please upgrade your system or use a newer container (e.g. Fedora 42+, RHEL 10+, Debian 13+)." + exit 1 +fi + +echo "[OK] Kernel version >= ${MIN_KERNEL_MAJOR}.${MIN_KERNEL_MINOR}." + # ===== Config & Parse arguments ========================================================= VERSION_ARG="${1:-}" # Pass version number like 7.13.8, or leave empty WITH_CORE="both" # Default: bundle both xray+sing-box diff --git a/v2rayN/ServiceLib/Resx/ResUI.fr.resx b/v2rayN/ServiceLib/Resx/ResUI.fr.resx index 2222d8c4..a56802e3 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fr.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fr.resx @@ -472,7 +472,7 @@ Langue (redémarrage requis) - Importer des liens depuis le presse-papiers (Ctrl+V) + Importer liens depuis le presse-papiers (Ctrl+V) Scanner le QR code à l’écran (Ctrl+S) @@ -619,7 +619,7 @@ Sécurité couche transport (TLS) - *tcp par défaut ; un mauvais choix empêchera la connexion + *tcp par défaut ; un mauvais choix bloque la connexion Type de Core @@ -652,7 +652,7 @@ Port Socks - *Valeur du port Socks pour la configuration personnalisée (facultatif). Si défini, Xray/sing-box (Tun) démarrera un service Socks en amont supplémentaire pour fournir le routage sélectif et l’affichage de la vitesse. + *Valeur du port Socks (config perso, optionnelle). Si défini, Xray/sing-box (Tun) démarre un service Socks en amont supplémentaire pour fournir le routage sélectif et l’affichage de la vitesse. Parcourir @@ -670,7 +670,7 @@ Masquer la fenêtre au démarrage - Intervalle de mise à jour automatique des fichiers Geo (heures) + Intervalle de mise à jour auto des fichiers Geo (heures) Core : paramètres de base @@ -691,7 +691,7 @@ domainStrategy de Freedom (sortant) - Auto-ajuster la largeur des colonnes après maj. des abonnements + Auto-ajuster la largeur des colonnes après maj. abonnements Vérifier les mises à jour pré-version (à activer avec prudence) @@ -724,7 +724,7 @@ Mot de passe d’authentification - DNS personnalisés (plusieurs possibles, séparés par des virgules) + DNS perso (plusieurs configurables, séparés par virgules) Lever la restriction de proxy en boucle locale pour les applications Win10 UWP @@ -865,7 +865,7 @@ Documentation détaillée des règles - Saisie de DnsObject prise en charge (format JSON), cliquer pour voir la documentation + Saisie DnsObject prise en charge (format JSON), cliquer pour doc Laissez vide pour les groupes ordinaires @@ -898,7 +898,7 @@ Filtrage par alias (regex) - Afficher les journaux + Afficher les logs Activer Tun @@ -979,7 +979,7 @@ En attente du test (appuyer sur Échap pour arrêter)... - Désactivez ceci en cas de coupures anormales + Désactiver cette option si coupure anormale Mise à jour désactivée, abonnement ignoré @@ -1009,10 +1009,10 @@ DNS personnalisé sing-box - Veuillez renseigner la structure JSON DNS ; cliquer pour voir la documentation + Saisissez la structure JSON DNS ; cliquez pour voir la doc. - Cliquez pour importer la configuration DNS par défaut + Cliquez pour importer la config DNS par défaut Stratégie résolution domaine (sing-box) @@ -1045,10 +1045,10 @@ Algo contrôle congestion - Alias de configuration du proxy amont + Alias de config du proxy amont - Alias de configuration du proxy aval + Alias de config du proxy aval Assurez-vous que l’alias config existe et est unique @@ -1114,7 +1114,7 @@ Activer le fragmentation (Fragment) - Activer le fichier de cache de sing-box (fichiers de reˋgles) + Activer le fichier de cache de sing-box (fichiers règles) Set de règles sing-box perso @@ -1207,7 +1207,7 @@ Stratégie de résolution par défaut des sortants - Orientation de la mise en page principale (redémarrage requis) + Orientation mise en page principale (redémarrage requis) Adresse de résolution de domaine pour sortants @@ -1330,7 +1330,7 @@ JSON brut XHTTP Extra, format : { XHTTPObject } - Masquer dans la barre d’état lors de la fermeture de la fenêtre + Masquer dans la barre d’état à la fermeture de la fenêtre Niveau de concurrence lors des tests multithread @@ -1351,7 +1351,7 @@ Thème - Copier la commande proxy terminal vers le presse-papiers + Copier la cmd proxy terminal dans le presse-papiers Recommencer le test des éléments échoués, {0} restants. Appuyez sur Échap pour arrêter... @@ -1369,7 +1369,7 @@ Plage de ports sautés - Écrase le port ; pour plusieurs groupes, séparez par des virgules (,) + Écrase le port ; pour plusieurs groupes, séparer par virgules (,) Générer un groupe de stratégie depuis plusieurs profils @@ -1447,7 +1447,7 @@ Valider les IP des domaines de la région concernée - Après configuration, les IP renvoyées pour les domaines régionaux (p. ex. geosite:cn) seront validées ; seules les IP attendues seront retournées + Après config, les IP renvoyées des domaines régionaux (ex. geosite:cn) seront vérifiées ; seules les IP attendues seront retournées. Activer le DNS personnalisé @@ -1471,7 +1471,7 @@ Modèle de configuration complet v2ray - Ajoute seulement la configuration sortante, routing.balancers et routing.rules.outboundTag. Voir la documentation. + Ajoute seulement la config sortante, routing.balancers et routing.rules.outboundTag. Voir la doc. N’ajoutez pas de sorties pour protocoles non-proxy. @@ -1492,10 +1492,10 @@ Début de l’analyse et du traitement du contenu d’abonnement - Sélectionner une configuration + Choisir une config. - Actif globalement par défaut, avec filtrage FakeIP intégré ; ne fonctionne que dans sing-box + Actif globalement par défaut, avec filtre FakeIP intégré ; ne fonctionne que dans sing-box Veuillez ajouter au moins une configuration From c63d4e83f9a375c016e2b2c9d01da61a078a16e3 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 29 Oct 2025 20:20:40 +0800 Subject: [PATCH 094/132] Use OperatingSystem replace RuntimeInformation --- v2rayN/ServiceLib/Common/Utils.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/v2rayN/ServiceLib/Common/Utils.cs b/v2rayN/ServiceLib/Common/Utils.cs index 13e550f0..7f842ddf 100644 --- a/v2rayN/ServiceLib/Common/Utils.cs +++ b/v2rayN/ServiceLib/Common/Utils.cs @@ -963,13 +963,13 @@ public class Utils #region Platform - public static bool IsWindows() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + public static bool IsWindows() => OperatingSystem.IsWindows(); - public static bool IsLinux() => RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + public static bool IsLinux() => OperatingSystem.IsLinux(); - public static bool IsOSX() => RuntimeInformation.IsOSPlatform(OSPlatform.OSX); + public static bool IsOSX() => OperatingSystem.IsMacOS(); - public static bool IsNonWindows() => !RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + public static bool IsNonWindows() => !OperatingSystem.IsWindows(); public static string GetExeName(string name) { From 86eb8297ddcb77c17c98806a0051b91eb0d86fc9 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 29 Oct 2025 20:20:44 +0800 Subject: [PATCH 095/132] Update JsonUtils.cs --- v2rayN/ServiceLib/Common/JsonUtils.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/v2rayN/ServiceLib/Common/JsonUtils.cs b/v2rayN/ServiceLib/Common/JsonUtils.cs index b5bcc7fe..7e2b7f78 100644 --- a/v2rayN/ServiceLib/Common/JsonUtils.cs +++ b/v2rayN/ServiceLib/Common/JsonUtils.cs @@ -35,9 +35,13 @@ public class JsonUtils /// /// /// - public static T DeepCopy(T obj) + public static T? DeepCopy(T? obj) { - return Deserialize(Serialize(obj, false))!; + if (obj is null) + { + return default; + } + return Deserialize(Serialize(obj, false)); } /// @@ -67,7 +71,7 @@ public class JsonUtils /// /// /// - public static JsonNode? ParseJson(string strJson) + public static JsonNode? ParseJson(string? strJson) { try { @@ -116,7 +120,7 @@ public class JsonUtils /// /// /// - public static string Serialize(object? obj, JsonSerializerOptions options) + public static string Serialize(object? obj, JsonSerializerOptions? options) { var result = string.Empty; try @@ -125,7 +129,7 @@ public class JsonUtils { return result; } - result = JsonSerializer.Serialize(obj, options); + result = JsonSerializer.Serialize(obj, options ?? _defaultSerializeOptions); } catch (Exception ex) { From c2ef3a4a8c8ea2f2aa1cba8921c44f78f0c278a5 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 29 Oct 2025 20:21:41 +0800 Subject: [PATCH 096/132] Add Design.IsDesignMode to the desktop version --- v2rayN/v2rayN.Desktop/App.axaml.cs | 8 +++++--- v2rayN/v2rayN.Desktop/Program.cs | 21 ++++++++++++++------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/v2rayN/v2rayN.Desktop/App.axaml.cs b/v2rayN/v2rayN.Desktop/App.axaml.cs index 5da86c6e..35371f0f 100644 --- a/v2rayN/v2rayN.Desktop/App.axaml.cs +++ b/v2rayN/v2rayN.Desktop/App.axaml.cs @@ -10,15 +10,17 @@ public partial class App : Application AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; - - DataContext = StatusBarViewModel.Instance; } public override void OnFrameworkInitializationCompleted() { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - AppManager.Instance.InitComponents(); + if (!Design.IsDesignMode) + { + AppManager.Instance.InitComponents(); + DataContext = StatusBarViewModel.Instance; + } desktop.Exit += OnExit; desktop.MainWindow = new MainWindow(); diff --git a/v2rayN/v2rayN.Desktop/Program.cs b/v2rayN/v2rayN.Desktop/Program.cs index 1a0ce38e..70c43130 100644 --- a/v2rayN/v2rayN.Desktop/Program.cs +++ b/v2rayN/v2rayN.Desktop/Program.cs @@ -54,12 +54,19 @@ internal class Program // Avalonia configuration, don't remove; also used by visual designer. public static AppBuilder BuildAvaloniaApp() { - return AppBuilder.Configure() - .UsePlatformDetect() - //.WithInterFont() - .WithFontByDefault() - .LogToTrace() - .UseReactiveUI() - .With(new MacOSPlatformOptions { ShowInDock = AppManager.Instance.Config.UiItem.MacOSShowInDock }); + var builder = AppBuilder.Configure() + .UsePlatformDetect() + //.WithInterFont() + .WithFontByDefault() + .LogToTrace() + .UseReactiveUI(); + + if (OperatingSystem.IsMacOS()) + { + var showInDock = Design.IsDesignMode || AppManager.Instance.Config.UiItem.MacOSShowInDock; + builder = builder.With(new MacOSPlatformOptions { ShowInDock = showInDock }); + } + + return builder; } } From 12fc3e9566f56f6fa7e4b4765541ef412be31de4 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Thu, 30 Oct 2025 20:00:15 +0800 Subject: [PATCH 097/132] Bug fix --- v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs index c3bba363..e47f93a5 100644 --- a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs @@ -402,9 +402,9 @@ public partial class MainWindow : WindowBase public void ShowHideWindow(bool? blShow) { var bl = blShow ?? - Utils.IsLinux() + (Utils.IsLinux() ? (!_config.UiItem.ShowInTaskbar ^ (WindowState == WindowState.Minimized)) - : !_config.UiItem.ShowInTaskbar; + : !_config.UiItem.ShowInTaskbar); if (bl) { this.Show(); From 554632cc0751822b7397a16774b352aebee6bf5e Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Thu, 30 Oct 2025 20:34:47 +0800 Subject: [PATCH 098/132] Bug fix https://github.com/2dust/v2rayN/issues/8207 --- v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs index e47f93a5..e88db5d1 100644 --- a/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/MainWindow.axaml.cs @@ -168,7 +168,7 @@ public partial class MainWindow : WindowBase } menuAddServerViaScan.IsVisible = false; - if (_config.UiItem.AutoHideStartup) + if (_config.UiItem.AutoHideStartup && Utils.IsWindows()) { this.WindowState = WindowState.Minimized; } From bf3703bca1fb9dac6bfbe8c55c6b349668343fcd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Oct 2025 20:36:43 +0800 Subject: [PATCH 099/132] Bump actions/download-artifact from 4 to 6 (#8225) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 6. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4...v6) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index e030e465..b67527a1 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -116,7 +116,7 @@ jobs: fetch-depth: '0' - name: Restore build artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v6 with: name: v2rayN-linux path: ${{ github.workspace }}/v2rayN/Release From 38b2a7d2ca1da517c70da1c863fb29ba0287af91 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 31 Oct 2025 17:50:28 +0800 Subject: [PATCH 100/132] Rename QRCodeWindowsUtils --- .../v2rayN/Common/{QRCodeUtils.cs => QRCodeWindowsUtils.cs} | 6 +++--- v2rayN/v2rayN/GlobalUsings.cs | 1 + v2rayN/v2rayN/Views/MainWindow.xaml.cs | 2 +- v2rayN/v2rayN/Views/ProfilesView.xaml.cs | 2 +- v2rayN/v2rayN/Views/SubSettingWindow.xaml.cs | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) rename v2rayN/v2rayN/Common/{QRCodeUtils.cs => QRCodeWindowsUtils.cs} (93%) diff --git a/v2rayN/v2rayN/Common/QRCodeUtils.cs b/v2rayN/v2rayN/Common/QRCodeWindowsUtils.cs similarity index 93% rename from v2rayN/v2rayN/Common/QRCodeUtils.cs rename to v2rayN/v2rayN/Common/QRCodeWindowsUtils.cs index 6b0bc38d..9e001a1d 100644 --- a/v2rayN/v2rayN/Common/QRCodeUtils.cs +++ b/v2rayN/v2rayN/Common/QRCodeWindowsUtils.cs @@ -2,9 +2,9 @@ using System.Drawing; using System.Windows.Media; using System.Windows.Media.Imaging; -namespace v2rayN; +namespace v2rayN.Common; -public class QRCodeUtils +public class QRCodeWindowsUtils { public static ImageSource? GetQRCode(string? strContent) { @@ -14,7 +14,7 @@ public class QRCodeUtils } try { - var qrCodeImage = ServiceLib.Common.QRCodeUtils.GenQRCode(strContent); + var qrCodeImage = QRCodeUtils.GenQRCode(strContent); return qrCodeImage is null ? null : ByteToImage(qrCodeImage); } catch (Exception ex) diff --git a/v2rayN/v2rayN/GlobalUsings.cs b/v2rayN/v2rayN/GlobalUsings.cs index a8fa461d..1f2d1936 100644 --- a/v2rayN/v2rayN/GlobalUsings.cs +++ b/v2rayN/v2rayN/GlobalUsings.cs @@ -31,3 +31,4 @@ global using ServiceLib.Manager; global using ServiceLib.Models; global using ServiceLib.Resx; global using ServiceLib.ViewModels; +global using v2rayN.Common; diff --git a/v2rayN/v2rayN/Views/MainWindow.xaml.cs b/v2rayN/v2rayN/Views/MainWindow.xaml.cs index 9dc818ee..a9114813 100644 --- a/v2rayN/v2rayN/Views/MainWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/MainWindow.xaml.cs @@ -328,7 +328,7 @@ public partial class MainWindow if (Application.Current?.MainWindow is Window window) { - var bytes = QRCodeUtils.CaptureScreen(window); + var bytes = QRCodeWindowsUtils.CaptureScreen(window); await ViewModel?.ScanScreenResult(bytes); } diff --git a/v2rayN/v2rayN/Views/ProfilesView.xaml.cs b/v2rayN/v2rayN/Views/ProfilesView.xaml.cs index 79d4cdb9..ac79a8c8 100644 --- a/v2rayN/v2rayN/Views/ProfilesView.xaml.cs +++ b/v2rayN/v2rayN/Views/ProfilesView.xaml.cs @@ -170,7 +170,7 @@ public partial class ProfilesView public async void ShareServer(string url) { - var img = QRCodeUtils.GetQRCode(url); + var img = QRCodeWindowsUtils.GetQRCode(url); var dialog = new QrcodeView() { imgQrcode = { Source = img }, diff --git a/v2rayN/v2rayN/Views/SubSettingWindow.xaml.cs b/v2rayN/v2rayN/Views/SubSettingWindow.xaml.cs index f44739d3..14b3aa38 100644 --- a/v2rayN/v2rayN/Views/SubSettingWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/SubSettingWindow.xaml.cs @@ -64,7 +64,7 @@ public partial class SubSettingWindow { return; } - var img = QRCodeUtils.GetQRCode(url); + var img = QRCodeWindowsUtils.GetQRCode(url); var dialog = new QrcodeView() { imgQrcode = { Source = img }, From 67494108ad147a85639329643add400907d5a873 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 31 Oct 2025 19:05:57 +0800 Subject: [PATCH 101/132] up 7.15.7 --- v2rayN/Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2rayN/Directory.Build.props b/v2rayN/Directory.Build.props index bea02572..d97ccb93 100644 --- a/v2rayN/Directory.Build.props +++ b/v2rayN/Directory.Build.props @@ -1,7 +1,7 @@ - 7.15.6 + 7.15.7 From 18ea6fdc0066120d8bd5d4c47027bf14a287dd1d Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 31 Oct 2025 20:25:45 +0800 Subject: [PATCH 102/132] Code clean --- v2rayN/ServiceLib/Common/Utils.cs | 2 +- v2rayN/ServiceLib/Common/WindowsJob.cs | 3 --- v2rayN/ServiceLib/Handler/ConfigHandler.cs | 16 ++++++++-------- v2rayN/ServiceLib/Handler/CoreConfigHandler.cs | 2 +- v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs | 2 +- v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs | 4 ++-- .../ServiceLib/Manager/ActionPrecheckManager.cs | 2 +- v2rayN/ServiceLib/Models/ProfileItem.cs | 2 +- .../CoreConfig/V2ray/CoreConfigV2rayService.cs | 8 ++++---- .../V2ray/V2rayConfigTemplateService.cs | 6 +++--- .../Services/CoreConfig/V2ray/V2rayDnsService.cs | 4 ++-- .../CoreConfig/V2ray/V2rayInboundService.cs | 2 +- .../CoreConfig/V2ray/V2rayOutboundService.cs | 14 +++++++------- .../CoreConfig/V2ray/V2rayStatisticService.cs | 2 +- v2rayN/ServiceLib/Services/UpdateService.cs | 6 +++--- .../ViewModels/ClashProxiesViewModel.cs | 4 ++-- .../ViewModels/OptionSettingViewModel.cs | 6 +++--- .../ServiceLib/ViewModels/ProfilesViewModel.cs | 2 +- .../ViewModels/RoutingRuleSettingViewModel.cs | 6 +++--- .../ServiceLib/ViewModels/StatusBarViewModel.cs | 12 ++++++------ 20 files changed, 51 insertions(+), 54 deletions(-) diff --git a/v2rayN/ServiceLib/Common/Utils.cs b/v2rayN/ServiceLib/Common/Utils.cs index 7f842ddf..2075342a 100644 --- a/v2rayN/ServiceLib/Common/Utils.cs +++ b/v2rayN/ServiceLib/Common/Utils.cs @@ -424,7 +424,7 @@ public class Utils // Handle IPv6 addresses, e.g., "[2001:db8::1]:443" if (authority.StartsWith("[") && authority.Contains("]")) { - int closingBracketIndex = authority.LastIndexOf(']'); + var closingBracketIndex = authority.LastIndexOf(']'); if (closingBracketIndex < authority.Length - 1 && authority[closingBracketIndex + 1] == ':') { // Port exists diff --git a/v2rayN/ServiceLib/Common/WindowsJob.cs b/v2rayN/ServiceLib/Common/WindowsJob.cs index f7fd2d74..dacf8586 100644 --- a/v2rayN/ServiceLib/Common/WindowsJob.cs +++ b/v2rayN/ServiceLib/Common/WindowsJob.cs @@ -1,6 +1,3 @@ -using System.Diagnostics; -using System.Runtime.InteropServices; - namespace ServiceLib.Common; /* * See: diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index 8b5acff2..7d2ca056 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -447,13 +447,13 @@ public static class ConfigHandler /// 0 if successful, -1 if failed public static async Task MoveServer(Config config, List lstProfile, int index, EMove eMove, int pos = -1) { - int count = lstProfile.Count; + var count = lstProfile.Count; if (index < 0 || index > lstProfile.Count - 1) { return -1; } - for (int i = 0; i < lstProfile.Count; i++) + for (var i = 0; i < lstProfile.Count; i++) { ProfileExManager.Instance.SetSort(lstProfile[i].IndexId, (i + 1) * 10); } @@ -527,7 +527,7 @@ public static class ConfigHandler return -1; } var ext = Path.GetExtension(fileName); - string newFileName = $"{Utils.GetGuid()}{ext}"; + var newFileName = $"{Utils.GetGuid()}{ext}"; //newFileName = Path.Combine(Utile.GetTempPath(), newFileName); try @@ -1356,7 +1356,7 @@ public static class ConfigHandler } continue; } - var profileItem = FmtHandler.ResolveConfig(str, out string msg); + var profileItem = FmtHandler.ResolveConfig(str, out var msg); if (profileItem is null) { continue; @@ -1440,7 +1440,7 @@ public static class ConfigHandler { await RemoveServersViaSubid(config, subid, isSub); } - int count = 0; + var count = 0; foreach (var it in lstProfiles) { it.Subid = subid; @@ -1530,7 +1530,7 @@ public static class ConfigHandler var lstSsServer = ShadowsocksFmt.ResolveSip008(strData); if (lstSsServer?.Count > 0) { - int counter = 0; + var counter = 0; foreach (var ssItem in lstSsServer) { ssItem.Subid = subid; @@ -1705,7 +1705,7 @@ public static class ConfigHandler var maxSort = 0; if (await SQLiteHelper.Instance.TableAsync().CountAsync() > 0) { - var lstSubs = (await AppManager.Instance.SubItems()); + var lstSubs = await AppManager.Instance.SubItems(); maxSort = lstSubs.LastOrDefault()?.Sort ?? 0; } item.Sort = maxSort + 1; @@ -1867,7 +1867,7 @@ public static class ConfigHandler /// 0 if successful, -1 if failed public static async Task MoveRoutingRule(List rules, int index, EMove eMove, int pos = -1) { - int count = rules.Count; + var count = rules.Count; if (index < 0 || index > rules.Count - 1) { return -1; diff --git a/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs b/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs index 96c72bb1..f51e2051 100644 --- a/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/CoreConfigHandler.cs @@ -58,7 +58,7 @@ public static class CoreConfigHandler File.Delete(fileName); } - string addressFileName = node.Address; + var addressFileName = node.Address; if (!File.Exists(addressFileName)) { addressFileName = Utils.GetConfigPath(addressFileName); diff --git a/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs b/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs index decda17f..4fc251b7 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/FmtHandler.cs @@ -37,7 +37,7 @@ public class FmtHandler try { - string str = config.TrimEx(); + var str = config.TrimEx(); if (str.IsNullOrEmpty()) { msg = ResUI.FailedReadConfiguration; diff --git a/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs b/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs index 32044e17..5376780f 100644 --- a/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs +++ b/v2rayN/ServiceLib/Handler/Fmt/Hysteria2Fmt.cs @@ -33,9 +33,9 @@ public class Hysteria2Fmt : BaseFmt { if (item == null) return null; - string url = string.Empty; + var url = string.Empty; - string remark = string.Empty; + var remark = string.Empty; if (item.Remarks.IsNotEmpty()) { remark = "#" + Utils.UrlEncode(item.Remarks); diff --git a/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs b/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs index 8d7077f1..a487401c 100644 --- a/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs +++ b/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs @@ -85,7 +85,7 @@ public class ActionPrecheckManager(Config config) break; case EConfigType.VLESS: - if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id) && item.Id.Length > 30) + if (item.Id.IsNullOrEmpty() || (!Utils.IsGuidByParse(item.Id) && item.Id.Length > 30)) errors.Add(string.Format(ResUI.InvalidProperty, "Id")); if (!Global.Flows.Contains(item.Flow)) errors.Add(string.Format(ResUI.InvalidProperty, "Flow")); diff --git a/v2rayN/ServiceLib/Models/ProfileItem.cs b/v2rayN/ServiceLib/Models/ProfileItem.cs index 729fa7b2..fa84ac59 100644 --- a/v2rayN/ServiceLib/Models/ProfileItem.cs +++ b/v2rayN/ServiceLib/Models/ProfileItem.cs @@ -28,7 +28,7 @@ public class ProfileItem : ReactiveObject public string GetSummary() { - var summary = $"[{(ConfigType).ToString()}] "; + var summary = $"[{ConfigType.ToString()}] "; if (IsComplex()) { summary += $"[{CoreType.ToString()}]{Remarks}"; diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs index f7fb384a..6deff64d 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/CoreConfigV2rayService.cs @@ -94,8 +94,8 @@ public partial class CoreConfigV2rayService(Config config) ret.Msg = ResUI.InitialConfiguration; - string result = EmbedUtils.GetEmbedText(Global.V2raySampleClient); - string txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound); + var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient); + var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound); if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty()) { ret.Msg = ResUI.FailedGetDefaultConfiguration; @@ -200,8 +200,8 @@ public partial class CoreConfigV2rayService(Config config) ret.Msg = ResUI.InitialConfiguration; - string result = EmbedUtils.GetEmbedText(Global.V2raySampleClient); - string txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound); + var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient); + var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound); if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty()) { ret.Msg = ResUI.FailedGetDefaultConfiguration; diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs index 986e1966..1f2583ff 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayConfigTemplateService.cs @@ -87,7 +87,7 @@ public partial class CoreConfigV2rayService } var customOutboundsNode = new JsonArray(); - + foreach (var outbound in v2rayConfig.outbounds) { if (outbound.protocol.ToLower() is "blackhole" or "dns" or "freedom") @@ -112,7 +112,7 @@ public partial class CoreConfigV2rayService } customOutboundsNode.Add(JsonUtils.DeepCopy(outbound)); } - + if (fullConfigTemplateNode["outbounds"] is JsonArray templateOutbounds) { foreach (var outbound in templateOutbounds) @@ -120,7 +120,7 @@ public partial class CoreConfigV2rayService customOutboundsNode.Add(outbound?.DeepClone()); } } - + fullConfigTemplateNode["outbounds"] = customOutboundsNode; return await Task.FromResult(JsonUtils.Serialize(fullConfigTemplateNode)); diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs index 7170c5f0..2218d013 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayDnsService.cs @@ -347,8 +347,8 @@ public partial class CoreConfigV2rayService if (obj is null) { List servers = []; - string[] arrDNS = normalDNS.Split(','); - foreach (string str in arrDNS) + var arrDNS = normalDNS.Split(','); + foreach (var str in arrDNS) { servers.Add(str); } diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayInboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayInboundService.cs index 7753c21e..2cbdfe88 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayInboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayInboundService.cs @@ -48,7 +48,7 @@ public partial class CoreConfigV2rayService private Inbounds4Ray GetInbound(InItem inItem, EInboundProtocol protocol, bool bSocks) { - string result = EmbedUtils.GetEmbedText(Global.V2raySampleInbound); + var result = EmbedUtils.GetEmbedText(Global.V2raySampleInbound); if (result.IsNullOrEmpty()) { return new(); diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs index ef642ce5..39338c77 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs @@ -453,16 +453,16 @@ public partial class CoreConfigV2rayService }; //request Host - string request = EmbedUtils.GetEmbedText(Global.V2raySampleHttpRequestFileName); - string[] arrHost = host.Split(','); - string host2 = string.Join(",".AppendQuotes(), arrHost); + var request = EmbedUtils.GetEmbedText(Global.V2raySampleHttpRequestFileName); + var arrHost = host.Split(','); + var host2 = string.Join(",".AppendQuotes(), arrHost); request = request.Replace("$requestHost$", $"{host2.AppendQuotes()}"); request = request.Replace("$requestUserAgent$", $"{useragent.AppendQuotes()}"); //Path - string pathHttp = @"/"; + var pathHttp = @"/"; if (path.IsNotEmpty()) { - string[] arrPath = path.Split(','); + var arrPath = path.Split(','); pathHttp = string.Join(",".AppendQuotes(), arrPath); } request = request.Replace("$requestPath$", $"{pathHttp.AppendQuotes()}"); @@ -623,10 +623,10 @@ public partial class CoreConfigV2rayService // Cache for chain proxies to avoid duplicate generation var nextProxyCache = new Dictionary(); var prevProxyTags = new Dictionary(); // Map from profile name to tag - int prevIndex = 0; // Index for prev outbounds + var prevIndex = 0; // Index for prev outbounds // Process nodes - int index = 0; + var index = 0; foreach (var node in nodes) { index++; diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayStatisticService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayStatisticService.cs index 1269a11f..b2ec37b4 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayStatisticService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayStatisticService.cs @@ -6,7 +6,7 @@ public partial class CoreConfigV2rayService { if (_config.GuiItem.EnableStatistics || _config.GuiItem.DisplayRealTimeSpeed) { - string tag = EInboundProtocol.api.ToString(); + var tag = EInboundProtocol.api.ToString(); Metrics4Ray apiObj = new(); Policy4Ray policyObj = new(); SystemPolicy4Ray policySystemSetting = new(); diff --git a/v2rayN/ServiceLib/Services/UpdateService.cs b/v2rayN/ServiceLib/Services/UpdateService.cs index 16a69464..ac72b01d 100644 --- a/v2rayN/ServiceLib/Services/UpdateService.cs +++ b/v2rayN/ServiceLib/Services/UpdateService.cs @@ -167,7 +167,7 @@ public class UpdateService try { var coreInfo = CoreInfoManager.Instance.GetCoreInfo(type); - string filePath = string.Empty; + var filePath = string.Empty; foreach (var name in coreInfo.CoreExes) { var vName = Utils.GetBinPath(Utils.GetExeName(name), coreInfo.CoreType.ToString()); @@ -180,14 +180,14 @@ public class UpdateService if (!File.Exists(filePath)) { - string msg = string.Format(ResUI.NotFoundCore, @"", "", ""); + var msg = string.Format(ResUI.NotFoundCore, @"", "", ""); //ShowMsg(true, msg); return new SemanticVersion(""); } var result = await Utils.GetCliWrapOutput(filePath, coreInfo.VersionArg); var echo = result ?? ""; - string version = string.Empty; + var version = string.Empty; switch (type) { case ECoreType.v2fly: diff --git a/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs b/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs index eb67a9d4..3f636456 100644 --- a/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/ClashProxiesViewModel.cs @@ -211,7 +211,7 @@ public class ClashProxiesViewModel : MyReactiveObject } //from api - foreach (KeyValuePair kv in _proxies) + foreach (var kv in _proxies) { if (!Global.allowSelectType.Contains(kv.Value.type.ToLower())) { @@ -319,7 +319,7 @@ public class ClashProxiesViewModel : MyReactiveObject //from providers if (_providers != null) { - foreach (KeyValuePair kv in _providers) + foreach (var kv in _providers) { if (Global.proxyVehicleType.Contains(kv.Value.vehicleType.ToLower())) { diff --git a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs index 8bf21763..5d4764b8 100644 --- a/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/OptionSettingViewModel.cs @@ -273,12 +273,12 @@ public class OptionSettingViewModel : MyReactiveObject NoticeManager.Instance.Enqueue(ResUI.FillLocalListeningPort); return; } - var needReboot = (EnableStatistics != _config.GuiItem.EnableStatistics + var needReboot = EnableStatistics != _config.GuiItem.EnableStatistics || DisplayRealTimeSpeed != _config.GuiItem.DisplayRealTimeSpeed || EnableDragDropSort != _config.UiItem.EnableDragDropSort || EnableHWA != _config.GuiItem.EnableHWA || CurrentFontFamily != _config.UiItem.CurrentFontFamily - || MainGirdOrientation != (int)_config.UiItem.MainGirdOrientation); + || MainGirdOrientation != (int)_config.UiItem.MainGirdOrientation; //if (Utile.IsNullOrEmpty(Kcpmtu.ToString()) || !Utile.IsNumeric(Kcpmtu.ToString()) // || Utile.IsNullOrEmpty(Kcptti.ToString()) || !Utile.IsNumeric(Kcptti.ToString()) @@ -375,7 +375,7 @@ public class OptionSettingViewModel : MyReactiveObject private async Task SaveCoreType() { - for (int k = 1; k <= _config.CoreTypeItem.Count; k++) + for (var k = 1; k <= _config.CoreTypeItem.Count; k++) { var item = _config.CoreTypeItem[k - 1]; var type = string.Empty; diff --git a/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs b/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs index 1780e513..12f4804b 100644 --- a/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs @@ -658,7 +658,7 @@ public class ProfilesViewModel : MyReactiveObject } _dicHeaderSort.TryAdd(colName, true); - _dicHeaderSort.TryGetValue(colName, out bool asc); + _dicHeaderSort.TryGetValue(colName, out var asc); if (await ConfigHandler.SortServers(_config, _config.SubIndexId, colName, asc) != 0) { return; diff --git a/v2rayN/ServiceLib/ViewModels/RoutingRuleSettingViewModel.cs b/v2rayN/ServiceLib/ViewModels/RoutingRuleSettingViewModel.cs index 445e61bc..71d42218 100644 --- a/v2rayN/ServiceLib/ViewModels/RoutingRuleSettingViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/RoutingRuleSettingViewModel.cs @@ -215,7 +215,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject private async Task SaveRoutingAsync() { - string remarks = SelectedRouting.Remarks; + var remarks = SelectedRouting.Remarks; if (remarks.IsNullOrEmpty()) { NoticeManager.Instance.Enqueue(ResUI.PleaseFillRemarks); @@ -286,7 +286,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject return; } - DownloadService downloadHandle = new DownloadService(); + var downloadHandle = new DownloadService(); var result = await downloadHandle.TryDownloadString(url, true, ""); var ret = await AddBatchRoutingRulesAsync(SelectedRouting, result); if (ret == 0) @@ -298,7 +298,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject private async Task AddBatchRoutingRulesAsync(RoutingItem routingItem, string? clipboardData) { - bool blReplace = false; + var blReplace = false; if (await _updateView?.Invoke(EViewAction.AddBatchRoutingRulesYesNo, null) == false) { blReplace = true; diff --git a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs index 707722ad..42cdafa8 100644 --- a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs @@ -313,10 +313,10 @@ public class StatusBarViewModel : MyReactiveObject } BlServers = true; - for (int k = 0; k < lstModel.Count; k++) + for (var k = 0; k < lstModel.Count; k++) { ProfileItem it = lstModel[k]; - string name = it.GetSummary(); + var name = it.GetSummary(); var item = new ComboItem() { ID = it.IndexId, Text = name }; Servers.Add(item); @@ -394,10 +394,10 @@ public class StatusBarViewModel : MyReactiveObject { await SysProxyHandler.UpdateSysProxy(_config, false); - BlSystemProxyClear = (type == ESysProxyType.ForcedClear); - BlSystemProxySet = (type == ESysProxyType.ForcedChange); - BlSystemProxyNothing = (type == ESysProxyType.Unchanged); - BlSystemProxyPac = (type == ESysProxyType.Pac); + BlSystemProxyClear = type == ESysProxyType.ForcedClear; + BlSystemProxySet = type == ESysProxyType.ForcedChange; + BlSystemProxyNothing = type == ESysProxyType.Unchanged; + BlSystemProxyPac = type == ESysProxyType.Pac; if (blChange) { From 1b5069a933cba0c2d87240e3600442b5b7405bba Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 31 Oct 2025 20:25:50 +0800 Subject: [PATCH 103/132] Code clean --- v2rayN/v2rayN/App.xaml.cs | 4 +-- v2rayN/v2rayN/Common/UI.cs | 2 +- v2rayN/v2rayN/Common/WindowsUtils.cs | 12 ++++---- v2rayN/v2rayN/Manager/HotkeyManager.cs | 4 +-- v2rayN/v2rayN/Manager/WindowsManager.cs | 12 ++++---- .../v2rayN/Views/AddGroupServerWindow.xaml.cs | 12 ++++---- v2rayN/v2rayN/Views/AddServer2Window.xaml.cs | 8 +++--- v2rayN/v2rayN/Views/AddServerWindow.xaml.cs | 8 +++--- .../v2rayN/Views/BackupAndRestoreView.xaml.cs | 4 +-- v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs | 4 +-- .../Views/FullConfigTemplateWindow.xaml.cs | 4 +-- .../Views/GlobalHotkeySettingWindow.xaml.cs | 6 ++-- v2rayN/v2rayN/Views/MainWindow.xaml.cs | 28 +++++++++---------- v2rayN/v2rayN/Views/MsgView.xaml.cs | 4 +-- .../v2rayN/Views/OptionSettingWindow.xaml.cs | 10 +++---- .../v2rayN/Views/ProfilesSelectWindow.xaml.cs | 2 +- v2rayN/v2rayN/Views/ProfilesView.xaml.cs | 18 ++++++------ .../Views/RoutingRuleDetailsWindow.xaml.cs | 6 ++-- .../Views/RoutingRuleSettingWindow.xaml.cs | 16 +++++------ .../v2rayN/Views/RoutingSettingWindow.xaml.cs | 16 +++++------ v2rayN/v2rayN/Views/StatusBarView.xaml.cs | 4 +-- v2rayN/v2rayN/Views/SubEditWindow.xaml.cs | 6 ++-- v2rayN/v2rayN/Views/SubSettingWindow.xaml.cs | 14 +++++----- 23 files changed, 102 insertions(+), 102 deletions(-) diff --git a/v2rayN/v2rayN/App.xaml.cs b/v2rayN/v2rayN/App.xaml.cs index de9a5370..ca56311b 100644 --- a/v2rayN/v2rayN/App.xaml.cs +++ b/v2rayN/v2rayN/App.xaml.cs @@ -9,7 +9,7 @@ public partial class App : Application public App() { - this.DispatcherUnhandledException += App_DispatcherUnhandledException; + DispatcherUnhandledException += App_DispatcherUnhandledException; AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; } @@ -23,7 +23,7 @@ public partial class App : Application var exePathKey = Utils.GetMd5(Utils.GetExePath()); var rebootas = (e.Args ?? Array.Empty()).Any(t => t == Global.RebootAs); - ProgramStarted = new EventWaitHandle(false, EventResetMode.AutoReset, exePathKey, out bool bCreatedNew); + ProgramStarted = new EventWaitHandle(false, EventResetMode.AutoReset, exePathKey, out var bCreatedNew); if (!rebootas && !bCreatedNew) { ProgramStarted.Set(); diff --git a/v2rayN/v2rayN/Common/UI.cs b/v2rayN/v2rayN/Common/UI.cs index 1d5e860c..4ad19a09 100644 --- a/v2rayN/v2rayN/Common/UI.cs +++ b/v2rayN/v2rayN/Common/UI.cs @@ -1,6 +1,6 @@ using Microsoft.Win32; -namespace v2rayN; +namespace v2rayN.Common; internal class UI { diff --git a/v2rayN/v2rayN/Common/WindowsUtils.cs b/v2rayN/v2rayN/Common/WindowsUtils.cs index 80778268..cc05f56d 100644 --- a/v2rayN/v2rayN/Common/WindowsUtils.cs +++ b/v2rayN/v2rayN/Common/WindowsUtils.cs @@ -3,7 +3,7 @@ using System.Windows.Media; using System.Windows.Media.Imaging; using Microsoft.Win32; -namespace v2rayN; +namespace v2rayN.Common; internal static class WindowsUtils { @@ -40,13 +40,13 @@ internal static class WindowsUtils } [DllImport("dwmapi.dll")] - public static extern int DwmSetWindowAttribute(IntPtr hwnd, DWMWINDOWATTRIBUTE attribute, ref int attributeValue, uint attributeSize); + public static extern int DwmSetWindowAttribute(nint hwnd, DWMWINDOWATTRIBUTE attribute, ref int attributeValue, uint attributeSize); public static ImageSource IconToImageSource(Icon icon) { return Imaging.CreateBitmapSourceFromHIcon( icon.Handle, - new System.Windows.Int32Rect(0, 0, icon.Width, icon.Height), + new Int32Rect(0, 0, icon.Width, icon.Height), BitmapSizeOptions.FromEmptyOptions()); } @@ -65,9 +65,9 @@ internal static class WindowsUtils private static void SetDarkBorder(Window window, bool dark) { // Make sure the handle is created before the window is shown - IntPtr hWnd = new WindowInteropHelper(window).EnsureHandle(); - int attribute = dark ? 1 : 0; - uint attributeSize = (uint)Marshal.SizeOf(attribute); + var hWnd = new WindowInteropHelper(window).EnsureHandle(); + var attribute = dark ? 1 : 0; + var attributeSize = (uint)Marshal.SizeOf(attribute); DwmSetWindowAttribute(hWnd, DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1, ref attribute, attributeSize); DwmSetWindowAttribute(hWnd, DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE, ref attribute, attributeSize); } diff --git a/v2rayN/v2rayN/Manager/HotkeyManager.cs b/v2rayN/v2rayN/Manager/HotkeyManager.cs index dc9eec8e..6b6bc546 100644 --- a/v2rayN/v2rayN/Manager/HotkeyManager.cs +++ b/v2rayN/v2rayN/Manager/HotkeyManager.cs @@ -43,7 +43,7 @@ public sealed class HotkeyManager modifiers |= KeyModifiers.Alt; } - key = key << 16 | (int)modifiers; + key = (key << 16) | (int)modifiers; if (!_hotkeyTriggerDic.ContainsKey(key)) { _hotkeyTriggerDic.Add(key, new() { item.EGlobalHotkey }); @@ -103,7 +103,7 @@ public sealed class HotkeyManager private (int fsModifiers, int vKey, string hotkeyStr, List Names) GetHotkeyInfo(int hotkeyCode) { var fsModifiers = hotkeyCode & 0xffff; - var vKey = hotkeyCode >> 16 & 0xffff; + var vKey = (hotkeyCode >> 16) & 0xffff; var hotkeyStr = new StringBuilder(); var names = new List(); diff --git a/v2rayN/v2rayN/Manager/WindowsManager.cs b/v2rayN/v2rayN/Manager/WindowsManager.cs index c3bb98d7..0b91f2fb 100644 --- a/v2rayN/v2rayN/Manager/WindowsManager.cs +++ b/v2rayN/v2rayN/Manager/WindowsManager.cs @@ -60,18 +60,18 @@ public sealed class WindowsManager return null; } - Color color = ColorTranslator.FromHtml("#3399CC"); - int index = (int)config.SystemProxyItem.SysProxyType; + var color = ColorTranslator.FromHtml("#3399CC"); + var index = (int)config.SystemProxyItem.SysProxyType; if (index > 0) { color = (new[] { Color.Red, Color.Purple, Color.DarkGreen, Color.Orange, Color.DarkSlateBlue, Color.RoyalBlue })[index - 1]; } - int width = 128; - int height = 128; + var width = 128; + var height = 128; Bitmap bitmap = new(width, height); - Graphics graphics = Graphics.FromImage(bitmap); + var graphics = Graphics.FromImage(bitmap); SolidBrush drawBrush = new(color); graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; @@ -79,7 +79,7 @@ public sealed class WindowsManager graphics.DrawImage(new Bitmap(item.CustomIcon), 0, 0, width, height); graphics.FillEllipse(drawBrush, width / 2, width / 2, width / 2, width / 2); - Icon createdIcon = Icon.FromHandle(bitmap.GetHicon()); + var createdIcon = Icon.FromHandle(bitmap.GetHicon()); drawBrush.Dispose(); graphics.Dispose(); diff --git a/v2rayN/v2rayN/Views/AddGroupServerWindow.xaml.cs b/v2rayN/v2rayN/Views/AddGroupServerWindow.xaml.cs index a08d7962..ad3c7ac6 100644 --- a/v2rayN/v2rayN/Views/AddGroupServerWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/AddGroupServerWindow.xaml.cs @@ -6,9 +6,9 @@ public partial class AddGroupServerWindow { InitializeComponent(); - this.Owner = Application.Current.MainWindow; - this.Loaded += Window_Loaded; - this.PreviewKeyDown += AddGroupServerWindow_PreviewKeyDown; + Owner = Application.Current.MainWindow; + Loaded += Window_Loaded; + PreviewKeyDown += AddGroupServerWindow_PreviewKeyDown; lstChild.SelectionChanged += LstChild_SelectionChanged; menuSelectAllChild.Click += MenuSelectAllChild_Click; @@ -27,11 +27,11 @@ public partial class AddGroupServerWindow switch (profileItem.ConfigType) { case EConfigType.PolicyGroup: - this.Title = ResUI.TbConfigTypePolicyGroup; + Title = ResUI.TbConfigTypePolicyGroup; break; case EConfigType.ProxyChain: - this.Title = ResUI.TbConfigTypeProxyChain; + Title = ResUI.TbConfigTypeProxyChain; gridPolicyGroup.Visibility = Visibility.Collapsed; break; } @@ -61,7 +61,7 @@ public partial class AddGroupServerWindow switch (action) { case EViewAction.CloseWindow: - this.DialogResult = true; + DialogResult = true; break; } return await Task.FromResult(true); diff --git a/v2rayN/v2rayN/Views/AddServer2Window.xaml.cs b/v2rayN/v2rayN/Views/AddServer2Window.xaml.cs index 0f429a49..0ea4d085 100644 --- a/v2rayN/v2rayN/Views/AddServer2Window.xaml.cs +++ b/v2rayN/v2rayN/Views/AddServer2Window.xaml.cs @@ -6,8 +6,8 @@ public partial class AddServer2Window { InitializeComponent(); - this.Owner = Application.Current.MainWindow; - this.Loaded += Window_Loaded; + Owner = Application.Current.MainWindow; + Loaded += Window_Loaded; ViewModel = new AddServer2ViewModel(profileItem, UpdateViewHandler); cmbCoreType.ItemsSource = Utils.GetEnumNames().Where(t => t != ECoreType.v2rayN.ToString()).ToList().AppendEmpty(); @@ -32,11 +32,11 @@ public partial class AddServer2Window switch (action) { case EViewAction.CloseWindow: - this.DialogResult = true; + DialogResult = true; break; case EViewAction.BrowseServer: - if (UI.OpenFileDialog(out string fileName, "Config|*.json|YAML|*.yaml;*.yml|All|*.*") != true) + if (UI.OpenFileDialog(out var fileName, "Config|*.json|YAML|*.yaml;*.yml|All|*.*") != true) { return false; } diff --git a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs index 65383c32..ad6b6c9e 100644 --- a/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/AddServerWindow.xaml.cs @@ -8,8 +8,8 @@ public partial class AddServerWindow { InitializeComponent(); - this.Owner = Application.Current.MainWindow; - this.Loaded += Window_Loaded; + Owner = Application.Current.MainWindow; + Loaded += Window_Loaded; cmbNetwork.SelectionChanged += CmbNetwork_SelectionChanged; cmbStreamSecurity.SelectionChanged += CmbStreamSecurity_SelectionChanged; btnGUID.Click += btnGUID_Click; @@ -191,7 +191,7 @@ public partial class AddServerWindow this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); }); - this.Title = $"{profileItem.ConfigType}"; + Title = $"{profileItem.ConfigType}"; WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme); } @@ -200,7 +200,7 @@ public partial class AddServerWindow switch (action) { case EViewAction.CloseWindow: - this.DialogResult = true; + DialogResult = true; break; } return await Task.FromResult(true); diff --git a/v2rayN/v2rayN/Views/BackupAndRestoreView.xaml.cs b/v2rayN/v2rayN/Views/BackupAndRestoreView.xaml.cs index cc5a3610..56bb0b61 100644 --- a/v2rayN/v2rayN/Views/BackupAndRestoreView.xaml.cs +++ b/v2rayN/v2rayN/Views/BackupAndRestoreView.xaml.cs @@ -28,7 +28,7 @@ public partial class BackupAndRestoreView private void MenuLocalBackup_Click(object sender, RoutedEventArgs e) { - if (UI.SaveFileDialog(out string fileName, "Zip|*.zip") != true) + if (UI.SaveFileDialog(out var fileName, "Zip|*.zip") != true) { return; } @@ -37,7 +37,7 @@ public partial class BackupAndRestoreView private void MenuLocalRestore_Click(object sender, RoutedEventArgs e) { - if (UI.OpenFileDialog(out string fileName, "Zip|*.zip|All|*.*") != true) + if (UI.OpenFileDialog(out var fileName, "Zip|*.zip|All|*.*") != true) { return; } diff --git a/v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs b/v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs index 3eabd26f..4242063c 100644 --- a/v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/DNSSettingWindow.xaml.cs @@ -8,7 +8,7 @@ public partial class DNSSettingWindow { InitializeComponent(); - this.Owner = Application.Current.MainWindow; + Owner = Application.Current.MainWindow; _config = AppManager.Instance.Config; ViewModel = new DNSSettingViewModel(UpdateViewHandler); @@ -78,7 +78,7 @@ public partial class DNSSettingWindow switch (action) { case EViewAction.CloseWindow: - this.DialogResult = true; + DialogResult = true; break; } return await Task.FromResult(true); diff --git a/v2rayN/v2rayN/Views/FullConfigTemplateWindow.xaml.cs b/v2rayN/v2rayN/Views/FullConfigTemplateWindow.xaml.cs index e0cd8b14..a9f95a53 100644 --- a/v2rayN/v2rayN/Views/FullConfigTemplateWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/FullConfigTemplateWindow.xaml.cs @@ -8,7 +8,7 @@ public partial class FullConfigTemplateWindow { InitializeComponent(); - this.Owner = Application.Current.MainWindow; + Owner = Application.Current.MainWindow; _config = AppManager.Instance.Config; ViewModel = new FullConfigTemplateViewModel(UpdateViewHandler); @@ -35,7 +35,7 @@ public partial class FullConfigTemplateWindow switch (action) { case EViewAction.CloseWindow: - this.DialogResult = true; + DialogResult = true; break; } return await Task.FromResult(true); diff --git a/v2rayN/v2rayN/Views/GlobalHotkeySettingWindow.xaml.cs b/v2rayN/v2rayN/Views/GlobalHotkeySettingWindow.xaml.cs index f71fe7cc..f49fe80f 100644 --- a/v2rayN/v2rayN/Views/GlobalHotkeySettingWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/GlobalHotkeySettingWindow.xaml.cs @@ -11,14 +11,14 @@ public partial class GlobalHotkeySettingWindow { InitializeComponent(); - this.Owner = Application.Current.MainWindow; + Owner = Application.Current.MainWindow; ViewModel = new GlobalHotkeySettingViewModel(UpdateViewHandler); btnReset.Click += btnReset_Click; HotkeyManager.Instance.IsPause = true; - this.Closing += (s, e) => HotkeyManager.Instance.IsPause = false; + Closing += (s, e) => HotkeyManager.Instance.IsPause = false; this.WhenActivated(disposables => { @@ -35,7 +35,7 @@ public partial class GlobalHotkeySettingWindow switch (action) { case EViewAction.CloseWindow: - this.DialogResult = true; + DialogResult = true; break; } return await Task.FromResult(true); diff --git a/v2rayN/v2rayN/Views/MainWindow.xaml.cs b/v2rayN/v2rayN/Views/MainWindow.xaml.cs index a9114813..f6bab699 100644 --- a/v2rayN/v2rayN/Views/MainWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/MainWindow.xaml.cs @@ -19,8 +19,8 @@ public partial class MainWindow ThreadPool.RegisterWaitForSingleObject(App.ProgramStarted, OnProgramStarted, null, -1, false); App.Current.SessionEnding += Current_SessionEnding; - this.Closing += MainWindow_Closing; - this.PreviewKeyDown += MainWindow_PreviewKeyDown; + Closing += MainWindow_Closing; + PreviewKeyDown += MainWindow_PreviewKeyDown; menuSettingsSetUWP.Click += menuSettingsSetUWP_Click; menuPromotion.Click += menuPromotion_Click; menuClose.Click += menuClose_Click; @@ -150,10 +150,10 @@ public partial class MainWindow .DisposeWith(disposables); }); - this.Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}"; + Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}"; if (_config.UiItem.AutoHideStartup) { - this.WindowState = WindowState.Minimized; + WindowState = WindowState.Minimized; } if (!_config.GuiItem.EnableHWA) @@ -187,35 +187,35 @@ public partial class MainWindow case EViewAction.AddServerWindow: if (obj is null) return false; - return (new AddServerWindow((ProfileItem)obj)).ShowDialog() ?? false; + return new AddServerWindow((ProfileItem)obj).ShowDialog() ?? false; case EViewAction.AddServer2Window: if (obj is null) return false; - return (new AddServer2Window((ProfileItem)obj)).ShowDialog() ?? false; + return new AddServer2Window((ProfileItem)obj).ShowDialog() ?? false; case EViewAction.AddGroupServerWindow: if (obj is null) return false; - return (new AddGroupServerWindow((ProfileItem)obj)).ShowDialog() ?? false; + return new AddGroupServerWindow((ProfileItem)obj).ShowDialog() ?? false; case EViewAction.DNSSettingWindow: - return (new DNSSettingWindow().ShowDialog() ?? false); + return new DNSSettingWindow().ShowDialog() ?? false; case EViewAction.RoutingSettingWindow: - return (new RoutingSettingWindow().ShowDialog() ?? false); + return new RoutingSettingWindow().ShowDialog() ?? false; case EViewAction.OptionSettingWindow: - return (new OptionSettingWindow().ShowDialog() ?? false); + return new OptionSettingWindow().ShowDialog() ?? false; case EViewAction.FullConfigTemplateWindow: - return (new FullConfigTemplateWindow().ShowDialog() ?? false); + return new FullConfigTemplateWindow().ShowDialog() ?? false; case EViewAction.GlobalHotkeySettingWindow: - return (new GlobalHotkeySettingWindow().ShowDialog() ?? false); + return new GlobalHotkeySettingWindow().ShowDialog() ?? false; case EViewAction.SubSettingWindow: - return (new SubSettingWindow().ShowDialog() ?? false); + return new SubSettingWindow().ShowDialog() ?? false; case EViewAction.ScanScreenTask: await ScanScreenTaskAsync(); @@ -372,7 +372,7 @@ public partial class MainWindow this?.Show(); if (this?.WindowState == WindowState.Minimized) { - this.WindowState = WindowState.Normal; + WindowState = WindowState.Normal; } this?.Activate(); this?.Focus(); diff --git a/v2rayN/v2rayN/Views/MsgView.xaml.cs b/v2rayN/v2rayN/Views/MsgView.xaml.cs index 61fbe469..683e0787 100644 --- a/v2rayN/v2rayN/Views/MsgView.xaml.cs +++ b/v2rayN/v2rayN/Views/MsgView.xaml.cs @@ -31,10 +31,10 @@ public partial class MsgView case EViewAction.DispatcherShowMsg: if (obj is null) return false; - Application.Current?.Dispatcher.Invoke((() => + Application.Current?.Dispatcher.Invoke(() => { ShowMsg(obj); - }), DispatcherPriority.ApplicationIdle); + }, DispatcherPriority.ApplicationIdle); break; } return await Task.FromResult(true); diff --git a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs index 70d27894..409db086 100644 --- a/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/OptionSettingWindow.xaml.cs @@ -10,7 +10,7 @@ public partial class OptionSettingWindow { InitializeComponent(); - this.Owner = Application.Current.MainWindow; + Owner = Application.Current.MainWindow; _config = AppManager.Instance.Config; ViewModel = new OptionSettingViewModel(UpdateViewHandler); @@ -136,7 +136,7 @@ public partial class OptionSettingWindow switch (action) { case EViewAction.CloseWindow: - this.DialogResult = true; + DialogResult = true; break; case EViewAction.InitSettingFont: @@ -168,12 +168,12 @@ public partial class OptionSettingWindow foreach (var ttf in files) { var families = Fonts.GetFontFamilies(Utils.GetFontsPath(ttf)); - foreach (FontFamily family in families) + foreach (var family in families) { var typefaces = family.GetTypefaces(); - foreach (Typeface typeface in typefaces) + foreach (var typeface in typefaces) { - typeface.TryGetGlyphTypeface(out GlyphTypeface glyph); + typeface.TryGetGlyphTypeface(out var glyph); //var fontFace = glyph.Win32FaceNames[new CultureInfo("en-us")]; //if (!fontFace.Equals("Regular") && !fontFace.Equals("Normal")) //{ diff --git a/v2rayN/v2rayN/Views/ProfilesSelectWindow.xaml.cs b/v2rayN/v2rayN/Views/ProfilesSelectWindow.xaml.cs index 1b8419a0..e357044c 100644 --- a/v2rayN/v2rayN/Views/ProfilesSelectWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/ProfilesSelectWindow.xaml.cs @@ -71,7 +71,7 @@ public partial class ProfilesSelectWindow switch (action) { case EViewAction.CloseWindow: - this.DialogResult = true; + DialogResult = true; break; } return await Task.FromResult(true); diff --git a/v2rayN/v2rayN/Views/ProfilesView.xaml.cs b/v2rayN/v2rayN/Views/ProfilesView.xaml.cs index ac79a8c8..59e42b7b 100644 --- a/v2rayN/v2rayN/Views/ProfilesView.xaml.cs +++ b/v2rayN/v2rayN/Views/ProfilesView.xaml.cs @@ -127,7 +127,7 @@ public partial class ProfilesView case EViewAction.SaveFileDialog: if (obj is null) return false; - if (UI.SaveFileDialog(out string fileName, "Config|*.json") != true) + if (UI.SaveFileDialog(out var fileName, "Config|*.json") != true) { return false; } @@ -137,17 +137,17 @@ public partial class ProfilesView case EViewAction.AddServerWindow: if (obj is null) return false; - return (new AddServerWindow((ProfileItem)obj)).ShowDialog() ?? false; + return new AddServerWindow((ProfileItem)obj).ShowDialog() ?? false; case EViewAction.AddServer2Window: if (obj is null) return false; - return (new AddServer2Window((ProfileItem)obj)).ShowDialog() ?? false; + return new AddServer2Window((ProfileItem)obj).ShowDialog() ?? false; case EViewAction.AddGroupServerWindow: if (obj is null) return false; - return (new AddGroupServerWindow((ProfileItem)obj)).ShowDialog() ?? false; + return new AddGroupServerWindow((ProfileItem)obj).ShowDialog() ?? false; case EViewAction.ShareServer: if (obj is null) @@ -158,7 +158,7 @@ public partial class ProfilesView case EViewAction.SubEditWindow: if (obj is null) return false; - return (new SubEditWindow((SubItem)obj)).ShowDialog() ?? false; + return new SubEditWindow((SubItem)obj).ShowDialog() ?? false; case EViewAction.DispatcherRefreshServersBiz: Application.Current?.Dispatcher.Invoke(RefreshServersBiz, DispatcherPriority.Normal); @@ -415,8 +415,8 @@ public partial class ProfilesView private void LstProfiles_MouseMove(object sender, MouseEventArgs e) { // Get the current mouse position - Point mousePos = e.GetPosition(null); - Vector diff = startPoint - mousePos; + var mousePos = e.GetPosition(null); + var diff = startPoint - mousePos; if (e.LeftButton == MouseButtonState.Pressed && (Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance || @@ -429,7 +429,7 @@ public partial class ProfilesView if (listViewItem == null) return; // Abort // Find the data behind the ListViewItem - ProfileItemModel item = (ProfileItemModel)listView.ItemContainerGenerator.ItemFromContainer(listViewItem); + var item = (ProfileItemModel)listView.ItemContainerGenerator.ItemFromContainer(listViewItem); if (item == null) return; // Abort // Initialize the drag & drop operation @@ -462,7 +462,7 @@ public partial class ProfilesView return; } // Find the data behind the Item - ProfileItemModel item = (ProfileItemModel)listView.ItemContainerGenerator.ItemFromContainer(listViewItem); + var item = (ProfileItemModel)listView.ItemContainerGenerator.ItemFromContainer(listViewItem); if (item == null) return; // Move item into observable collection diff --git a/v2rayN/v2rayN/Views/RoutingRuleDetailsWindow.xaml.cs b/v2rayN/v2rayN/Views/RoutingRuleDetailsWindow.xaml.cs index 0586067a..e824d324 100644 --- a/v2rayN/v2rayN/Views/RoutingRuleDetailsWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/RoutingRuleDetailsWindow.xaml.cs @@ -6,8 +6,8 @@ public partial class RoutingRuleDetailsWindow { InitializeComponent(); - this.Owner = Application.Current.MainWindow; - this.Loaded += Window_Loaded; + Owner = Application.Current.MainWindow; + Loaded += Window_Loaded; clbProtocol.SelectionChanged += ClbProtocol_SelectionChanged; clbInboundTag.SelectionChanged += ClbInboundTag_SelectionChanged; @@ -54,7 +54,7 @@ public partial class RoutingRuleDetailsWindow switch (action) { case EViewAction.CloseWindow: - this.DialogResult = true; + DialogResult = true; break; } return await Task.FromResult(true); diff --git a/v2rayN/v2rayN/Views/RoutingRuleSettingWindow.xaml.cs b/v2rayN/v2rayN/Views/RoutingRuleSettingWindow.xaml.cs index 153aaedc..b72dfc37 100644 --- a/v2rayN/v2rayN/Views/RoutingRuleSettingWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/RoutingRuleSettingWindow.xaml.cs @@ -6,9 +6,9 @@ public partial class RoutingRuleSettingWindow { InitializeComponent(); - this.Owner = Application.Current.MainWindow; - this.Loaded += Window_Loaded; - this.PreviewKeyDown += RoutingRuleSettingWindow_PreviewKeyDown; + Owner = Application.Current.MainWindow; + Loaded += Window_Loaded; + PreviewKeyDown += RoutingRuleSettingWindow_PreviewKeyDown; lstRules.SelectionChanged += lstRules_SelectionChanged; lstRules.MouseDoubleClick += LstRules_MouseDoubleClick; menuRuleSelectAll.Click += menuRuleSelectAll_Click; @@ -57,7 +57,7 @@ public partial class RoutingRuleSettingWindow switch (action) { case EViewAction.CloseWindow: - this.DialogResult = true; + DialogResult = true; break; case EViewAction.ShowYesNo: @@ -80,11 +80,11 @@ public partial class RoutingRuleSettingWindow if (obj is null) return false; - return (new RoutingRuleDetailsWindow((RulesItem)obj)).ShowDialog() ?? false; + return new RoutingRuleDetailsWindow((RulesItem)obj).ShowDialog() ?? false; case EViewAction.ImportRulesFromFile: - if (UI.OpenFileDialog(out string fileName, "Rules|*.json|All|*.*") != true) + if (UI.OpenFileDialog(out var fileName, "Rules|*.json|All|*.*") != true) { return false; } @@ -174,7 +174,7 @@ public partial class RoutingRuleSettingWindow private void btnBrowseCustomIcon_Click(object sender, System.Windows.RoutedEventArgs e) { - if (UI.OpenFileDialog(out string fileName, + if (UI.OpenFileDialog(out var fileName, "PNG,ICO|*.png;*.ico") != true) { return; @@ -185,7 +185,7 @@ public partial class RoutingRuleSettingWindow private void btnBrowseCustomRulesetPath4Singbox_Click(object sender, RoutedEventArgs e) { - if (UI.OpenFileDialog(out string fileName, + if (UI.OpenFileDialog(out var fileName, "Config|*.json|All|*.*") != true) { return; diff --git a/v2rayN/v2rayN/Views/RoutingSettingWindow.xaml.cs b/v2rayN/v2rayN/Views/RoutingSettingWindow.xaml.cs index 565897b0..588ea228 100644 --- a/v2rayN/v2rayN/Views/RoutingSettingWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/RoutingSettingWindow.xaml.cs @@ -6,9 +6,9 @@ public partial class RoutingSettingWindow { InitializeComponent(); - this.Owner = Application.Current.MainWindow; - this.Closing += RoutingSettingWindow_Closing; - this.PreviewKeyDown += RoutingSettingWindow_PreviewKeyDown; + Owner = Application.Current.MainWindow; + Closing += RoutingSettingWindow_Closing; + PreviewKeyDown += RoutingSettingWindow_PreviewKeyDown; lstRoutings.SelectionChanged += lstRoutings_SelectionChanged; lstRoutings.MouseDoubleClick += LstRoutings_MouseDoubleClick; menuRoutingAdvancedSelectAll.Click += menuRoutingAdvancedSelectAll_Click; @@ -44,7 +44,7 @@ public partial class RoutingSettingWindow switch (action) { case EViewAction.CloseWindow: - this.DialogResult = true; + DialogResult = true; break; case EViewAction.ShowYesNo: @@ -58,7 +58,7 @@ public partial class RoutingSettingWindow if (obj is null) return false; - return (new RoutingRuleSettingWindow((RoutingItem)obj)).ShowDialog() ?? false; + return new RoutingRuleSettingWindow((RoutingItem)obj).ShowDialog() ?? false; } return await Task.FromResult(true); } @@ -67,7 +67,7 @@ public partial class RoutingSettingWindow { if (ViewModel?.IsModified == true) { - this.DialogResult = true; + DialogResult = true; } } @@ -122,11 +122,11 @@ public partial class RoutingSettingWindow { if (ViewModel?.IsModified == true) { - this.DialogResult = true; + DialogResult = true; } else { - this.Close(); + Close(); } } } diff --git a/v2rayN/v2rayN/Views/StatusBarView.xaml.cs b/v2rayN/v2rayN/Views/StatusBarView.xaml.cs index 906308ab..47cf90bf 100644 --- a/v2rayN/v2rayN/Views/StatusBarView.xaml.cs +++ b/v2rayN/v2rayN/Views/StatusBarView.xaml.cs @@ -71,11 +71,11 @@ public partial class StatusBarView switch (action) { case EViewAction.DispatcherRefreshIcon: - Application.Current?.Dispatcher.Invoke((async () => + Application.Current?.Dispatcher.Invoke(async () => { tbNotify.Icon = await WindowsManager.Instance.GetNotifyIcon(_config); Application.Current.MainWindow.Icon = WindowsManager.Instance.GetAppIcon(_config); - }), DispatcherPriority.Normal); + }, DispatcherPriority.Normal); break; case EViewAction.SetClipboardData: diff --git a/v2rayN/v2rayN/Views/SubEditWindow.xaml.cs b/v2rayN/v2rayN/Views/SubEditWindow.xaml.cs index 488420fa..d1451a9c 100644 --- a/v2rayN/v2rayN/Views/SubEditWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/SubEditWindow.xaml.cs @@ -6,8 +6,8 @@ public partial class SubEditWindow { InitializeComponent(); - this.Owner = Application.Current.MainWindow; - this.Loaded += Window_Loaded; + Owner = Application.Current.MainWindow; + Loaded += Window_Loaded; ViewModel = new SubEditViewModel(subItem, UpdateViewHandler); @@ -39,7 +39,7 @@ public partial class SubEditWindow switch (action) { case EViewAction.CloseWindow: - this.DialogResult = true; + DialogResult = true; break; } return await Task.FromResult(true); diff --git a/v2rayN/v2rayN/Views/SubSettingWindow.xaml.cs b/v2rayN/v2rayN/Views/SubSettingWindow.xaml.cs index 14b3aa38..2e4ffe8f 100644 --- a/v2rayN/v2rayN/Views/SubSettingWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/SubSettingWindow.xaml.cs @@ -8,10 +8,10 @@ public partial class SubSettingWindow { InitializeComponent(); - this.Owner = Application.Current.MainWindow; + Owner = Application.Current.MainWindow; ViewModel = new SubSettingViewModel(UpdateViewHandler); - this.Closing += SubSettingWindow_Closing; + Closing += SubSettingWindow_Closing; lstSubscription.MouseDoubleClick += LstSubscription_MouseDoubleClick; lstSubscription.SelectionChanged += LstSubscription_SelectionChanged; menuClose.Click += menuClose_Click; @@ -34,7 +34,7 @@ public partial class SubSettingWindow switch (action) { case EViewAction.CloseWindow: - this.DialogResult = true; + DialogResult = true; break; case EViewAction.ShowYesNo: @@ -47,7 +47,7 @@ public partial class SubSettingWindow case EViewAction.SubEditWindow: if (obj is null) return false; - return (new SubEditWindow((SubItem)obj)).ShowDialog() ?? false; + return new SubEditWindow((SubItem)obj).ShowDialog() ?? false; case EViewAction.ShareSub: if (obj is null) @@ -78,7 +78,7 @@ public partial class SubSettingWindow { if (ViewModel?.IsModified == true) { - this.DialogResult = true; + DialogResult = true; } } @@ -99,11 +99,11 @@ public partial class SubSettingWindow { if (ViewModel?.IsModified == true) { - this.DialogResult = true; + DialogResult = true; } else { - this.Close(); + Close(); } } } From d727ff40bb8bf0792248326b161943af294ad57a Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 31 Oct 2025 20:25:54 +0800 Subject: [PATCH 104/132] Code clean --- v2rayN/v2rayN.Desktop/GlobalUsings.cs | 6 ++--- .../Views/AddGroupServerWindow.axaml.cs | 12 +++++----- .../Views/AddServer2Window.axaml.cs | 6 ++--- .../Views/AddServerWindow.axaml.cs | 8 +++---- .../Views/ClashProxiesView.axaml.cs | 2 +- .../Views/DNSSettingWindow.axaml.cs | 4 ++-- .../Views/FullConfigTemplateWindow.axaml.cs | 4 ++-- .../Views/GlobalHotkeySettingWindow.axaml.cs | 6 ++--- .../v2rayN.Desktop/Views/MainWindow.axaml.cs | 24 +++++++++---------- .../Views/OptionSettingWindow.axaml.cs | 4 ++-- .../Views/RoutingRuleDetailsWindow.axaml.cs | 6 ++--- .../Views/RoutingRuleSettingWindow.axaml.cs | 8 +++---- .../Views/RoutingSettingWindow.axaml.cs | 10 ++++---- .../Views/SubEditWindow.axaml.cs | 4 ++-- .../Views/SubSettingWindow.axaml.cs | 8 +++---- .../Views/SudoPasswordInputView.axaml.cs | 2 +- 16 files changed, 57 insertions(+), 57 deletions(-) diff --git a/v2rayN/v2rayN.Desktop/GlobalUsings.cs b/v2rayN/v2rayN.Desktop/GlobalUsings.cs index 6a2f1d3b..e7499b01 100644 --- a/v2rayN/v2rayN.Desktop/GlobalUsings.cs +++ b/v2rayN/v2rayN.Desktop/GlobalUsings.cs @@ -17,13 +17,13 @@ global using Avalonia.Markup.Xaml; global using Avalonia.Media; global using Avalonia.Media.Imaging; global using Avalonia.Platform; -global using ReactiveUI.Avalonia; global using Avalonia.Styling; global using Avalonia.Threading; -global using ReactiveUI; -global using ReactiveUI.Fody.Helpers; global using DynamicData; global using MsBox.Avalonia.Enums; +global using ReactiveUI; +global using ReactiveUI.Avalonia; +global using ReactiveUI.Fody.Helpers; global using ServiceLib; global using ServiceLib.Base; global using ServiceLib.Common; diff --git a/v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml.cs index 0430a57c..a75f0e43 100644 --- a/v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml.cs @@ -13,8 +13,8 @@ public partial class AddGroupServerWindow : WindowBase { InitializeComponent(); - this.Loaded += Window_Loaded; - btnCancel.Click += (s, e) => this.Close(); + Loaded += Window_Loaded; + btnCancel.Click += (s, e) => Close(); lstChild.SelectionChanged += LstChild_SelectionChanged; ViewModel = new AddGroupServerViewModel(profileItem, UpdateViewHandler); @@ -32,11 +32,11 @@ public partial class AddGroupServerWindow : WindowBase switch (profileItem.ConfigType) { case EConfigType.PolicyGroup: - this.Title = ResUI.TbConfigTypePolicyGroup; + Title = ResUI.TbConfigTypePolicyGroup; break; case EConfigType.ProxyChain: - this.Title = ResUI.TbConfigTypeProxyChain; + Title = ResUI.TbConfigTypeProxyChain; gridPolicyGroup.IsVisible = false; break; } @@ -64,7 +64,7 @@ public partial class AddGroupServerWindow : WindowBase menuSelectAllChild.Click += (s, e) => lstChild.SelectAll(); // Keyboard shortcuts when focus is within grid - this.AddHandler(KeyDownEvent, AddGroupServerWindow_KeyDown, RoutingStrategies.Tunnel); + AddHandler(KeyDownEvent, AddGroupServerWindow_KeyDown, RoutingStrategies.Tunnel); lstChild.LoadingRow += LstChild_LoadingRow; } @@ -78,7 +78,7 @@ public partial class AddGroupServerWindow : WindowBase switch (action) { case EViewAction.CloseWindow: - this.Close(true); + Close(true); break; } return await Task.FromResult(true); diff --git a/v2rayN/v2rayN.Desktop/Views/AddServer2Window.axaml.cs b/v2rayN/v2rayN.Desktop/Views/AddServer2Window.axaml.cs index 2e6731b7..f1624a6b 100644 --- a/v2rayN/v2rayN.Desktop/Views/AddServer2Window.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/AddServer2Window.axaml.cs @@ -14,8 +14,8 @@ public partial class AddServer2Window : WindowBase { InitializeComponent(); - this.Loaded += Window_Loaded; - btnCancel.Click += (s, e) => this.Close(); + Loaded += Window_Loaded; + btnCancel.Click += (s, e) => Close(); ViewModel = new AddServer2ViewModel(profileItem, UpdateViewHandler); cmbCoreType.ItemsSource = Utils.GetEnumNames().Where(t => t != ECoreType.v2rayN.ToString()).ToList().AppendEmpty(); @@ -39,7 +39,7 @@ public partial class AddServer2Window : WindowBase switch (action) { case EViewAction.CloseWindow: - this.Close(true); + Close(true); break; case EViewAction.BrowseServer: diff --git a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs index 255ace75..5acf6915 100644 --- a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml.cs @@ -13,8 +13,8 @@ public partial class AddServerWindow : WindowBase { InitializeComponent(); - this.Loaded += Window_Loaded; - btnCancel.Click += (s, e) => this.Close(); + Loaded += Window_Loaded; + btnCancel.Click += (s, e) => Close(); cmbNetwork.SelectionChanged += CmbNetwork_SelectionChanged; cmbStreamSecurity.SelectionChanged += CmbStreamSecurity_SelectionChanged; btnGUID.Click += btnGUID_Click; @@ -196,7 +196,7 @@ public partial class AddServerWindow : WindowBase this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); }); - this.Title = $"{profileItem.ConfigType}"; + Title = $"{profileItem.ConfigType}"; } private async Task UpdateViewHandler(EViewAction action, object? obj) @@ -204,7 +204,7 @@ public partial class AddServerWindow : WindowBase switch (action) { case EViewAction.CloseWindow: - this.Close(true); + Close(true); break; } return await Task.FromResult(true); diff --git a/v2rayN/v2rayN.Desktop/Views/ClashProxiesView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/ClashProxiesView.axaml.cs index 9a86846b..42068af4 100644 --- a/v2rayN/v2rayN.Desktop/Views/ClashProxiesView.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/ClashProxiesView.axaml.cs @@ -7,7 +7,7 @@ public partial class ClashProxiesView : ReactiveUserControl { diff --git a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs index a37f2c14..e673a025 100644 --- a/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/DNSSettingWindow.axaml.cs @@ -12,7 +12,7 @@ public partial class DNSSettingWindow : WindowBase _config = AppManager.Instance.Config; Loaded += Window_Loaded; - btnCancel.Click += (s, e) => this.Close(); + btnCancel.Click += (s, e) => Close(); ViewModel = new DNSSettingViewModel(UpdateViewHandler); cmbRayFreedomDNSStrategy.ItemsSource = Global.DomainStrategy4Freedoms; @@ -77,7 +77,7 @@ public partial class DNSSettingWindow : WindowBase switch (action) { case EViewAction.CloseWindow: - this.Close(true); + Close(true); break; } return await Task.FromResult(true); diff --git a/v2rayN/v2rayN.Desktop/Views/FullConfigTemplateWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/FullConfigTemplateWindow.axaml.cs index 1a8d5061..bfe0c2c5 100644 --- a/v2rayN/v2rayN.Desktop/Views/FullConfigTemplateWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/FullConfigTemplateWindow.axaml.cs @@ -12,7 +12,7 @@ public partial class FullConfigTemplateWindow : WindowBase this.Close(); + btnCancel.Click += (s, e) => Close(); ViewModel = new FullConfigTemplateViewModel(UpdateViewHandler); this.WhenActivated(disposables => @@ -36,7 +36,7 @@ public partial class FullConfigTemplateWindow : WindowBase HotkeyManager.Instance.IsPause = false; - btnCancel.Click += (s, e) => this.Close(); + Closing += (s, e) => HotkeyManager.Instance.IsPause = false; + btnCancel.Click += (s, e) => Close(); this.WhenActivated(disposables => { @@ -34,7 +34,7 @@ public partial class GlobalHotkeySettingWindow : WindowBase _config = AppManager.Instance.Config; _manager = new WindowNotificationManager(TopLevel.GetTopLevel(this)) { MaxItems = 3, Position = NotificationPosition.TopRight }; - this.KeyDown += MainWindow_KeyDown; + KeyDown += MainWindow_KeyDown; menuSettingsSetUWP.Click += menuSettingsSetUWP_Click; menuPromotion.Click += menuPromotion_Click; menuCheckUpdate.Click += MenuCheckUpdate_Click; @@ -153,14 +153,14 @@ public partial class MainWindow : WindowBase if (Utils.IsWindows()) { - this.Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}"; + Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}"; ThreadPool.RegisterWaitForSingleObject(Program.ProgramStarted, OnProgramStarted, null, -1, false); HotkeyManager.Instance.Init(_config, OnHotkeyHandler); } else { - this.Title = $"{Utils.GetVersion()}"; + Title = $"{Utils.GetVersion()}"; menuRebootAsAdmin.IsVisible = false; menuSettingsSetUWP.IsVisible = false; @@ -170,7 +170,7 @@ public partial class MainWindow : WindowBase if (_config.UiItem.AutoHideStartup && Utils.IsWindows()) { - this.WindowState = WindowState.Minimized; + WindowState = WindowState.Minimized; } AddHelpMenuItem(); @@ -407,27 +407,27 @@ public partial class MainWindow : WindowBase : !_config.UiItem.ShowInTaskbar); if (bl) { - this.Show(); - if (this.WindowState == WindowState.Minimized) + Show(); + if (WindowState == WindowState.Minimized) { - this.WindowState = WindowState.Normal; + WindowState = WindowState.Normal; } - this.Activate(); - this.Focus(); + Activate(); + Focus(); } else { if (Utils.IsLinux() && _config.UiItem.Hide2TrayWhenClose == false) { - this.WindowState = WindowState.Minimized; + WindowState = WindowState.Minimized; return; } - foreach (var ownedWindow in this.OwnedWindows) + foreach (var ownedWindow in OwnedWindows) { ownedWindow.Close(); } - this.Hide(); + Hide(); } _config.UiItem.ShowInTaskbar = bl; diff --git a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs index bfebcddd..dd918007 100644 --- a/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/OptionSettingWindow.axaml.cs @@ -11,7 +11,7 @@ public partial class OptionSettingWindow : WindowBase InitializeComponent(); Loaded += Window_Loaded; - btnCancel.Click += (s, e) => this.Close(); + btnCancel.Click += (s, e) => Close(); _config = AppManager.Instance.Config; ViewModel = new OptionSettingViewModel(UpdateViewHandler); @@ -153,7 +153,7 @@ public partial class OptionSettingWindow : WindowBase switch (action) { case EViewAction.CloseWindow: - this.Close(true); + Close(true); break; case EViewAction.InitSettingFont: diff --git a/v2rayN/v2rayN.Desktop/Views/RoutingRuleDetailsWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/RoutingRuleDetailsWindow.axaml.cs index 6c3f663d..e2979244 100644 --- a/v2rayN/v2rayN.Desktop/Views/RoutingRuleDetailsWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/RoutingRuleDetailsWindow.axaml.cs @@ -13,8 +13,8 @@ public partial class RoutingRuleDetailsWindow : WindowBase this.Close(); + Loaded += Window_Loaded; + btnCancel.Click += (s, e) => Close(); clbProtocol.SelectionChanged += ClbProtocol_SelectionChanged; clbInboundTag.SelectionChanged += ClbInboundTag_SelectionChanged; @@ -61,7 +61,7 @@ public partial class RoutingRuleDetailsWindow : WindowBase this.Close(); - this.KeyDown += RoutingRuleSettingWindow_KeyDown; + Loaded += Window_Loaded; + btnCancel.Click += (s, e) => Close(); + KeyDown += RoutingRuleSettingWindow_KeyDown; lstRules.SelectionChanged += lstRules_SelectionChanged; lstRules.DoubleTapped += LstRules_DoubleTapped; menuRuleSelectAll.Click += menuRuleSelectAll_Click; @@ -64,7 +64,7 @@ public partial class RoutingRuleSettingWindow : WindowBase InitializeComponent(); Loaded += Window_Loaded; - this.Closing += RoutingSettingWindow_Closing; - btnCancel.Click += (s, e) => this.Close(); - this.KeyDown += RoutingSettingWindow_KeyDown; + Closing += RoutingSettingWindow_Closing; + btnCancel.Click += (s, e) => Close(); + KeyDown += RoutingSettingWindow_KeyDown; lstRoutings.SelectionChanged += lstRoutings_SelectionChanged; lstRoutings.DoubleTapped += LstRoutings_DoubleTapped; menuRoutingAdvancedSelectAll.Click += menuRoutingAdvancedSelectAll_Click; @@ -48,7 +48,7 @@ public partial class RoutingSettingWindow : WindowBase switch (action) { case EViewAction.CloseWindow: - this.Close(true); + Close(true); break; case EViewAction.ShowYesNo: @@ -116,7 +116,7 @@ public partial class RoutingSettingWindow : WindowBase private void btnCancel_Click(object? sender, RoutedEventArgs e) { _manualClose = true; - this.Close(ViewModel?.IsModified); + Close(ViewModel?.IsModified); } private void RoutingSettingWindow_Closing(object? sender, WindowClosingEventArgs e) diff --git a/v2rayN/v2rayN.Desktop/Views/SubEditWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/SubEditWindow.axaml.cs index a25dcd28..9d749ba8 100644 --- a/v2rayN/v2rayN.Desktop/Views/SubEditWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/SubEditWindow.axaml.cs @@ -14,7 +14,7 @@ public partial class SubEditWindow : WindowBase InitializeComponent(); Loaded += Window_Loaded; - btnCancel.Click += (s, e) => this.Close(); + btnCancel.Click += (s, e) => Close(); ViewModel = new SubEditViewModel(subItem, UpdateViewHandler); @@ -45,7 +45,7 @@ public partial class SubEditWindow : WindowBase switch (action) { case EViewAction.CloseWindow: - this.Close(true); + Close(true); break; } return await Task.FromResult(true); diff --git a/v2rayN/v2rayN.Desktop/Views/SubSettingWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/SubSettingWindow.axaml.cs index cf0f5d75..970f5c5b 100644 --- a/v2rayN/v2rayN.Desktop/Views/SubSettingWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/SubSettingWindow.axaml.cs @@ -14,8 +14,8 @@ public partial class SubSettingWindow : WindowBase menuClose.Click += menuClose_Click; Loaded += Window_Loaded; - this.Closing += SubSettingWindow_Closing; - this.KeyDown += SubSettingWindow_KeyDown; + Closing += SubSettingWindow_Closing; + KeyDown += SubSettingWindow_KeyDown; ViewModel = new SubSettingViewModel(UpdateViewHandler); lstSubscription.DoubleTapped += LstSubscription_DoubleTapped; lstSubscription.SelectionChanged += LstSubscription_SelectionChanged; @@ -37,7 +37,7 @@ public partial class SubSettingWindow : WindowBase switch (action) { case EViewAction.CloseWindow: - this.Close(); + Close(); break; case EViewAction.ShowYesNo: @@ -89,7 +89,7 @@ public partial class SubSettingWindow : WindowBase private void menuClose_Click(object? sender, RoutedEventArgs e) { _manualClose = true; - this.Close(ViewModel?.IsModified); + Close(ViewModel?.IsModified); } private void SubSettingWindow_Closing(object? sender, WindowClosingEventArgs e) diff --git a/v2rayN/v2rayN.Desktop/Views/SudoPasswordInputView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/SudoPasswordInputView.axaml.cs index cb5182eb..6373c683 100644 --- a/v2rayN/v2rayN.Desktop/Views/SudoPasswordInputView.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/SudoPasswordInputView.axaml.cs @@ -9,7 +9,7 @@ public partial class SudoPasswordInputView : UserControl { InitializeComponent(); - this.Loaded += (s, e) => txtPassword.Focus(); + Loaded += (s, e) => txtPassword.Focus(); btnSave.Click += async (_, _) => await SavePasswordAsync(); From 7b5686cd8f56f07e96250fdec03bfa5f35acbc51 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sat, 1 Nov 2025 21:13:39 +0800 Subject: [PATCH 105/132] In the policy group, automatically add filtered configurations from the subscription group. https://github.com/2dust/v2rayN/issues/8214 --- .../Manager/ActionPrecheckManager.cs | 8 +++- .../Manager/ProfileGroupItemManager.cs | 34 +++++++++++++-- v2rayN/ServiceLib/Models/ProfileGroupItem.cs | 9 ++++ v2rayN/ServiceLib/Resx/ResUI.Designer.cs | 11 ++++- v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx | 3 ++ v2rayN/ServiceLib/Resx/ResUI.fr.resx | 5 ++- v2rayN/ServiceLib/Resx/ResUI.hu.resx | 3 ++ v2rayN/ServiceLib/Resx/ResUI.resx | 3 ++ v2rayN/ServiceLib/Resx/ResUI.ru.resx | 3 ++ v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx | 3 ++ v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx | 3 ++ .../ViewModels/AddGroupServerViewModel.cs | 32 +++++++++++---- .../Views/AddGroupServerWindow.axaml | 36 +++++++++++++++- .../Views/AddGroupServerWindow.axaml.cs | 3 ++ v2rayN/v2rayN/Views/AddGroupServerWindow.xaml | 41 +++++++++++++++++++ .../v2rayN/Views/AddGroupServerWindow.xaml.cs | 3 ++ 16 files changed, 184 insertions(+), 16 deletions(-) diff --git a/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs b/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs index a487401c..f4f998b3 100644 --- a/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs +++ b/v2rayN/ServiceLib/Manager/ActionPrecheckManager.cs @@ -115,7 +115,7 @@ public class ActionPrecheckManager(Config config) if (item.ConfigType.IsGroupType()) { ProfileGroupItemManager.Instance.TryGet(item.IndexId, out var group); - if (group is null || group.ChildItems.IsNullOrEmpty()) + if (group is null || group.NotHasChild()) { errors.Add(string.Format(ResUI.GroupEmpty, item.Remarks)); return errors; @@ -128,7 +128,11 @@ public class ActionPrecheckManager(Config config) return errors; } - foreach (var child in Utils.String2List(group.ChildItems)) + var childIds = Utils.String2List(group.ChildItems) ?? []; + var subItems = await ProfileGroupItemManager.GetSubChildProfileItems(group); + childIds.AddRange(subItems.Select(p => p.IndexId)); + + foreach (var child in childIds) { var childErrors = new List(); if (child.IsNullOrEmpty()) diff --git a/v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs b/v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs index bf52dcb2..6cd260d3 100644 --- a/v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs +++ b/v2rayN/ServiceLib/Manager/ProfileGroupItemManager.cs @@ -220,11 +220,14 @@ public class ProfileGroupItemManager public static async Task<(List Items, ProfileGroupItem? Group)> GetChildProfileItems(string? indexId) { Instance.TryGet(indexId, out var profileGroupItem); - if (profileGroupItem == null || profileGroupItem.ChildItems.IsNullOrEmpty()) + if (profileGroupItem == null || profileGroupItem.NotHasChild()) { return (new List(), profileGroupItem); } var items = await GetChildProfileItems(profileGroupItem); + var subItems = await GetSubChildProfileItems(profileGroupItem); + items.AddRange(subItems); + return (items, profileGroupItem); } @@ -248,14 +251,39 @@ public class ProfileGroupItemManager return childProfiles; } + public static async Task> GetSubChildProfileItems(ProfileGroupItem? group) + { + if (group == null || group.SubChildItems.IsNullOrEmpty()) + { + return new(); + } + var childProfiles = await AppManager.Instance.ProfileItems(group.SubChildItems); + + return childProfiles.Where(p => + p != null && + p.IsValid() && + !p.ConfigType.IsComplexType() && + (group.Filter.IsNullOrEmpty() || Regex.IsMatch(p.Remarks, group.Filter)) + ) + .ToList(); + } + public static async Task> GetAllChildDomainAddresses(string indexId) { // include grand children var childAddresses = new HashSet(); - if (!Instance.TryGet(indexId, out var groupItem) || groupItem.ChildItems.IsNullOrEmpty()) + if (!Instance.TryGet(indexId, out var groupItem) || groupItem == null) + { return childAddresses; + } - var childIds = Utils.String2List(groupItem.ChildItems); + if (groupItem.SubChildItems.IsNotEmpty()) + { + var subItems = await GetSubChildProfileItems(groupItem); + subItems.ForEach(p => childAddresses.Add(p.Address)); + } + + var childIds = Utils.String2List(groupItem.ChildItems) ?? []; foreach (var childId in childIds) { diff --git a/v2rayN/ServiceLib/Models/ProfileGroupItem.cs b/v2rayN/ServiceLib/Models/ProfileGroupItem.cs index c6131275..12c0f899 100644 --- a/v2rayN/ServiceLib/Models/ProfileGroupItem.cs +++ b/v2rayN/ServiceLib/Models/ProfileGroupItem.cs @@ -8,5 +8,14 @@ public class ProfileGroupItem public string ChildItems { get; set; } + public string? SubChildItems { get; set; } + + public string? Filter { get; set; } + public EMultipleLoad MultipleLoad { get; set; } = EMultipleLoad.LeastPing; + + public bool NotHasChild() + { + return string.IsNullOrWhiteSpace(ChildItems) && string.IsNullOrWhiteSpace(SubChildItems); + } } diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index 90b25530..90a44ae5 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -19,7 +19,7 @@ namespace ServiceLib.Resx { // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen // (以 /str 作为命令选项),或重新生成 VS 项目。 - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class ResUI { @@ -2958,6 +2958,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Auto add filtered configuration from subscription groups 的本地化字符串。 + /// + public static string TbPolicyGroupSubChildTip { + get { + return ResourceManager.GetString("TbPolicyGroupSubChildTip", resourceCulture); + } + } + /// /// 查找类似 Policy Group Type 的本地化字符串。 /// diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index 08d4d111..edbd21da 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -1599,4 +1599,7 @@ Test real delay + + Auto add filtered configuration from subscription groups + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.fr.resx b/v2rayN/ServiceLib/Resx/ResUI.fr.resx index a56802e3..542d9ef9 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fr.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fr.resx @@ -1596,4 +1596,7 @@ Test 1-clic de latence réelle - + + Auto add filtered configuration from subscription groups + + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx index abb43c0a..e61f279e 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -1599,4 +1599,7 @@ Test real delay + + Auto add filtered configuration from subscription groups + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx index 9a1639ce..f1123407 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.resx @@ -1599,4 +1599,7 @@ Test real delay + + Auto add filtered configuration from subscription groups + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx index 67067b9a..14edb9b8 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1599,4 +1599,7 @@ Test real delay + + Auto add filtered configuration from subscription groups + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx index 96c470c6..cfb89284 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -1596,4 +1596,7 @@ 一键测试真连接延迟 + + 自动从订阅分组添加过滤后的配置 + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx index d03b0d20..4a3822b4 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -1596,4 +1596,7 @@ 一鍵測試真連線延遲 + + 自動從訂閱分組新增過濾後的配置 + \ No newline at end of file diff --git a/v2rayN/ServiceLib/ViewModels/AddGroupServerViewModel.cs b/v2rayN/ServiceLib/ViewModels/AddGroupServerViewModel.cs index b5bfe80d..5b0778a5 100644 --- a/v2rayN/ServiceLib/ViewModels/AddGroupServerViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/AddGroupServerViewModel.cs @@ -17,6 +17,14 @@ public class AddGroupServerViewModel : MyReactiveObject [Reactive] public string? PolicyGroupType { get; set; } + [Reactive] + public SubItem? SelectedSubItem { get; set; } + + [Reactive] + public string? Filter { get; set; } + + public IObservableCollection SubItems { get; } = new ObservableCollectionExtended(); + public IObservableCollection ChildItemsObs { get; } = new ObservableCollectionExtended(); //public ReactiveCommand AddCmd { get; } @@ -64,10 +72,14 @@ public class AddGroupServerViewModel : MyReactiveObject }); SelectedSource = profileItem.IndexId.IsNullOrEmpty() ? profileItem : JsonUtils.DeepCopy(profileItem); - CoreType = (SelectedSource?.CoreType ?? ECoreType.Xray).ToString(); - ProfileGroupItemManager.Instance.TryGet(profileItem.IndexId, out var profileGroup); + _ = Init(); + } + + public async Task Init() + { + ProfileGroupItemManager.Instance.TryGet(SelectedSource.IndexId, out var profileGroup); PolicyGroupType = (profileGroup?.MultipleLoad ?? EMultipleLoad.LeastPing) switch { EMultipleLoad.LeastPing => ResUI.TbLeastPing, @@ -78,15 +90,16 @@ public class AddGroupServerViewModel : MyReactiveObject _ => ResUI.TbLeastPing, }; - _ = Init(); - } + var subs = await AppManager.Instance.SubItems(); + subs.Add(new SubItem()); + SubItems.AddRange(subs); + SelectedSubItem = SubItems.Where(s => s.Id == profileGroup?.SubChildItems).FirstOrDefault(); + Filter = profileGroup?.Filter; - public async Task Init() - { var childItemMulti = ProfileGroupItemManager.Instance.GetOrCreateAndMarkDirty(SelectedSource?.IndexId); if (childItemMulti != null) { - var childIndexIds = childItemMulti.ChildItems.IsNullOrEmpty() ? new List() : Utils.String2List(childItemMulti.ChildItems); + var childIndexIds = Utils.String2List(childItemMulti.ChildItems) ?? []; foreach (var item in childIndexIds) { var child = await AppManager.Instance.GetProfileItem(item); @@ -181,7 +194,7 @@ public class AddGroupServerViewModel : MyReactiveObject NoticeManager.Instance.Enqueue(ResUI.PleaseFillRemarks); return; } - if (ChildItemsObs.Count == 0) + if (ChildItemsObs.Count == 0 && SelectedSubItem?.Id.IsNullOrEmpty() == true) { NoticeManager.Instance.Enqueue(ResUI.PleaseAddAtLeastOneServer); return; @@ -213,6 +226,9 @@ public class AddGroupServerViewModel : MyReactiveObject _ => EMultipleLoad.LeastPing, }; + profileGroup.SubChildItems = SelectedSubItem?.Id; + profileGroup.Filter = Filter; + var hasCycle = ProfileGroupItemManager.HasCycle(profileGroup.IndexId); if (hasCycle) { diff --git a/v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml b/v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml index e24fa67c..4279dc4a 100644 --- a/v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml @@ -38,7 +38,7 @@ + RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto"> + + + + + + + diff --git a/v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml.cs b/v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml.cs index a75f0e43..a5692ffc 100644 --- a/v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/AddGroupServerWindow.axaml.cs @@ -46,6 +46,9 @@ public partial class AddGroupServerWindow : WindowBase this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CoreType, v => v.cmbCoreType.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.PolicyGroupType, v => v.cmbPolicyGroupType.SelectedValue).DisposeWith(disposables); + //this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbSubChildItems.ItemsSource).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSubItem, v => v.cmbSubChildItems.SelectedItem).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Filter, v => v.txtFilter.Text).DisposeWith(disposables); this.OneWayBind(ViewModel, vm => vm.ChildItemsObs, v => v.lstChild.ItemsSource).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedChild, v => v.lstChild.SelectedItem).DisposeWith(disposables); diff --git a/v2rayN/v2rayN/Views/AddGroupServerWindow.xaml b/v2rayN/v2rayN/Views/AddGroupServerWindow.xaml index b1867be8..b46d3dcd 100644 --- a/v2rayN/v2rayN/Views/AddGroupServerWindow.xaml +++ b/v2rayN/v2rayN/Views/AddGroupServerWindow.xaml @@ -59,6 +59,8 @@ + + @@ -130,6 +132,45 @@ materialDesign:HintAssist.Hint="{x:Static resx:ResUI.TbPolicyGroupType}" Style="{StaticResource DefComboBox}" /> + + + + + + + diff --git a/v2rayN/v2rayN/Views/AddGroupServerWindow.xaml.cs b/v2rayN/v2rayN/Views/AddGroupServerWindow.xaml.cs index ad3c7ac6..e542c922 100644 --- a/v2rayN/v2rayN/Views/AddGroupServerWindow.xaml.cs +++ b/v2rayN/v2rayN/Views/AddGroupServerWindow.xaml.cs @@ -41,6 +41,9 @@ public partial class AddGroupServerWindow this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.CoreType, v => v.cmbCoreType.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.PolicyGroupType, v => v.cmbPolicyGroupType.Text).DisposeWith(disposables); + this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbSubChildItems.ItemsSource).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.SelectedSubItem, v => v.cmbSubChildItems.SelectedItem).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Filter, v => v.txtFilter.Text).DisposeWith(disposables); this.OneWayBind(ViewModel, vm => vm.ChildItemsObs, v => v.lstChild.ItemsSource).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedChild, v => v.lstChild.SelectedItem).DisposeWith(disposables); From b218f0b50168d12491675d234b69bc16955cb85e Mon Sep 17 00:00:00 2001 From: DHR60 Date: Sun, 2 Nov 2025 15:17:47 +0800 Subject: [PATCH 106/132] Cert Pinning (#8234) * Cert Pinning * Cert Chain Pinning * Add Trusted Ca Pinning * Tip * Perf UI --- v2rayN/ServiceLib/Handler/ConfigHandler.cs | 1 + v2rayN/ServiceLib/Manager/CertPemManager.cs | 323 ++++++++++++++++++ v2rayN/ServiceLib/Models/ProfileItem.cs | 1 + v2rayN/ServiceLib/Models/SingboxConfig.cs | 1 + v2rayN/ServiceLib/Models/V2rayConfig.cs | 8 + v2rayN/ServiceLib/Resx/ResUI.Designer.cs | 66 +++- v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx | 22 ++ v2rayN/ServiceLib/Resx/ResUI.fr.resx | 22 ++ v2rayN/ServiceLib/Resx/ResUI.hu.resx | 22 ++ v2rayN/ServiceLib/Resx/ResUI.resx | 22 ++ v2rayN/ServiceLib/Resx/ResUI.ru.resx | 22 ++ v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx | 22 ++ v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx | 22 ++ .../Singbox/SingboxOutboundService.cs | 111 +++--- .../CoreConfig/V2ray/V2rayOutboundService.cs | 22 ++ .../ViewModels/AddServerViewModel.cs | 91 +++++ .../Views/AddServerWindow.axaml | 69 +++- .../Views/AddServerWindow.axaml.cs | 4 + v2rayN/v2rayN/Views/AddServerWindow.xaml | 58 ++++ v2rayN/v2rayN/Views/AddServerWindow.xaml.cs | 4 + 20 files changed, 858 insertions(+), 55 deletions(-) create mode 100644 v2rayN/ServiceLib/Manager/CertPemManager.cs diff --git a/v2rayN/ServiceLib/Handler/ConfigHandler.cs b/v2rayN/ServiceLib/Handler/ConfigHandler.cs index 7d2ca056..72addec4 100644 --- a/v2rayN/ServiceLib/Handler/ConfigHandler.cs +++ b/v2rayN/ServiceLib/Handler/ConfigHandler.cs @@ -252,6 +252,7 @@ public static class ConfigHandler item.Mldsa65Verify = profileItem.Mldsa65Verify; item.Extra = profileItem.Extra; item.MuxEnabled = profileItem.MuxEnabled; + item.Cert = profileItem.Cert; } var ret = item.ConfigType switch diff --git a/v2rayN/ServiceLib/Manager/CertPemManager.cs b/v2rayN/ServiceLib/Manager/CertPemManager.cs new file mode 100644 index 00000000..7469f351 --- /dev/null +++ b/v2rayN/ServiceLib/Manager/CertPemManager.cs @@ -0,0 +1,323 @@ +using System.Net.Security; +using System.Security.Cryptography.X509Certificates; + +namespace ServiceLib.Manager; + +/// +/// Manager for certificate operations with CA pinning to prevent MITM attacks +/// +public class CertPemManager +{ + private static readonly string _tag = "CertPemManager"; + private static readonly Lazy _instance = new(() => new()); + public static CertPemManager Instance => _instance.Value; + + /// + /// Trusted CA certificate thumbprints (SHA256) to prevent MITM attacks + /// + private static readonly HashSet TrustedCaThumbprints = new(StringComparer.OrdinalIgnoreCase) + { + "EBD41040E4BB3EC742C9E381D31EF2A41A48B6685C96E7CEF3C1DF6CD4331C99", // GlobalSign Root CA + "6DC47172E01CBCB0BF62580D895FE2B8AC9AD4F873801E0C10B9C837D21EB177", // Entrust.net Premium 2048 Secure Server CA + "73C176434F1BC6D5ADF45B0E76E727287C8DE57616C1E6E6141A2B2CBC7D8E4C", // Entrust Root Certification Authority + "D8E0FEBC1DB2E38D00940F37D27D41344D993E734B99D5656D9778D4D8143624", // Certum Root CA + "D7A7A0FB5D7E2731D771E9484EBCDEF71D5F0C3E0A2948782BC83EE0EA699EF4", // Comodo AAA Services root + "85A0DD7DD720ADB7FF05F83D542B209DC7FF4528F7D677B18389FEA5E5C49E86", // QuoVadis Root CA 2 + "18F1FC7F205DF8ADDDEB7FE007DD57E3AF375A9C4D8D73546BF4F1FED1E18D35", // QuoVadis Root CA 3 + "CECDDC905099D8DADFC5B1D209B737CBE2C18CFB2C10C0FF0BCF0D3286FC1AA2", // XRamp Global CA Root + "C3846BF24B9E93CA64274C0EC67C1ECC5E024FFCACD2D74019350E81FE546AE4", // Go Daddy Class 2 CA + "1465FA205397B876FAA6F0A9958E5590E40FCC7FAA4FB7C2C8677521FB5FB658", // Starfield Class 2 CA + "3E9099B5015E8F486C00BCEA9D111EE721FABA355A89BCF1DF69561E3DC6325C", // DigiCert Assured ID Root CA + "4348A0E9444C78CB265E058D5E8944B4D84F9662BD26DB257F8934A443C70161", // DigiCert Global Root CA + "7431E5F4C3C1CE4690774F0B61E05440883BA9A01ED00BA6ABD7806ED3B118CF", // DigiCert High Assurance EV Root CA + "62DD0BE9B9F50A163EA0F8E75C053B1ECA57EA55C8688F647C6881F2C8357B95", // SwissSign Gold CA - G2 + "F1C1B50AE5A20DD8030EC9F6BC24823DD367B5255759B4E71B61FCE9F7375D73", // SecureTrust CA + "4200F5043AC8590EBB527D209ED1503029FBCBD41CA1B506EC27F15ADE7DAC69", // Secure Global CA + "0C2CD63DF7806FA399EDE809116B575BF87989F06518F9808C860503178BAF66", // COMODO Certification Authority + "1793927A0614549789ADCE2F8F34F7F0B66D0F3AE3A3B84D21EC15DBBA4FADC7", // COMODO ECC Certification Authority + "41C923866AB4CAD6B7AD578081582E020797A6CBDF4FFF78CE8396B38937D7F5", // OISTE WISeKey Global Root GA CA + "E3B6A2DB2ED7CE48842F7AC53241C7B71D54144BFB40C11F3F1D0B42F5EEA12D", // Certigna + "C0A6F4DC63A24BFDCF54EF2A6A082A0A72DE35803E2FF5FF527AE5D87206DFD5", // ePKI Root Certification Authority + "EAA962C4FA4A6BAFEBE415196D351CCD888D4F53F3FA8AE6D7C466A94E6042BB", // certSIGN ROOT CA + "6C61DAC3A2DEF031506BE036D2A6FE401994FBD13DF9C8D466599274C446EC98", // NetLock Arany (Class Gold) Főtanúsítvány + "3C5F81FEA5FAB82C64BFA2EAECAFCDE8E077FC8620A7CAE537163DF36EDBF378", // Microsec e-Szigno Root CA 2009 + "CBB522D7B7F127AD6A0113865BDF1CD4102E7D0759AF635A7CF4720DC963C53B", // GlobalSign Root CA - R3 + "2530CC8E98321502BAD96F9B1FBA1B099E2D299E0F4548BB914F363BC0D4531F", // Izenpe.com + "45140B3247EB9CC8C5B4F0D7B53091F73292089E6E5A63E2749DD3ACA9198EDA", // Go Daddy Root Certificate Authority - G2 + "2CE1CB0BF9D2F9E102993FBE215152C3B2DD0CABDE1C68E5319B839154DBB7F5", // Starfield Root Certificate Authority - G2 + "568D6905A2C88708A4B3025190EDCFEDB1974A606A13C6E5290FCB2AE63EDAB5", // Starfield Services Root Certificate Authority - G2 + "0376AB1D54C5F9803CE4B2E201A0EE7EEF7B57B636E8A93C9B8D4860C96F5FA7", // AffirmTrust Commercial + "0A81EC5A929777F145904AF38D5D509F66B5E2C58FCDB531058B0E17F3F0B41B", // AffirmTrust Networking + "70A73F7F376B60074248904534B11482D5BF0E698ECC498DF52577EBF2E93B9A", // AffirmTrust Premium + "BD71FDF6DA97E4CF62D1647ADD2581B07D79ADF8397EB4ECBA9C5E8488821423", // AffirmTrust Premium ECC + "5C58468D55F58E497E743982D2B50010B6D165374ACF83A7D4A32DB768C4408E", // Certum Trusted Network CA + "BFD88FE1101C41AE3E801BF8BE56350EE9BAD1A6B9BD515EDC5C6D5B8711AC44", // TWCA Root Certification Authority + "513B2CECB810D4CDE5DD85391ADFC6C2DD60D87BB736D2B521484AA47A0EBEF6", // Security Communication RootCA2 + "55926084EC963A64B96E2ABE01CE0BA86A64FBFEBCC7AAB5AFC155B37FD76066", // Actalis Authentication Root CA + "9A114025197C5BB95D94E63D55CD43790847B646B23CDF11ADA4A00EFF15FB48", // Buypass Class 2 Root CA + "EDF7EBBCA27A2A384D387B7D4010C666E2EDB4843E4C29B4AE1D5B9332E6B24D", // Buypass Class 3 Root CA + "FD73DAD31C644FF1B43BEF0CCDDA96710B9CD9875ECA7E31707AF3E96D522BBD", // T-TeleSec GlobalRoot Class 3 + "49E7A442ACF0EA6287050054B52564B650E4F49E42E348D6AA38E039E957B1C1", // D-TRUST Root Class 3 CA 2 2009 + "EEC5496B988CE98625B934092EEC2908BED0B0F316C2D4730C84EAF1F3D34881", // D-TRUST Root Class 3 CA 2 EV 2009 + "E23D4A036D7B70E9F595B1422079D2B91EDFBB1FB651A0633EAA8A9DC5F80703", // CA Disig Root R2 + "9A6EC012E1A7DA9DBE34194D478AD7C0DB1822FB071DF12981496ED104384113", // ACCVRAIZ1 + "59769007F7685D0FCD50872F9F95D5755A5B2B457D81F3692B610A98672F0E1B", // TWCA Global Root CA + "DD6936FE21F8F077C123A1A521C12224F72255B73E03A7260693E8A24B0FA389", // TeliaSonera Root CA v1 + "91E2F5788D5810EBA7BA58737DE1548A8ECACD014598BC0B143E041B17052552", // T-TeleSec GlobalRoot Class 2 + "F356BEA244B7A91EB35D53CA9AD7864ACE018E2D35D5F8F96DDF68A6F41AA474", // Atos TrustedRoot 2011 + "8A866FD1B276B57E578E921C65828A2BED58E9F2F288054134B7F1F4BFC9CC74", // QuoVadis Root CA 1 G3 + "8FE4FB0AF93A4D0D67DB0BEBB23E37C71BF325DCBCDD240EA04DAF58B47E1840", // QuoVadis Root CA 2 G3 + "88EF81DE202EB018452E43F864725CEA5FBD1FC2D9D205730709C5D8B8690F46", // QuoVadis Root CA 3 G3 + "7D05EBB682339F8C9451EE094EEBFEFA7953A114EDB2F44949452FAB7D2FC185", // DigiCert Assured ID Root G2 + "7E37CB8B4C47090CAB36551BA6F45DB840680FBA166A952DB100717F43053FC2", // DigiCert Assured ID Root G3 + "CB3CCBB76031E5E0138F8DD39A23F9DE47FFC35E43C1144CEA27D46A5AB1CB5F", // DigiCert Global Root G2 + "31AD6648F8104138C738F39EA4320133393E3A18CC02296EF97C2AC9EF6731D0", // DigiCert Global Root G3 + "552F7BDCF1A7AF9E6CE672017F4F12ABF77240C78E761AC203D1D9D20AC89988", // DigiCert Trusted Root G4 + "52F0E1C4E58EC629291B60317F074671B85D7EA80D5B07273463534B32B40234", // COMODO RSA Certification Authority + "E793C9B02FD8AA13E21C31228ACCB08119643B749C898964B1746D46C3D4CBD2", // USERTrust RSA Certification Authority + "4FF460D54B9C86DABFBCFC5712E0400D2BED3FBC4D4FBDAA86E06ADCD2A9AD7A", // USERTrust ECC Certification Authority + "179FBC148A3DD00FD24EA13458CC43BFA7F59C8182D783A513F6EBEC100C8924", // GlobalSign ECC Root CA - R5 + "3C4FB0B95AB8B30032F432B86F535FE172C185D0FD39865837CF36187FA6F428", // Staat der Nederlanden Root CA - G3 + "5D56499BE4D2E08BCFCAD08A3E38723D50503BDE706948E42F55603019E528AE", // IdenTrust Commercial Root CA 1 + "30D0895A9A448A262091635522D1F52010B5867ACAE12C78EF958FD4F4389F2F", // IdenTrust Public Sector Root CA 1 + "43DF5774B03E7FEF5FE40D931A7BEDF1BB2E6B42738C4E6D3841103D3AA7F339", // Entrust Root Certification Authority - G2 + "02ED0EB28C14DA45165C566791700D6451D7FB56F0B2AB1D3B8EB070E56EDFF5", // Entrust Root Certification Authority - EC1 + "5CC3D78E4E1D5E45547A04E6873E64F90CF9536D1CCC2EF800F355C4C5FD70FD", // CFCA EV ROOT + "6B9C08E86EB0F767CFAD65CD98B62149E5494A67F5845E7BD1ED019F27B86BD6", // OISTE WISeKey Global Root GB CA + "A1339D33281A0B56E557D3D32B1CE7F9367EB094BD5FA72A7E5004C8DED7CAFE", // SZAFIR ROOT CA2 + "B676F2EDDAE8775CD36CB0F63CD1D4603961F49E6265BA013A2F0307B6D0B804", // Certum Trusted Network CA 2 + "A040929A02CE53B4ACF4F2FFC6981CE4496F755E6D45FE0B2A692BCD52523F36", // Hellenic Academic and Research Institutions RootCA 2015 + "44B545AA8A25E65A73CA15DC27FC36D24C1CB9953A066539B11582DC487B4833", // Hellenic Academic and Research Institutions ECC RootCA 2015 + "96BCEC06264976F37460779ACF28C5A7CFE8A3C0AAE11A8FFCEE05C0BDDF08C6", // ISRG Root X1 + "EBC5570C29018C4D67B1AA127BAF12F703B4611EBC17B7DAB5573894179B93FA", // AC RAIZ FNMT-RCM + "8ECDE6884F3D87B1125BA31AC3FCB13D7016DE7F57CC904FE1CB97C6AE98196E", // Amazon Root CA 1 + "1BA5B2AA8C65401A82960118F80BEC4F62304D83CEC4713A19C39C011EA46DB4", // Amazon Root CA 2 + "18CE6CFE7BF14E60B2E347B8DFE868CB31D02EBB3ADA271569F50343B46DB3A4", // Amazon Root CA 3 + "E35D28419ED02025CFA69038CD623962458DA5C695FBDEA3C22B0BFB25897092", // Amazon Root CA 4 + "A1A86D04121EB87F027C66F53303C28E5739F943FC84B38AD6AF009035DD9457", // D-TRUST Root CA 3 2013 + "46EDC3689046D53A453FB3104AB80DCAEC658B2660EA1629DD7E867990648716", // TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 + "BFFF8FD04433487D6A8AA60C1A29767A9FC2BBB05E420F713A13B992891D3893", // GDCA TrustAUTH R5 ROOT + "85666A562EE0BE5CE925C1D8890A6F76A87EC16D4D7D5F29EA7419CF20123B69", // SSL.com Root Certification Authority RSA + "3417BB06CC6007DA1B961C920B8AB4CE3FAD820E4AA30B9ACBC4A74EBDCEBC65", // SSL.com Root Certification Authority ECC + "2E7BF16CC22485A7BBE2AA8696750761B0AE39BE3B2FE9D0CC6D4EF73491425C", // SSL.com EV Root Certification Authority RSA R2 + "22A2C1F7BDED704CC1E701B5F408C310880FE956B5DE2A4A44F99C873A25A7C8", // SSL.com EV Root Certification Authority ECC + "2CABEAFE37D06CA22ABA7391C0033D25982952C453647349763A3AB5AD6CCF69", // GlobalSign Root CA - R6 + "8560F91C3624DABA9570B5FEA0DBE36FF11A8323BE9486854FB3F34A5571198D", // OISTE WISeKey Global Root GC CA + "9BEA11C976FE014764C1BE56A6F914B5A560317ABD9988393382E5161AA0493C", // UCA Global G2 Root + "D43AF9B35473755C9684FC06D7D8CB70EE5C28E773FB294EB41EE71722924D24", // UCA Extended Validation Root + "D48D3D23EEDB50A459E55197601C27774B9D7B18C94D5A059511A10250B93168", // Certigna Root CA + "40F6AF0346A99AA1CD1D555A4E9CCE62C7F9634603EE406615833DC8C8D00367", // emSign Root CA - G1 + "86A1ECBA089C4A8D3BBE2734C612BA341D813E043CF9E8A862CD5C57A36BBE6B", // emSign ECC Root CA - G3 + "125609AA301DA0A249B97A8239CB6A34216F44DCAC9F3954B14292F2E8C8608F", // emSign Root CA - C1 + "BC4D809B15189D78DB3E1D8CF4F9726A795DA1643CA5F1358E1DDB0EDC0D7EB3", // emSign ECC Root CA - C3 + "5A2FC03F0C83B090BBFA40604B0988446C7636183DF9846E17101A447FB8EFD6", // Hongkong Post Root CA 3 + "DB3517D1F6732A2D5AB97C533EC70779EE3270A62FB4AC4238372460E6F01E88", // Entrust Root Certification Authority - G4 + "358DF39D764AF9E1B766E9C972DF352EE15CFAC227AF6AD1D70E8E4A6EDCBA02", // Microsoft ECC Root Certificate Authority 2017 + "C741F70F4B2A8D88BF2E71C14122EF53EF10EBA0CFA5E64CFA20F418853073E0", // Microsoft RSA Root Certificate Authority 2017 + "BEB00B30839B9BC32C32E4447905950641F26421B15ED089198B518AE2EA1B99", // e-Szigno Root CA 2017 + "657CFE2FA73FAA38462571F332A2363A46FCE7020951710702CDFBB6EEDA3305", // certSIGN Root CA G2 + "97552015F5DDFC3C8788C006944555408894450084F100867086BC1A2BB58DC8", // Trustwave Global Certification Authority + "945BBC825EA554F489D1FD51A73DDF2EA624AC7019A05205225C22A78CCFA8B4", // Trustwave Global ECC P256 Certification Authority + "55903859C8C0C3EBB8759ECE4E2557225FF5758BBD38EBD48276601E1BD58097", // Trustwave Global ECC P384 Certification Authority + "88F438DCF8FFD1FA8F429115FFE5F82AE1E06E0C70C375FAAD717B34A49E7265", // NAVER Global Root Certification Authority + "554153B13D2CF9DDB753BFBE1A4E0AE08D0AA4187058FE60A2B862B2E4B87BCB", // AC RAIZ FNMT-RCM SERVIDORES SEGUROS + "319AF0A7729E6F89269C131EA6A3A16FCD86389FDCAB3C47A4A675C161A3F974", // GlobalSign Secure Mail Root R45 + "5CBF6FB81FD417EA4128CD6F8172A3C9402094F74AB2ED3A06B4405D04F30B19", // GlobalSign Secure Mail Root E45 + "4FA3126D8D3A11D1C4855A4F807CBAD6CF919D3A5A88B03BEA2C6372D93C40C9", // GlobalSign Root R46 + "CBB9C44D84B8043E1050EA31A69F514955D7BFD2E2C6B49301019AD61D9F5058", // GlobalSign Root E46 + "9A296A5182D1D451A2E37F439B74DAAFA267523329F90F9A0D2007C334E23C9A", // GLOBALTRUST 2020 + "FB8FEC759169B9106B1E511644C618C51304373F6C0643088D8BEFFD1B997599", // ANF Secure Server Root CA + "6B328085625318AA50D173C98D8BDA09D57E27413D114CF787A0F5D06C030CF6", // Certum EC-384 CA + "FE7696573855773E37A95E7AD4D9CC96C30157C15D31765BA9B15704E1AE78FD", // Certum Trusted Root CA + "2E44102AB58CB85419451C8E19D9ACF3662CAFBC614B6A53960A30F7D0E2EB41", // TunTrust Root CA + "D95D0E8EDA79525BF9BEB11B14D2100D3294985F0C62D9FABD9CD999ECCB7B1D", // HARICA TLS RSA Root CA 2021 + "3F99CC474ACFCE4DFED58794665E478D1547739F2E780F1BB4CA9B133097D401", // HARICA TLS ECC Root CA 2021 + "1BE7ABE30686B16348AFD1C61B6866A0EA7F4821E67D5E8AF937CF8011BC750D", // HARICA Client RSA Root CA 2021 + "8DD4B5373CB0DE36769C12339280D82746B3AA6CD426E797A31BABE4279CF00B", // HARICA Client ECC Root CA 2021 + "57DE0583EFD2B26E0361DA99DA9DF4648DEF7EE8441C3B728AFA9BCDE0F9B26A", // Autoridad de Certificacion Firmaprofesional CIF A62634068 + "30FBBA2C32238E2A98547AF97931E550428B9B3F1C8EEB6633DCFA86C5B27DD3", // vTrus ECC Root CA + "8A71DE6559336F426C26E53880D00D88A18DA4C6A91F0DCB6194E206C5C96387", // vTrus Root CA + "69729B8E15A86EFC177A57AFB7171DFC64ADD28C2FCA8CF1507E34453CCB1470", // ISRG Root X2 + "F015CE3CC239BFEF064BE9F1D2C417E1A0264A0A94BE1F0C8D121864EB6949CC", // HiPKI Root CA - G1 + "B085D70B964F191A73E4AF0D54AE7A0E07AAFDAF9B71DD0862138AB7325A24A2", // GlobalSign ECC Root CA - R4 + "D947432ABDE7B7FA90FC2E6B59101B1280E0E1C7E4E40FA3C6887FFF57A7F4CF", // GTS Root R1 + "8D25CD97229DBF70356BDA4EB3CC734031E24CF00FAFCFD32DC76EB5841C7EA8", // GTS Root R2 + "34D8A73EE208D9BCDB0D956520934B4E40E69482596E8B6F73C8426B010A6F48", // GTS Root R3 + "349DFA4058C5E263123B398AE795573C4E1313C83FE68F93556CD5E8031B3C7D", // GTS Root R4 + "242B69742FCB1E5B2ABF98898B94572187544E5B4D9911786573621F6A74B82C", // Telia Root CA v2 + "E59AAA816009C22BFF5B25BAD37DF306F049797C1F81D85AB089E657BD8F0044", // D-TRUST BR Root CA 1 2020 + "08170D1AA36453901A2F959245E347DB0C8D37ABAABC56B81AA100DC958970DB", // D-TRUST EV Root CA 1 2020 + "018E13F0772532CF809BD1B17281867283FC48C6E13BE9C69812854A490C1B05", // DigiCert TLS ECC P384 Root G5 + "371A00DC0533B3721A7EEB40E8419E70799D2B0A0F2C1D80693165F7CEC4AD75", // DigiCert TLS RSA4096 Root G5 + "E8E8176536A60CC2C4E10187C3BEFCA20EF263497018F566D5BEA0F94D0C111B", // DigiCert SMIME ECC P384 Root G5 + "90370D3EFA88BF58C30105BA25104A358460A7FA52DFC2011DF233A0F417912A", // DigiCert SMIME RSA4096 Root G5 + "77B82CD8644C4305F7ACC5CB156B45675004033D51C60C6202A8E0C33467D3A0", // Certainly Root R1 + "B4585F22E4AC756A4E8612A1361C5D9D031A93FD84FEBB778FA3068B0FC42DC2", // Certainly Root E1 + "82BD5D851ACF7F6E1BA7BFCBC53030D0E7BC3C21DF772D858CAB41D199BDF595", // DIGITALSIGN GLOBAL ROOT RSA CA + "261D7114AE5F8FF2D8C7209A9DE4289E6AFC9D717023D85450909199F1857CFE", // DIGITALSIGN GLOBAL ROOT ECDSA CA + "E74FBDA55BD564C473A36B441AA799C8A68E077440E8288B9FA1E50E4BBACA11", // Security Communication ECC RootCA1 + "F3896F88FE7C0A882766A7FA6AD2749FB57A7F3E98FB769C1FA7B09C2C44D5AE", // BJCA Global Root CA1 + "574DF6931E278039667B720AFDC1600FC27EB66DD3092979FB73856487212882", // BJCA Global Root CA2 + "48E1CF9E43B688A51044160F46D773B8277FE45BEAAD0E4DF90D1974382FEA99", // LAWtrust Root CA2 (4096) + "22D9599234D60F1D4BC7C7E96F43FA555B07301FD475175089DAFB8C25E477B3", // Sectigo Public Email Protection Root E46 + "D5917A7791EB7CF20A2E57EB98284A67B28A57E89182DA53D546678C9FDE2B4F", // Sectigo Public Email Protection Root R46 + "C90F26F0FB1B4018B22227519B5CA2B53E2CA5B3BE5CF18EFE1BEF47380C5383", // Sectigo Public Server Authentication Root E46 + "7BB647A62AEEAC88BF257AA522D01FFEA395E0AB45C73F93F65654EC38F25A06", // Sectigo Public Server Authentication Root R46 + "8FAF7D2E2CB4709BB8E0B33666BF75A5DD45B5DE480F8EA8D4BFE6BEBC17F2ED", // SSL.com TLS RSA Root CA 2022 + "C32FFD9F46F936D16C3673990959434B9AD60AAFBB9E7CF33654F144CC1BA143", // SSL.com TLS ECC Root CA 2022 + "AD7DD58D03AEDB22A30B5084394920CE12230C2D8017AD9B81AB04079BDD026B", // SSL.com Client ECC Root CA 2022 + "1D4CA4A2AB21D0093659804FC0EB2175A617279B56A2475245C9517AFEB59153", // SSL.com Client RSA Root CA 2022 + "E38655F4B0190C84D3B3893D840A687E190A256D98052F159E6D4A39F589A6EB", // Atos TrustedRoot Root CA ECC G2 2020 + "78833A783BB2986C254B9370D3C20E5EBA8FA7840CBF63FE17297A0B0119685E", // Atos TrustedRoot Root CA RSA G2 2020 + "B2FAE53E14CCD7AB9212064701AE279C1D8988FACB775FA8A008914E663988A8", // Atos TrustedRoot Root CA ECC TLS 2021 + "81A9088EA59FB364C548A6F85559099B6F0405EFBF18E5324EC9F457BA00112F", // Atos TrustedRoot Root CA RSA TLS 2021 + "E0D3226AEB1163C2E48FF9BE3B50B4C6431BE7BB1EACC5C36B5D5EC509039A08", // TrustAsia Global Root CA G3 + "BE4B56CB5056C0136A526DF444508DAA36A0B54F42E4AC38F72AF470E479654C", // TrustAsia Global Root CA G4 + "D92C171F5CF890BA428019292927FE22F3207FD2B54449CB6F675AF4922146E2", // D-Trust SBR Root CA 1 2022 + "DBA84DD7EF622D485463A90137EA4D574DF8550928F6AFA03B4D8B1141E636CC", // D-Trust SBR Root CA 2 2022 + "3AE6DF7E0D637A65A8C81612EC6F9A142F85A16834C10280D88E707028518755", // Telekom Security SMIME ECC Root 2021 + "578AF4DED0853F4E5998DB4AEAF9CBEA8D945F60B620A38D1A3C13B2BC7BA8E1", // Telekom Security TLS ECC Root 2020 + "78A656344F947E9CC0F734D9053D32F6742086B6B9CD2CAE4FAE1A2E4EFDE048", // Telekom Security SMIME RSA Root 2023 + "EFC65CADBB59ADB6EFE84DA22311B35624B71B3B1EA0DA8B6655174EC8978646", // Telekom Security TLS RSA Root 2023 + "BEF256DAF26E9C69BDEC1602359798F3CAF71821A03E018257C53C65617F3D4A", // FIRMAPROFESIONAL CA ROOT-A WEB + "3F63BB2814BE174EC8B6439CF08D6D56F0B7C405883A5648A334424D6B3EC558", // TWCA CYBER Root CA + "3A0072D49FFC04E996C59AEB75991D3C340F3615D6FD4DCE90AC0B3D88EAD4F4", // TWCA Global Root CA G2 + "3F034BB5704D44B2D08545A02057DE93EBF3905FCE721ACBC730C06DDAEE904E", // SecureSign Root CA12 + "4B009C1034494F9AB56BBA3BA1D62731FC4D20D8955ADCEC10A925607261E338", // SecureSign Root CA14 + "E778F0F095FE843729CD1A0082179E5314A9C291442805E1FB1D8FB6B8886C3A", // SecureSign Root CA15 + "0552E6F83FDF65E8FA9670E666DF28A4E21340B510CBE52566F97C4FB94B2BD1", // D-TRUST BR Root CA 2 2023 + "436472C1009A325C54F1A5BBB5468A7BAEECCBE05DE5F099CB70D3FE41E13C16", // TrustAsia SMIME ECC Root CA + "C7796BEB62C101BB143D262A7C96A0C6168183223EF50D699632D86E03B8CC9B", // TrustAsia SMIME RSA Root CA + "C0076B9EF0531FB1A656D67C4EBE97CD5DBAA41EF44598ACC2489878C92D8711", // TrustAsia TLS ECC Root CA + "06C08D7DAFD876971EB1124FE67F847EC0C7A158D3EA53CBE940E2EA9791F4C3", // TrustAsia TLS RSA Root CA + "8E8221B2E7D4007836A1672F0DCC299C33BC07D316F132FA1A206D587150F1CE", // D-TRUST EV Root CA 2 2023 + "9A12C392BFE57891A0C545309D4D9FD567E480CB613D6342278B195C79A7931F", // SwissSign RSA SMIME Root CA 2022 - 1 + "193144F431E0FDDB740717D4DE926A571133884B4360D30E272913CBE660CE41", // SwissSign RSA TLS Root CA 2022 - 1 + "D9A32485A8CCA85539CEF12FFFFF711378A17851D73DA2732AB4302D763BD62B", // OISTE Client Root ECC G1 + "D02A0F994A868C66395F2E7A880DF509BD0C29C96DE16015A0FD501EDA4F96A9", // OISTE Client Root RSA G1 + "EEC997C0C30F216F7E3B8B307D2BAE42412D753FC8219DAFD1520B2572850F49", // OISTE Server Root ECC G1 + "9AE36232A5189FFDDB353DFD26520C015395D22777DAC59DB57B98C089A651E6", // OISTE Server Root RSA G1 + }; + + /// + /// Get certificate in PEM format from a server with CA pinning validation + /// + public async Task GetCertPemAsync(string target, string serverName) + { + try + { + var (domain, _, port, _) = Utils.ParseUrl(target); + + using var client = new TcpClient(); + await client.ConnectAsync(domain, port > 0 ? port : 443); + + using var ssl = new SslStream(client.GetStream(), false, ValidateServerCertificate); + + await ssl.AuthenticateAsClientAsync(serverName); + + var remote = ssl.RemoteCertificate; + if (remote == null) + { + return null; + } + + var leaf = new X509Certificate2(remote); + return ExportCertToPem(leaf); + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + return null; + } + } + + /// + /// Get certificate chain in PEM format from a server with CA pinning validation + /// + public async Task> GetCertChainPemAsync(string target, string serverName) + { + try + { + var pemList = new List(); + var (domain, _, port, _) = Utils.ParseUrl(target); + + using var client = new TcpClient(); + await client.ConnectAsync(domain, port > 0 ? port : 443); + + using var ssl = new SslStream(client.GetStream(), false, ValidateServerCertificate); + + await ssl.AuthenticateAsClientAsync(serverName); + + if (ssl.RemoteCertificate is not X509Certificate2 certChain) + { + return pemList; + } + + var chain = new X509Chain(); + chain.Build(certChain); + + foreach (var element in chain.ChainElements) + { + var pem = ExportCertToPem(element.Certificate); + pemList.Add(pem); + } + + return pemList; + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + return new List(); + } + } + + /// + /// Validate server certificate with CA pinning + /// + private bool ValidateServerCertificate( + object sender, + X509Certificate? certificate, + X509Chain? chain, + SslPolicyErrors sslPolicyErrors) + { + if (certificate == null) + { + return false; + } + + // Check certificate name mismatch + if (sslPolicyErrors.HasFlag(SslPolicyErrors.RemoteCertificateNameMismatch)) + { + return false; + } + + // Build certificate chain + var cert2 = certificate as X509Certificate2 ?? new X509Certificate2(certificate); + var certChain = chain ?? new X509Chain(); + + certChain.ChainPolicy.RevocationMode = X509RevocationMode.Online; + certChain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; + certChain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag; + certChain.ChainPolicy.VerificationTime = DateTime.Now; + + certChain.Build(cert2); + + // Find root CA + if (certChain.ChainElements.Count == 0) + { + return false; + } + + var rootCert = certChain.ChainElements[certChain.ChainElements.Count - 1].Certificate; + var rootThumbprint = rootCert.GetCertHashString(HashAlgorithmName.SHA256); + + return TrustedCaThumbprints.Contains(rootThumbprint); + } + + public string ExportCertToPem(X509Certificate2 cert) + { + var der = cert.Export(X509ContentType.Cert); + var b64 = Convert.ToBase64String(der, Base64FormattingOptions.InsertLineBreaks); + return $"-----BEGIN CERTIFICATE-----\n{b64}\n-----END CERTIFICATE-----\n"; + } +} diff --git a/v2rayN/ServiceLib/Models/ProfileItem.cs b/v2rayN/ServiceLib/Models/ProfileItem.cs index fa84ac59..b832a5e7 100644 --- a/v2rayN/ServiceLib/Models/ProfileItem.cs +++ b/v2rayN/ServiceLib/Models/ProfileItem.cs @@ -141,4 +141,5 @@ public class ProfileItem : ReactiveObject public string Mldsa65Verify { get; set; } public string Extra { get; set; } public bool? MuxEnabled { get; set; } + public string Cert { get; set; } } diff --git a/v2rayN/ServiceLib/Models/SingboxConfig.cs b/v2rayN/ServiceLib/Models/SingboxConfig.cs index 9474631b..6dde5e7c 100644 --- a/v2rayN/ServiceLib/Models/SingboxConfig.cs +++ b/v2rayN/ServiceLib/Models/SingboxConfig.cs @@ -181,6 +181,7 @@ public class Tls4Sbox public bool? fragment { get; set; } public string? fragment_fallback_delay { get; set; } public bool? record_fragment { get; set; } + public List? certificate { get; set; } } public class Multiplex4Sbox diff --git a/v2rayN/ServiceLib/Models/V2rayConfig.cs b/v2rayN/ServiceLib/Models/V2rayConfig.cs index 22dcf9f4..e10cd0d9 100644 --- a/v2rayN/ServiceLib/Models/V2rayConfig.cs +++ b/v2rayN/ServiceLib/Models/V2rayConfig.cs @@ -354,6 +354,14 @@ public class TlsSettings4Ray public string? shortId { get; set; } public string? spiderX { get; set; } public string? mldsa65Verify { get; set; } + public List? certificates { get; set; } + public bool? disableSystemRoot { get; set; } +} + +public class CertificateSettings4Ray +{ + public List? certificate { get; set; } + public string? usage { get; set; } } public class TcpSettings4Ray diff --git a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs index 90a44ae5..ea649da5 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.Designer.cs +++ b/v2rayN/ServiceLib/Resx/ResUI.Designer.cs @@ -19,7 +19,7 @@ namespace ServiceLib.Resx { // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen // (以 /str 作为命令选项),或重新生成 VS 项目。 - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class ResUI { @@ -87,6 +87,24 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Certificate not set 的本地化字符串。 + /// + public static string CertNotSet { + get { + return ResourceManager.GetString("CertNotSet", resourceCulture); + } + } + + /// + /// 查找类似 Certificate set 的本地化字符串。 + /// + public static string CertSet { + get { + return ResourceManager.GetString("CertSet", resourceCulture); + } + } + /// /// 查找类似 Please check the Configuration settings first. 的本地化字符串。 /// @@ -2301,6 +2319,15 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Please set a valid domain 的本地化字符串。 + /// + public static string ServerNameMustBeValidDomain { + get { + return ResourceManager.GetString("ServerNameMustBeValidDomain", resourceCulture); + } + } + /// /// 查找类似 {0} : {1}/s↑ | {2}/s↓ 的本地化字符串。 /// @@ -2562,6 +2589,25 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Certificate Pinning 的本地化字符串。 + /// + public static string TbCertPinning { + get { + return ResourceManager.GetString("TbCertPinning", resourceCulture); + } + } + + /// + /// 查找类似 Server certificate (PEM format, optional). Entering a certificate will pin it. + ///Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled. 的本地化字符串。 + /// + public static string TbCertPinningTips { + get { + return ResourceManager.GetString("TbCertPinningTips", resourceCulture); + } + } + /// /// 查找类似 Clear system proxy 的本地化字符串。 /// @@ -2769,6 +2815,24 @@ namespace ServiceLib.Resx { } } + /// + /// 查找类似 Fetch Certificate 的本地化字符串。 + /// + public static string TbFetchCert { + get { + return ResourceManager.GetString("TbFetchCert", resourceCulture); + } + } + + /// + /// 查找类似 Fetch Certificate Chain 的本地化字符串。 + /// + public static string TbFetchCertChain { + get { + return ResourceManager.GetString("TbFetchCertChain", resourceCulture); + } + } + /// /// 查找类似 Fingerprint 的本地化字符串。 /// diff --git a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx index edbd21da..64d74cd9 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fa-Ir.resx @@ -1602,4 +1602,26 @@ Auto add filtered configuration from subscription groups + + Certificate Pinning + + + Server certificate (PEM format, optional). Entering a certificate will pin it. +Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled. + + + Fetch Certificate + + + Fetch Certificate Chain + + + Please set a valid domain + + + Certificate not set + + + Certificate set + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.fr.resx b/v2rayN/ServiceLib/Resx/ResUI.fr.resx index 542d9ef9..88a724fb 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.fr.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.fr.resx @@ -1599,4 +1599,26 @@ Auto add filtered configuration from subscription groups + + Certificate Pinning + + + Server certificate (PEM format, optional). Entering a certificate will pin it. +Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled. + + + Fetch Certificate + + + Fetch Certificate Chain + + + Please set a valid domain + + + Certificate not set + + + Certificate set + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.hu.resx b/v2rayN/ServiceLib/Resx/ResUI.hu.resx index e61f279e..ce57a3ba 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.hu.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.hu.resx @@ -1602,4 +1602,26 @@ Auto add filtered configuration from subscription groups + + Certificate Pinning + + + Server certificate (PEM format, optional). Entering a certificate will pin it. +Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled. + + + Fetch Certificate + + + Fetch Certificate Chain + + + Please set a valid domain + + + Certificate not set + + + Certificate set + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.resx b/v2rayN/ServiceLib/Resx/ResUI.resx index f1123407..03bd9660 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.resx @@ -1602,4 +1602,26 @@ Auto add filtered configuration from subscription groups + + Certificate Pinning + + + Server certificate (PEM format, optional). Entering a certificate will pin it. +Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled. + + + Fetch Certificate + + + Fetch Certificate Chain + + + Please set a valid domain + + + Certificate not set + + + Certificate set + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.ru.resx b/v2rayN/ServiceLib/Resx/ResUI.ru.resx index 14edb9b8..dd7604fc 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.ru.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.ru.resx @@ -1602,4 +1602,26 @@ Auto add filtered configuration from subscription groups + + Certificate Pinning + + + Server certificate (PEM format, optional). Entering a certificate will pin it. +Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled. + + + Fetch Certificate + + + Fetch Certificate Chain + + + Please set a valid domain + + + Certificate not set + + + Certificate set + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx index cfb89284..87443803 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hans.resx @@ -1599,4 +1599,26 @@ 自动从订阅分组添加过滤后的配置 + + 固定证书 + + + 服务器证书(PEM 格式,可选)。填入后将固定该证书。 +启用“跳过证书验证”时,请勿使用 '获取证书'。 + + + 获取证书 + + + 获取证书链 + + + 请设置有效的域名 + + + 证书未设置 + + + 证书已设置 + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx index 4a3822b4..c12f023b 100644 --- a/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx +++ b/v2rayN/ServiceLib/Resx/ResUI.zh-Hant.resx @@ -1599,4 +1599,26 @@ 自動從訂閱分組新增過濾後的配置 + + Certificate Pinning + + + Server certificate (PEM format, optional). Entering a certificate will pin it. +Do not use the "Fetch Certificate" button when "Allow Insecure" is enabled. + + + Fetch Certificate + + + Fetch Certificate Chain + + + Please set a valid domain + + + Certificate not set + + + Certificate set + \ No newline at end of file diff --git a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs index 84111652..1f1e68a0 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/Singbox/SingboxOutboundService.cs @@ -204,54 +204,6 @@ public partial class CoreConfigSingboxService return await Task.FromResult(null); } - private async Task GenGroupOutbound(ProfileItem node, SingboxConfig singboxConfig, string baseTagName = Global.ProxyTag, bool ignoreOriginChain = false) - { - try - { - if (!node.ConfigType.IsGroupType()) - { - return -1; - } - var hasCycle = ProfileGroupItemManager.HasCycle(node.IndexId); - if (hasCycle) - { - return -1; - } - - var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId); - if (childProfiles.Count <= 0) - { - return -1; - } - switch (node.ConfigType) - { - case EConfigType.PolicyGroup: - if (ignoreOriginChain) - { - await GenOutboundsList(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, baseTagName); - } - else - { - await GenOutboundsListWithChain(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, baseTagName); - } - - break; - - case EConfigType.ProxyChain: - await GenChainOutboundsList(childProfiles, singboxConfig, baseTagName); - break; - - default: - break; - } - } - catch (Exception ex) - { - Logging.SaveLog(_tag, ex); - } - return await Task.FromResult(0); - } - private async Task GenOutboundMux(ProfileItem node, Outbound4Sbox outbound) { try @@ -280,7 +232,7 @@ public partial class CoreConfigSingboxService { try { - if (node.StreamSecurity == Global.StreamSecurityReality || node.StreamSecurity == Global.StreamSecurity) + if (node.StreamSecurity is Global.StreamSecurityReality or Global.StreamSecurity) { var server_name = string.Empty; if (node.Sni.IsNotEmpty()) @@ -307,7 +259,18 @@ public partial class CoreConfigSingboxService fingerprint = node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : node.Fingerprint }; } - if (node.StreamSecurity == Global.StreamSecurityReality) + if (node.StreamSecurity == Global.StreamSecurity) + { + var certs = node.Cert + ?.Split("-----END CERTIFICATE-----", StringSplitOptions.RemoveEmptyEntries) + .Select(s => s.TrimEx()) + .Where(s => !s.IsNullOrEmpty()) + .Select(s => s + "\n-----END CERTIFICATE-----") + .Select(s => s.Replace("\r\n", "\n")) + .ToList() ?? new(); + tls.certificate = certs.Count > 0 ? certs : null; + } + else { tls.reality = new Reality4Sbox() { @@ -404,6 +367,54 @@ public partial class CoreConfigSingboxService return await Task.FromResult(0); } + private async Task GenGroupOutbound(ProfileItem node, SingboxConfig singboxConfig, string baseTagName = Global.ProxyTag, bool ignoreOriginChain = false) + { + try + { + if (!node.ConfigType.IsGroupType()) + { + return -1; + } + var hasCycle = ProfileGroupItemManager.HasCycle(node.IndexId); + if (hasCycle) + { + return -1; + } + + var (childProfiles, profileGroupItem) = await ProfileGroupItemManager.GetChildProfileItems(node.IndexId); + if (childProfiles.Count <= 0) + { + return -1; + } + switch (node.ConfigType) + { + case EConfigType.PolicyGroup: + if (ignoreOriginChain) + { + await GenOutboundsList(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, baseTagName); + } + else + { + await GenOutboundsListWithChain(childProfiles, singboxConfig, profileGroupItem.MultipleLoad, baseTagName); + } + + break; + + case EConfigType.ProxyChain: + await GenChainOutboundsList(childProfiles, singboxConfig, baseTagName); + break; + + default: + break; + } + } + catch (Exception ex) + { + Logging.SaveLog(_tag, ex); + } + return await Task.FromResult(0); + } + private async Task GenMoreOutbounds(ProfileItem node, SingboxConfig singboxConfig) { if (node.Subid.IsNullOrEmpty()) diff --git a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs index 39338c77..46169c37 100644 --- a/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs +++ b/v2rayN/ServiceLib/Services/CoreConfig/V2ray/V2rayOutboundService.cs @@ -245,6 +245,13 @@ public partial class CoreConfigV2rayService var host = node.RequestHost.TrimEx(); var path = node.Path.TrimEx(); var sni = node.Sni.TrimEx(); + var certs = node.Cert + ?.Split("-----END CERTIFICATE-----", StringSplitOptions.RemoveEmptyEntries) + .Select(s => s.TrimEx()) + .Where(s => !s.IsNullOrEmpty()) + .Select(s => s + "\n-----END CERTIFICATE-----") + .Select(s => s.Replace("\r\n", "\n")) + .ToList() ?? new(); var useragent = ""; if (!_config.CoreBasicItem.DefUserAgent.IsNullOrEmpty()) { @@ -277,6 +284,21 @@ public partial class CoreConfigV2rayService { tlsSettings.serverName = Utils.String2List(host)?.First(); } + if (certs.Count > 0) + { + var certsettings = new List(); + foreach (var cert in certs) + { + var certPerLine = cert.Split("\n").ToList(); + certsettings.Add(new CertificateSettings4Ray + { + certificate = certPerLine, + usage = "verify", + }); + } + tlsSettings.certificates = certsettings; + tlsSettings.disableSystemRoot = true; + } streamSettings.tlsSettings = tlsSettings; } diff --git a/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs b/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs index addd5bcc..121bf5da 100644 --- a/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/AddServerViewModel.cs @@ -2,12 +2,22 @@ namespace ServiceLib.ViewModels; public class AddServerViewModel : MyReactiveObject { + private string _certError = string.Empty; + [Reactive] public ProfileItem SelectedSource { get; set; } [Reactive] public string? CoreType { get; set; } + [Reactive] + public string Cert { get; set; } + + [Reactive] + public string CertTip { get; set; } + + public ReactiveCommand FetchCertCmd { get; } + public ReactiveCommand FetchCertChainCmd { get; } public ReactiveCommand SaveCmd { get; } public AddServerViewModel(ProfileItem profileItem, Func>? updateView) @@ -15,11 +25,22 @@ public class AddServerViewModel : MyReactiveObject _config = AppManager.Instance.Config; _updateView = updateView; + FetchCertCmd = ReactiveCommand.CreateFromTask(async () => + { + await FetchCert(); + }); + FetchCertChainCmd = ReactiveCommand.CreateFromTask(async () => + { + await FetchCertChain(); + }); SaveCmd = ReactiveCommand.CreateFromTask(async () => { await SaveServerAsync(); }); + this.WhenAnyValue(x => x.Cert) + .Subscribe(_ => UpdateCertTip()); + if (profileItem.IndexId.IsNullOrEmpty()) { profileItem.Network = Global.DefaultNetwork; @@ -33,6 +54,7 @@ public class AddServerViewModel : MyReactiveObject SelectedSource = JsonUtils.DeepCopy(profileItem); } CoreType = SelectedSource?.CoreType?.ToString(); + Cert = SelectedSource?.Cert?.ToString() ?? string.Empty; } private async Task SaveServerAsync() @@ -77,6 +99,7 @@ public class AddServerViewModel : MyReactiveObject } } SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? null : (ECoreType)Enum.Parse(typeof(ECoreType), CoreType); + SelectedSource.Cert = Cert.IsNullOrEmpty() ? null : Cert; if (await ConfigHandler.AddServer(_config, SelectedSource) == 0) { @@ -88,4 +111,72 @@ public class AddServerViewModel : MyReactiveObject NoticeManager.Instance.Enqueue(ResUI.OperationFailed); } } + + private void UpdateCertTip() + { + CertTip = _certError.IsNullOrEmpty() + ? (Cert.IsNullOrEmpty() ? ResUI.CertNotSet : ResUI.CertSet) + : _certError; + } + + private async Task FetchCert() + { + if (SelectedSource.StreamSecurity != Global.StreamSecurity) + { + return; + } + var domain = SelectedSource.Address; + var serverName = SelectedSource.Sni; + if (serverName.IsNullOrEmpty()) + { + serverName = SelectedSource.RequestHost; + } + if (serverName.IsNullOrEmpty()) + { + serverName = SelectedSource.Address; + } + if (!Utils.IsDomain(serverName)) + { + _certError = ResUI.ServerNameMustBeValidDomain; + UpdateCertTip(); + _certError = string.Empty; + return; + } + if (SelectedSource.Port > 0) + { + domain += $":{SelectedSource.Port}"; + } + Cert = await CertPemManager.Instance.GetCertPemAsync(domain, serverName); + } + + private async Task FetchCertChain() + { + if (SelectedSource.StreamSecurity != Global.StreamSecurity) + { + return; + } + var domain = SelectedSource.Address; + var serverName = SelectedSource.Sni; + if (serverName.IsNullOrEmpty()) + { + serverName = SelectedSource.RequestHost; + } + if (serverName.IsNullOrEmpty()) + { + serverName = SelectedSource.Address; + } + if (!Utils.IsDomain(serverName)) + { + _certError = ResUI.ServerNameMustBeValidDomain; + UpdateCertTip(); + _certError = string.Empty; + return; + } + if (SelectedSource.Port > 0) + { + domain += $":{SelectedSource.Port}"; + } + var certs = await CertPemManager.Instance.GetCertChainPemAsync(domain, serverName); + Cert = string.Join("\n", certs); + } } diff --git a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml index 7e3dda10..f01da56d 100644 --- a/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml +++ b/v2rayN/v2rayN.Desktop/Views/AddServerWindow.axaml @@ -607,10 +607,10 @@ + this.Bind(ViewModel, vm => vm.SelectedSource.AllowInsecure, v => v.cmbAllowInsecure.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint.SelectedValue).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Alpn, v => v.cmbAlpn.SelectedValue).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.CertTip, v => v.labCertPinning.Text).DisposeWith(disposables); + this.Bind(ViewModel, vm => vm.Cert, v => v.txtCert.Text).DisposeWith(disposables); //reality this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI2.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint2.SelectedValue).DisposeWith(disposables); @@ -193,6 +195,8 @@ public partial class AddServerWindow : WindowBase this.Bind(ViewModel, vm => vm.SelectedSource.SpiderX, v => v.txtSpiderX.Text).DisposeWith(disposables); this.Bind(ViewModel, vm => vm.SelectedSource.Mldsa65Verify, v => v.txtMldsa65Verify.Text).DisposeWith(disposables); + this.BindCommand(ViewModel, vm => vm.FetchCertCmd, v => v.btnFetchCert).DisposeWith(disposables); + this.BindCommand(ViewModel, vm => vm.FetchCertChainCmd, v => v.btnFetchCertChain).DisposeWith(disposables); this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables); }); diff --git a/v2rayN/v2rayN/Views/AddServerWindow.xaml b/v2rayN/v2rayN/Views/AddServerWindow.xaml index b293a115..699e4911 100644 --- a/v2rayN/v2rayN/Views/AddServerWindow.xaml +++ b/v2rayN/v2rayN/Views/AddServerWindow.xaml @@ -928,6 +928,7 @@ + @@ -995,6 +996,63 @@ Width="200" Margin="{StaticResource Margin4}" Style="{StaticResource DefComboBox}" /> + + + + + + + + +