From bc06a5dc210877b64fd994b2a4e99b807bfd5ef8 Mon Sep 17 00:00:00 2001 From: DHR60 <192860629+DHR60@users.noreply.github.com> Date: Fri, 14 Aug 2026 02:20:58 +0000 Subject: [PATCH] Fix (#9951) * Fix * Fix * Add HandleSafe --- v2rayN/ServiceLib/Common/Extension.cs | 21 +++++++++++ .../ViewModels/AddServer2ViewModel.cs | 2 +- .../ViewModels/MainWindowViewModel.cs | 37 ++++++++++++------- v2rayN/ServiceLib/ViewModels/MsgViewModel.cs | 2 +- .../ViewModels/ProfilesSelectViewModel.cs | 8 +--- .../ViewModels/ProfilesViewModel.cs | 36 ++++++------------ .../ViewModels/RoutingRuleSettingViewModel.cs | 10 ++--- .../ViewModels/RoutingSettingViewModel.cs | 2 +- .../ViewModels/StatusBarViewModel.cs | 15 ++------ .../ViewModels/SubSettingViewModel.cs | 4 +- 10 files changed, 72 insertions(+), 65 deletions(-) diff --git a/v2rayN/ServiceLib/Common/Extension.cs b/v2rayN/ServiceLib/Common/Extension.cs index c9e46d31..a7e705db 100644 --- a/v2rayN/ServiceLib/Common/Extension.cs +++ b/v2rayN/ServiceLib/Common/Extension.cs @@ -134,4 +134,25 @@ public static class Extension .Replace("\r", replacement) .Replace("\n", replacement); } + + public static async Task HandleSafe( + this Interaction interaction, + TInput input, + TOutput defaultValue = default!) + { + try + { + return await interaction.Handle(input); + } + catch (UnhandledInteractionException ex) + { + Logging.SaveLog($"Unhandled interaction exception for input: {input}", ex); + return defaultValue; + } + catch (Exception ex) + { + Logging.SaveLog($"Exception occurred while handling interaction for input: {input}", ex); + return defaultValue; + } + } } diff --git a/v2rayN/ServiceLib/ViewModels/AddServer2ViewModel.cs b/v2rayN/ServiceLib/ViewModels/AddServer2ViewModel.cs index 5a35eeea..6d08dbab 100644 --- a/v2rayN/ServiceLib/ViewModels/AddServer2ViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/AddServer2ViewModel.cs @@ -26,7 +26,7 @@ public partial class AddServer2ViewModel : MyReactiveObject, ICloseable BrowseServerCmd = ReactiveCommand.CreateFromTask(async () => { - var fileName = await BrowseConfigFileInteraction.Handle(RxVoid.Default); + var fileName = await BrowseConfigFileInteraction.HandleSafe(RxVoid.Default); if (fileName.IsNullOrEmpty()) { return; diff --git a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs index d167a02a..a6914aa8 100644 --- a/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/MainWindowViewModel.cs @@ -85,8 +85,6 @@ public partial class MainWindowViewModel : MyReactiveObject #endregion Menu - private readonly SynchronizationContext _uiContext = SynchronizationContext.Current; - #region Init public MainWindowViewModel() @@ -302,7 +300,7 @@ public partial class MainWindowViewModel : MyReactiveObject .ObserveOn(RxSchedulers.MainThreadScheduler) .Subscribe(async blShow => { - await ShowHideWindowInteraction.Handle(blShow); + await ShowHideWindowInteraction.HandleSafe(blShow); }); StatusBarViewModel.SetDefaultServerRequested @@ -413,14 +411,25 @@ public partial class MainWindowViewModel : MyReactiveObject private async Task RefreshServersDispatcherAsync() { //await Observable.Start(async () => await RefreshServers(), RxSchedulers.MainThreadScheduler); - _uiContext?.Post(_ => _ = RefreshServers(), null); + await Signal.FromAsync(async () => + { + await RefreshServers(); + return RxVoid.Default; + }) + .SubscribeOn(RxSchedulers.MainThreadScheduler) + .ToTask(); } private async Task RefreshSubscriptions() { //await Observable.Start(async () => await ProfilesViewModel.RefreshSubscriptions(), RxSchedulers.MainThreadScheduler); - - _uiContext?.Post(_ => _ = ProfilesViewModel.RefreshSubscriptions(), null); + await Signal.FromAsync(async () => + { + await ProfilesViewModel.RefreshSubscriptions(); + return RxVoid.Default; + }) + .SubscribeOn(RxSchedulers.MainThreadScheduler) + .ToTask(); } #endregion Servers && Groups @@ -467,7 +476,7 @@ public partial class MainWindowViewModel : MyReactiveObject var stringData = clipboardData; if (clipboardData == null) { - var result = await ReadTextFromClipboardInteraction.Handle(RxVoid.Default); + var result = await ReadTextFromClipboardInteraction.HandleSafe(RxVoid.Default); if (result.IsNullOrEmpty()) { NoticeManager.Instance.Enqueue(ResUI.OperationFailed); @@ -490,7 +499,7 @@ public partial class MainWindowViewModel : MyReactiveObject public async Task AddServerViaScanAsync() { - var result = await ScanScreenInteraction.Handle(RxVoid.Default); + var result = await ScanScreenInteraction.HandleSafe(RxVoid.Default); await ScanScreenResult(result); } @@ -502,7 +511,7 @@ public partial class MainWindowViewModel : MyReactiveObject public async Task AddServerViaImageAsync() { - var imageFileName = await BrowseImageFileInteraction.Handle(RxVoid.Default); + var imageFileName = await BrowseImageFileInteraction.HandleSafe(RxVoid.Default); await AddScanResultAsync(imageFileName); } @@ -691,10 +700,12 @@ public partial class MainWindowViewModel : MyReactiveObject //{ // await ClashProxiesViewModel.ProxiesReload(); //}, RxSchedulers.MainThreadScheduler); - RxSchedulers.MainThreadScheduler.Schedule(async () => - { - await ClashProxiesViewModel.ProxiesReload(); - }); + await Signal.FromAsync(async () => + { + await ClashProxiesViewModel.ProxiesReload(); + return RxVoid.Default; + }).SubscribeOn(RxSchedulers.MainThreadScheduler) + .ToTask(); } ReloadResult(showClashUI); diff --git a/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs b/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs index 5dd3c3c8..eaa336dc 100644 --- a/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs @@ -74,7 +74,7 @@ public partial class MsgViewModel : MyReactiveObject { try { - await DispatcherShowMsgInteraction.Handle(sb.ToString()); + await DispatcherShowMsgInteraction.HandleSafe(sb.ToString()); } catch (Exception) { diff --git a/v2rayN/ServiceLib/ViewModels/ProfilesSelectViewModel.cs b/v2rayN/ServiceLib/ViewModels/ProfilesSelectViewModel.cs index 761523f2..f2777d65 100644 --- a/v2rayN/ServiceLib/ViewModels/ProfilesSelectViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/ProfilesSelectViewModel.cs @@ -138,13 +138,7 @@ public partial class ProfilesSelectViewModel : MyReactiveObject, ICloseable await RefreshServers(); - try - { - await ProfilesFocusInteraction.Handle(RxVoid.Default); - } - catch (UnhandledInteractionException) - { - } + await ProfilesFocusInteraction.HandleSafe(RxVoid.Default); } private async Task ServerFilterChanged(bool c) diff --git a/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs b/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs index 42b17c35..a273aafd 100644 --- a/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/ProfilesViewModel.cs @@ -345,13 +345,7 @@ public partial class ProfilesViewModel : MyReactiveObject await RefreshServers(); - try - { - await ProfilesFocusInteraction.Handle(RxVoid.Default); - } - catch (UnhandledInteractionException) - { - } + await ProfilesFocusInteraction.HandleSafe(RxVoid.Default); } private async Task ServerFilterChanged(bool c) @@ -395,13 +389,7 @@ public partial class ProfilesViewModel : MyReactiveObject SelectedProfile = selected ?? lstModel.First(); } - try - { - await DispatcherRefreshServersBizInteraction.Handle(RxVoid.Default); - } - catch (UnhandledInteractionException) - { - } + await DispatcherRefreshServersBizInteraction.HandleSafe(RxVoid.Default); } public async Task RefreshSubscriptions() @@ -419,7 +407,7 @@ public partial class ProfilesViewModel : MyReactiveObject public async Task AdjustMainLvColWidth() { - await AdjustMainLvColWidthInteraction.Handle(RxVoid.Default); + await AdjustMainLvColWidthInteraction.HandleSafe(RxVoid.Default); } private async Task?> GetProfileItemsEx(string subid, string filter) @@ -535,7 +523,7 @@ public partial class ProfilesViewModel : MyReactiveObject { return; } - if (await ShowYesNoInteraction.Handle(ResUI.RemoveServer) == false) + if (await ShowYesNoInteraction.HandleSafe(ResUI.RemoveServer) == false) { return; } @@ -556,7 +544,7 @@ public partial class ProfilesViewModel : MyReactiveObject private async Task RemoveDuplicateServer() { - if (await ShowYesNoInteraction.Handle(ResUI.RemoveServer) == false) + if (await ShowYesNoInteraction.HandleSafe(ResUI.RemoveServer) == false) { return; } @@ -631,7 +619,7 @@ public partial class ProfilesViewModel : MyReactiveObject return; } - await ShareServerInteraction.Handle(url); + await ShareServerInteraction.HandleSafe(url); } private async Task GenGroupAllServer() @@ -799,13 +787,13 @@ public partial class ProfilesViewModel : MyReactiveObject } else { - await SetClipboardDataInteraction.Handle((string)result.Data); + await SetClipboardDataInteraction.HandleSafe((string)result.Data); NoticeManager.Instance.SendMessage(ResUI.OperationSuccess); } } else { - await SaveFileDialogInteraction.Handle(item); + await SaveFileDialogInteraction.HandleSafe(item); } } @@ -854,11 +842,11 @@ public partial class ProfilesViewModel : MyReactiveObject { if (blEncode) { - await SetClipboardDataInteraction.Handle(Utils.Base64Encode(sb.ToString())); + await SetClipboardDataInteraction.HandleSafe(Utils.Base64Encode(sb.ToString())); } else { - await SetClipboardDataInteraction.Handle(sb.ToString()); + await SetClipboardDataInteraction.HandleSafe(sb.ToString()); } NoticeManager.Instance.SendMessage(ResUI.BatchExportURLSuccessfully); } @@ -881,7 +869,7 @@ public partial class ProfilesViewModel : MyReactiveObject if (!result.IsNullOrEmpty()) { - await SetClipboardDataInteraction.Handle(result); + await SetClipboardDataInteraction.HandleSafe(result); NoticeManager.Instance.SendMessage(ResUI.BatchExportURLSuccessfully); } else @@ -925,7 +913,7 @@ public partial class ProfilesViewModel : MyReactiveObject return; } - if (await ShowYesNoInteraction.Handle(ResUI.RemoveServer) == false) + if (await ShowYesNoInteraction.HandleSafe(ResUI.RemoveServer) == false) { return; } diff --git a/v2rayN/ServiceLib/ViewModels/RoutingRuleSettingViewModel.cs b/v2rayN/ServiceLib/ViewModels/RoutingRuleSettingViewModel.cs index 48ae8858..3b4f1104 100644 --- a/v2rayN/ServiceLib/ViewModels/RoutingRuleSettingViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/RoutingRuleSettingViewModel.cs @@ -48,7 +48,7 @@ public partial class RoutingRuleSettingViewModel : MyReactiveObject, ICloseable }); ImportRulesFromFileCmd = ReactiveCommand.CreateFromTask(async () => { - var fileName = await BrowseRulesFileInteraction.Handle(RxVoid.Default); + var fileName = await BrowseRulesFileInteraction.HandleSafe(RxVoid.Default); await ImportRulesFromFileAsync(fileName); }); ImportRulesFromClipboardCmd = ReactiveCommand.CreateFromTask(async () => @@ -156,7 +156,7 @@ public partial class RoutingRuleSettingViewModel : MyReactiveObject, ICloseable NoticeManager.Instance.Enqueue(ResUI.PleaseSelectRules); return; } - if (await ShowYesNoInteraction.Handle(ResUI.RemoveServer) == false) + if (await ShowYesNoInteraction.HandleSafe(ResUI.RemoveServer) == false) { return; } @@ -199,7 +199,7 @@ public partial class RoutingRuleSettingViewModel : MyReactiveObject, ICloseable DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, PropertyNamingPolicy = JsonNamingPolicy.CamelCase, }; - await SetClipboardDataInteraction.Handle(JsonUtils.Serialize(lst, options)); + await SetClipboardDataInteraction.HandleSafe(JsonUtils.Serialize(lst, options)); } } @@ -277,7 +277,7 @@ public partial class RoutingRuleSettingViewModel : MyReactiveObject, ICloseable var stringData = clipboardData; if (clipboardData == null) { - var result = await ReadTextFromClipboardInteraction.Handle(RxVoid.Default); + var result = await ReadTextFromClipboardInteraction.HandleSafe(RxVoid.Default); if (result.IsNullOrEmpty()) { NoticeManager.Instance.Enqueue(ResUI.OperationFailed); @@ -315,7 +315,7 @@ public partial class RoutingRuleSettingViewModel : MyReactiveObject, ICloseable private async Task AddBatchRoutingRulesAsync(RoutingItem routingItem, string? clipboardData) { var blReplace = false; - if (await ShowYesNoInteraction.Handle(ResUI.AddBatchRoutingRulesYesNo) == false) + if (await ShowYesNoInteraction.HandleSafe(ResUI.AddBatchRoutingRulesYesNo) == false) { blReplace = true; } diff --git a/v2rayN/ServiceLib/ViewModels/RoutingSettingViewModel.cs b/v2rayN/ServiceLib/ViewModels/RoutingSettingViewModel.cs index d5f26020..f7ad92a8 100644 --- a/v2rayN/ServiceLib/ViewModels/RoutingSettingViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/RoutingSettingViewModel.cs @@ -147,7 +147,7 @@ public partial class RoutingSettingViewModel : MyReactiveObject NoticeManager.Instance.Enqueue(ResUI.PleaseSelectRules); return; } - if (await ShowYesNoInteraction.Handle(ResUI.RemoveServer) == false) + if (await ShowYesNoInteraction.HandleSafe(ResUI.RemoveServer) == false) { return; } diff --git a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs index ef675b05..39c0dfe5 100644 --- a/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/StatusBarViewModel.cs @@ -243,7 +243,7 @@ public partial class StatusBarViewModel : MyReactiveObject sb.AppendLine($"{cmd} HTTPS_PROXY={Global.HttpProtocol}{address}"); sb.AppendLine($"{cmd} ALL_PROXY={Global.Socks5Protocol}{address}"); - await SetClipboardDataInteraction.Handle(sb.ToString()); + await SetClipboardDataInteraction.HandleSafe(sb.ToString()); } private async Task AddServerViaClipboard() @@ -389,14 +389,7 @@ public partial class StatusBarViewModel : MyReactiveObject if (blChange) { - try - { - await DispatcherRefreshIconInteraction.Handle(RxVoid.Default); - } - catch (UnhandledInteractionException) - { - // Ignore - } + await DispatcherRefreshIconInteraction.HandleSafe(RxVoid.Default); } } @@ -432,7 +425,7 @@ public partial class StatusBarViewModel : MyReactiveObject { NoticeManager.Instance.SendMessageEx(ResUI.TipChangeRouting); ReloadRequested.Publish(); - await DispatcherRefreshIconInteraction.Handle(RxVoid.Default); + await DispatcherRefreshIconInteraction.HandleSafe(RxVoid.Default); } } @@ -469,7 +462,7 @@ public partial class StatusBarViewModel : MyReactiveObject } else { - var password = await PasswordInputInteraction.Handle(RxVoid.Default); + var password = await PasswordInputInteraction.HandleSafe(RxVoid.Default); if (password.IsNullOrEmpty()) { _config.TunModeItem.EnableTun = false; diff --git a/v2rayN/ServiceLib/ViewModels/SubSettingViewModel.cs b/v2rayN/ServiceLib/ViewModels/SubSettingViewModel.cs index 5c3ef56d..8ba8f00f 100644 --- a/v2rayN/ServiceLib/ViewModels/SubSettingViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/SubSettingViewModel.cs @@ -40,7 +40,7 @@ public partial class SubSettingViewModel : MyReactiveObject }, canEditRemove); SubShareCmd = ReactiveCommand.CreateFromTask(async () => { - await ShareSubInteraction.Handle(SelectedSource?.Url); + await ShareSubInteraction.HandleSafe(SelectedSource?.Url); }, canEditRemove); _ = Init(); @@ -84,7 +84,7 @@ public partial class SubSettingViewModel : MyReactiveObject private async Task DeleteSubAsync() { - if (await ShowYesNoInteraction.Handle(ResUI.RemoveServer) == false) + if (await ShowYesNoInteraction.HandleSafe(ResUI.RemoveServer) == false) { return; }