From 324b3eb08171b19146c181ed4a2f781cd79757f1 Mon Sep 17 00:00:00 2001 From: Jack251970 <1160210343@qq.com> Date: Mon, 21 Jul 2025 17:33:47 +0800 Subject: [PATCH] Do not call interface methods for init failed plugins --- Flow.Launcher.Core/Plugin/PluginManager.cs | 39 ++++++++++++------- .../SettingsPanePluginsViewModel.cs | 3 +- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/Flow.Launcher.Core/Plugin/PluginManager.cs b/Flow.Launcher.Core/Plugin/PluginManager.cs index 7f9a4a539..f69a4ba0a 100644 --- a/Flow.Launcher.Core/Plugin/PluginManager.cs +++ b/Flow.Launcher.Core/Plugin/PluginManager.cs @@ -59,7 +59,7 @@ namespace Flow.Launcher.Core.Plugin /// public static void Save() { - foreach (var pluginPair in GetAllInitializedPlugins()) + foreach (var pluginPair in GetAllInitializedPlugins(false)) { var savable = pluginPair.Plugin as ISavable; try @@ -78,7 +78,8 @@ namespace Flow.Launcher.Core.Plugin public static async ValueTask DisposePluginsAsync() { - foreach (var pluginPair in GetAllInitializedPlugins()) + // Still call dispose for all plugins even if initialization failed, so that we can clean up resources + foreach (var pluginPair in GetAllInitializedPlugins(true)) { await DisposePluginAsync(pluginPair); } @@ -106,7 +107,7 @@ namespace Flow.Launcher.Core.Plugin public static async Task ReloadDataAsync() { - await Task.WhenAll([.. GetAllInitializedPlugins().Select(plugin => plugin.Plugin switch + await Task.WhenAll([.. GetAllInitializedPlugins(false).Select(plugin => plugin.Plugin switch { IReloadable p => Task.Run(p.ReloadData), IAsyncReloadable p => p.ReloadDataAsync(), @@ -120,7 +121,7 @@ namespace Flow.Launcher.Core.Plugin public static async Task OpenExternalPreviewAsync(string path, bool sendFailToast = true) { - await Task.WhenAll([.. GetAllInitializedPlugins().Select(plugin => plugin.Plugin switch + await Task.WhenAll([.. GetAllInitializedPlugins(false).Select(plugin => plugin.Plugin switch { IAsyncExternalPreview p => p.OpenPreviewAsync(path, sendFailToast), _ => Task.CompletedTask, @@ -129,7 +130,7 @@ namespace Flow.Launcher.Core.Plugin public static async Task CloseExternalPreviewAsync() { - await Task.WhenAll([.. GetAllInitializedPlugins().Select(plugin => plugin.Plugin switch + await Task.WhenAll([.. GetAllInitializedPlugins(false).Select(plugin => plugin.Plugin switch { IAsyncExternalPreview p => p.ClosePreviewAsync(), _ => Task.CompletedTask, @@ -138,7 +139,7 @@ namespace Flow.Launcher.Core.Plugin public static async Task SwitchExternalPreviewAsync(string path, bool sendFailToast = true) { - await Task.WhenAll([.. GetAllInitializedPlugins().Select(plugin => plugin.Plugin switch + await Task.WhenAll([.. GetAllInitializedPlugins(false).Select(plugin => plugin.Plugin switch { IAsyncExternalPreview p => p.SwitchPreviewAsync(path, sendFailToast), _ => Task.CompletedTask, @@ -523,9 +524,17 @@ namespace Flow.Launcher.Core.Plugin return [.. _allLoadedPlugins]; } - public static List GetAllInitializedPlugins() + public static List GetAllInitializedPlugins(bool containFailed) { - return [.. _allInitializedPlugins.Values]; + if (containFailed) + { + return [.. _allInitializedPlugins.Values]; + } + else + { + return [.. _allInitializedPlugins.Values + .Where(p => !_initFailedPlugins.ContainsKey(p.Metadata.ID))]; + } } public static List GetGlobalPlugins() @@ -695,9 +704,10 @@ namespace Flow.Launcher.Core.Plugin if (!Version.TryParse(newMetadata.Version, out var newVersion)) return true; // If version is not valid, we assume it is lesser than any existing version - return GetAllInitializedPlugins().Any(x => x.Metadata.ID == newMetadata.ID - && Version.TryParse(x.Metadata.Version, out var version) - && newVersion <= version); + // Get all plugins even if initialization failed so that we can check if the plugin with the same ID exists + return GetAllInitializedPlugins(true).Any(x => x.Metadata.ID == newMetadata.ID + && Version.TryParse(x.Metadata.Version, out var version) + && newVersion <= version); } #endregion @@ -839,7 +849,7 @@ namespace Flow.Launcher.Core.Plugin // If we want to remove plugin from AllPlugins, // we need to dispose them so that they can release file handles // which can help FL to delete the plugin settings & cache folders successfully - var pluginPairs = GetAllInitializedPlugins().Where(p => p.Metadata.ID == plugin.ID).ToList(); + var pluginPairs = GetAllInitializedPlugins(true).Where(p => p.Metadata.ID == plugin.ID).ToList(); foreach (var pluginPair in pluginPairs) { await DisposePluginAsync(pluginPair); @@ -885,11 +895,12 @@ namespace Flow.Launcher.Core.Plugin } Settings.RemovePluginSettings(plugin.ID); _allInitializedPlugins.TryRemove(plugin.ID, out var item); - _globalPlugins.TryRemove(plugin.ID, out var item1); + _initFailedPlugins.TryRemove(plugin.ID, out var item1); + _globalPlugins.TryRemove(plugin.ID, out var item2); var keysToRemove = _nonGlobalPlugins.Where(p => p.Value.Metadata.ID == plugin.ID).Select(p => p.Key).ToList(); foreach (var key in keysToRemove) { - _nonGlobalPlugins.Remove(key, out var item2); + _nonGlobalPlugins.Remove(key, out var item3); } } diff --git a/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginsViewModel.cs b/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginsViewModel.cs index 5254cdfde..b7723276e 100644 --- a/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginsViewModel.cs +++ b/Flow.Launcher/SettingPages/ViewModels/SettingsPanePluginsViewModel.cs @@ -116,7 +116,8 @@ public partial class SettingsPanePluginsViewModel : BaseModel private List? _pluginViewModels; // Get all initialized plugins and ignore those that are not initialized - public List PluginViewModels => _pluginViewModels ??= PluginManager.GetAllInitializedPlugins() + // Include those init failed plugins so that we can uninstall them + public List PluginViewModels => _pluginViewModels ??= PluginManager.GetAllInitializedPlugins(true) .OrderBy(plugin => plugin.Metadata.Disabled) .ThenBy(plugin => plugin.Metadata.Name) .Select(plugin => new PluginViewModel