mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Plugin Async ModelAdd Full Async model, including AsyncPlugin and AsyncReloadable
This commit is contained in:
parent
d60b873ef5
commit
3cd609377e
11 changed files with 210 additions and 84 deletions
|
|
@ -3,6 +3,7 @@ using System.Collections.Concurrent;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
|
|
@ -32,7 +33,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
/// <summary>
|
||||
/// Directories that will hold Flow Launcher plugin directory
|
||||
/// </summary>
|
||||
private static readonly string[] Directories = { Constant.PreinstalledDirectory, DataLocation.PluginsDirectory };
|
||||
private static readonly string[] Directories = {Constant.PreinstalledDirectory, DataLocation.PluginsDirectory};
|
||||
|
||||
private static void DeletePythonBinding()
|
||||
{
|
||||
|
|
@ -52,12 +53,19 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
}
|
||||
|
||||
public static void ReloadData()
|
||||
public static async Task ReloadData()
|
||||
{
|
||||
foreach(var plugin in AllPlugins)
|
||||
{
|
||||
var reloadablePlugin = plugin.Plugin as IReloadable;
|
||||
reloadablePlugin?.ReloadData();
|
||||
switch (plugin.Plugin)
|
||||
{
|
||||
case IReloadable p:
|
||||
p.ReloadData();
|
||||
break;
|
||||
case IAsyncReloadable p:
|
||||
await p.ReloadDataAsync();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -86,24 +94,50 @@ namespace Flow.Launcher.Core.Plugin
|
|||
/// Call initialize for all plugins
|
||||
/// </summary>
|
||||
/// <returns>return the list of failed to init plugins or null for none</returns>
|
||||
public static void InitializePlugins(IPublicAPI api)
|
||||
public static async Task InitializePlugins(IPublicAPI api)
|
||||
{
|
||||
API = api;
|
||||
var failedPlugins = new ConcurrentQueue<PluginPair>();
|
||||
Parallel.ForEach(AllPlugins, pair =>
|
||||
|
||||
var InitTasks = AllPlugins.Select(pair => Task.Run(async delegate
|
||||
{
|
||||
try
|
||||
{
|
||||
var milliseconds = Stopwatch.Debug($"|PluginManager.InitializePlugins|Init method time cost for <{pair.Metadata.Name}>", () =>
|
||||
long milliseconds;
|
||||
|
||||
switch (pair.Plugin)
|
||||
{
|
||||
pair.Plugin.Init(new PluginInitContext
|
||||
{
|
||||
CurrentPluginMetadata = pair.Metadata,
|
||||
API = API
|
||||
});
|
||||
});
|
||||
case IAsyncPlugin plugin:
|
||||
milliseconds = await Stopwatch.DebugAsync(
|
||||
$"|PluginManager.InitializePlugins|Init method time cost for <{pair.Metadata.Name}>",
|
||||
async delegate
|
||||
{
|
||||
await plugin.InitAsync(new PluginInitContext
|
||||
{
|
||||
CurrentPluginMetadata = pair.Metadata,
|
||||
API = API
|
||||
});
|
||||
});
|
||||
break;
|
||||
case IPlugin plugin:
|
||||
milliseconds = Stopwatch.Debug(
|
||||
$"|PluginManager.InitializePlugins|Init method time cost for <{pair.Metadata.Name}>",
|
||||
() =>
|
||||
{
|
||||
plugin.Init(new PluginInitContext
|
||||
{
|
||||
CurrentPluginMetadata = pair.Metadata,
|
||||
API = API
|
||||
});
|
||||
});
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException();
|
||||
}
|
||||
|
||||
pair.Metadata.InitTime += milliseconds;
|
||||
Log.Info($"|PluginManager.InitializePlugins|Total init cost for <{pair.Metadata.Name}> is <{pair.Metadata.InitTime}ms>");
|
||||
Log.Info(
|
||||
$"|PluginManager.InitializePlugins|Total init cost for <{pair.Metadata.Name}> is <{pair.Metadata.InitTime}ms>");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
@ -111,25 +145,33 @@ namespace Flow.Launcher.Core.Plugin
|
|||
pair.Metadata.Disabled = true;
|
||||
failedPlugins.Enqueue(pair);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
await Task.WhenAll(InitTasks);
|
||||
|
||||
_contextMenuPlugins = GetPluginsForInterface<IContextMenu>();
|
||||
foreach (var plugin in AllPlugins)
|
||||
{
|
||||
if (IsGlobalPlugin(plugin.Metadata))
|
||||
GlobalPlugins.Add(plugin);
|
||||
|
||||
// Plugins may have multiple ActionKeywords, eg. WebSearch
|
||||
plugin.Metadata.ActionKeywords
|
||||
.Where(x => x != Query.GlobalPluginWildcardSign)
|
||||
.ToList()
|
||||
.ForEach(x => NonGlobalPlugins[x] = plugin);
|
||||
foreach (var actionKeyword in plugin.Metadata.ActionKeywords)
|
||||
{
|
||||
switch (actionKeyword)
|
||||
{
|
||||
case Query.GlobalPluginWildcardSign:
|
||||
GlobalPlugins.Add(plugin);
|
||||
break;
|
||||
default:
|
||||
NonGlobalPlugins[actionKeyword] = plugin;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (failedPlugins.Any())
|
||||
{
|
||||
var failed = string.Join(",", failedPlugins.Select(x => x.Metadata.Name));
|
||||
API.ShowMsg($"Fail to Init Plugins", $"Plugins: {failed} - fail to load and would be disabled, please contact plugin creator for help", "", false);
|
||||
API.ShowMsg($"Fail to Init Plugins",
|
||||
$"Plugins: {failed} - fail to load and would be disabled, please contact plugin creator for help",
|
||||
"", false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -138,7 +180,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
if (NonGlobalPlugins.ContainsKey(query.ActionKeyword))
|
||||
{
|
||||
var plugin = NonGlobalPlugins[query.ActionKeyword];
|
||||
return new List<PluginPair> { plugin };
|
||||
return new List<PluginPair> {plugin};
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -146,25 +188,42 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
}
|
||||
|
||||
public static List<Result> QueryForPlugin(PluginPair pair, Query query)
|
||||
public static async Task<List<Result>> QueryForPlugin(PluginPair pair, Query query, CancellationToken token)
|
||||
{
|
||||
var results = new List<Result>();
|
||||
try
|
||||
{
|
||||
var metadata = pair.Metadata;
|
||||
var milliseconds = Stopwatch.Debug($"|PluginManager.QueryForPlugin|Cost for {metadata.Name}", () =>
|
||||
{
|
||||
results = pair.Plugin.Query(query) ?? new List<Result>();
|
||||
UpdatePluginMetadata(results, metadata, query);
|
||||
});
|
||||
var milliseconds = await Stopwatch.DebugAsync($"|PluginManager.QueryForPlugin|Cost for {metadata.Name}",
|
||||
async () =>
|
||||
{
|
||||
switch (pair.Plugin)
|
||||
{
|
||||
case IAsyncPlugin plugin:
|
||||
results = await plugin.QueryAsync(query, token).ConfigureAwait(false) ??
|
||||
new List<Result>();
|
||||
UpdatePluginMetadata(results, metadata, query);
|
||||
break;
|
||||
case IPlugin plugin:
|
||||
results = await Task.Run(() => plugin.Query(query), token).ConfigureAwait(false) ??
|
||||
new List<Result>();
|
||||
UpdatePluginMetadata(results, metadata, query);
|
||||
break;
|
||||
}
|
||||
});
|
||||
metadata.QueryCount += 1;
|
||||
metadata.AvgQueryTime = metadata.QueryCount == 1 ? milliseconds : (metadata.AvgQueryTime + milliseconds) / 2;
|
||||
metadata.AvgQueryTime =
|
||||
metadata.QueryCount == 1 ? milliseconds : (metadata.AvgQueryTime + milliseconds) / 2;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|PluginManager.QueryForPlugin|Exception for plugin <{pair.Metadata.Name}> when query <{query}>", e);
|
||||
Log.Exception(
|
||||
$"|PluginManager.QueryForPlugin|Exception for plugin <{pair.Metadata.Name}> when query <{query}>",
|
||||
e);
|
||||
}
|
||||
return results;
|
||||
|
||||
// null will be fine since the results will only be added into queue if the token hasn't been cancelled
|
||||
return token.IsCancellationRequested ? results = null : results;
|
||||
}
|
||||
|
||||
public static void UpdatePluginMetadata(List<Result> results, PluginMetadata metadata, Query query)
|
||||
|
|
@ -182,11 +241,6 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
}
|
||||
|
||||
private static bool IsGlobalPlugin(PluginMetadata metadata)
|
||||
{
|
||||
return metadata.ActionKeywords.Contains(Query.GlobalPluginWildcardSign);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// get specified plugin, return null if not found
|
||||
/// </summary>
|
||||
|
|
@ -208,7 +262,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
var pluginPair = _contextMenuPlugins.FirstOrDefault(o => o.Metadata.ID == result.PluginID);
|
||||
if (pluginPair != null)
|
||||
{
|
||||
var plugin = (IContextMenu)pluginPair.Plugin;
|
||||
var plugin = (IContextMenu) pluginPair.Plugin;
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -222,16 +276,19 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|PluginManager.GetContextMenusForPlugin|Can't load context menus for plugin <{pluginPair.Metadata.Name}>", e);
|
||||
Log.Exception(
|
||||
$"|PluginManager.GetContextMenusForPlugin|Can't load context menus for plugin <{pluginPair.Metadata.Name}>",
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public static bool ActionKeywordRegistered(string actionKeyword)
|
||||
{
|
||||
return actionKeyword != Query.GlobalPluginWildcardSign
|
||||
&& NonGlobalPlugins.ContainsKey(actionKeyword);
|
||||
&& NonGlobalPlugins.ContainsKey(actionKeyword);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -249,6 +306,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
{
|
||||
NonGlobalPlugins[newActionKeyword] = plugin;
|
||||
}
|
||||
|
||||
plugin.Metadata.ActionKeywords.Add(newActionKeyword);
|
||||
}
|
||||
|
||||
|
|
@ -262,9 +320,9 @@ namespace Flow.Launcher.Core.Plugin
|
|||
if (oldActionkeyword == Query.GlobalPluginWildcardSign
|
||||
&& // Plugins may have multiple ActionKeywords that are global, eg. WebSearch
|
||||
plugin.Metadata.ActionKeywords
|
||||
.Where(x => x == Query.GlobalPluginWildcardSign)
|
||||
.ToList()
|
||||
.Count == 1)
|
||||
.Where(x => x == Query.GlobalPluginWildcardSign)
|
||||
.ToList()
|
||||
.Count == 1)
|
||||
{
|
||||
GlobalPlugins.Remove(plugin);
|
||||
}
|
||||
|
|
@ -285,4 +343,4 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure
|
||||
|
|
@ -22,7 +23,22 @@ namespace Flow.Launcher.Infrastructure
|
|||
Log.Debug(info);
|
||||
return milliseconds;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This stopwatch will appear only in Debug mode
|
||||
/// </summary>
|
||||
public static async Task<long> DebugAsync(string message, Func<Task> action)
|
||||
{
|
||||
var stopWatch = new System.Diagnostics.Stopwatch();
|
||||
stopWatch.Start();
|
||||
await action();
|
||||
stopWatch.Stop();
|
||||
var milliseconds = stopWatch.ElapsedMilliseconds;
|
||||
string info = $"{message} <{milliseconds}ms>";
|
||||
Log.Debug(info);
|
||||
return milliseconds;
|
||||
}
|
||||
|
||||
public static long Normal(string message, Action action)
|
||||
{
|
||||
var stopWatch = new System.Diagnostics.Stopwatch();
|
||||
|
|
@ -34,6 +50,20 @@ namespace Flow.Launcher.Infrastructure
|
|||
Log.Info(info);
|
||||
return milliseconds;
|
||||
}
|
||||
|
||||
public static async Task<long> NormalAsync(string message, Func<Task> action)
|
||||
{
|
||||
var stopWatch = new System.Diagnostics.Stopwatch();
|
||||
stopWatch.Start();
|
||||
await action();
|
||||
stopWatch.Stop();
|
||||
var milliseconds = stopWatch.ElapsedMilliseconds;
|
||||
string info = $"{message} <{milliseconds}ms>";
|
||||
Log.Info(info);
|
||||
return milliseconds;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void StartCount(string name, Action action)
|
||||
{
|
||||
|
|
|
|||
12
Flow.Launcher.Plugin/IAsyncPlugin.cs
Normal file
12
Flow.Launcher.Plugin/IAsyncPlugin.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Flow.Launcher.Plugin
|
||||
{
|
||||
public interface IAsyncPlugin
|
||||
{
|
||||
Task<List<Result>> QueryAsync(Query query, CancellationToken token);
|
||||
Task InitAsync(PluginInitContext context);
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ namespace Flow.Launcher.Plugin
|
|||
public interface IPlugin
|
||||
{
|
||||
List<Result> Query(Query query);
|
||||
|
||||
void Init(PluginInitContext context);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Flow.Launcher.Plugin
|
||||
{
|
||||
|
|
@ -34,7 +35,7 @@ namespace Flow.Launcher.Plugin
|
|||
/// Plugin's in memory data with new content
|
||||
/// added by user.
|
||||
/// </summary>
|
||||
void ReloadAllPluginData();
|
||||
Task ReloadAllPluginData();
|
||||
|
||||
/// <summary>
|
||||
/// Check for new Flow Launcher update
|
||||
|
|
|
|||
9
Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs
Normal file
9
Flow.Launcher.Plugin/Interfaces/IAsyncReloadable.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace Flow.Launcher.Plugin
|
||||
{
|
||||
public interface IAsyncReloadable
|
||||
{
|
||||
Task ReloadDataAsync();
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
{
|
||||
public class PluginPair
|
||||
{
|
||||
public IPlugin Plugin { get; internal set; }
|
||||
public object Plugin { get; internal set; }
|
||||
public PluginMetadata Metadata { get; internal set; }
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -78,9 +78,9 @@ namespace Flow.Launcher
|
|||
ImageLoader.Save();
|
||||
}
|
||||
|
||||
public void ReloadAllPluginData()
|
||||
public async Task ReloadAllPluginData()
|
||||
{
|
||||
PluginManager.ReloadData();
|
||||
await PluginManager.ReloadData();
|
||||
}
|
||||
|
||||
public void ShowMsg(string title, string subTitle = "", string iconPath = "")
|
||||
|
|
|
|||
|
|
@ -405,45 +405,47 @@ namespace Flow.Launcher.ViewModel
|
|||
}, currentCancellationToken);
|
||||
|
||||
var plugins = PluginManager.ValidPluginsForQuery(query);
|
||||
Task.Run(() =>
|
||||
Task.Run(async () =>
|
||||
{
|
||||
// so looping will stop once it was cancelled
|
||||
|
||||
Task[] tasks = new Task[plugins.Count];
|
||||
var parallelOptions = new ParallelOptions { CancellationToken = currentCancellationToken };
|
||||
try
|
||||
{
|
||||
Parallel.ForEach(plugins, parallelOptions, plugin =>
|
||||
Parallel.For(0, plugins.Count, parallelOptions, i =>
|
||||
{
|
||||
if (!plugin.Metadata.Disabled)
|
||||
if (!plugins[i].Metadata.Disabled)
|
||||
{
|
||||
try
|
||||
{
|
||||
var results = PluginManager.QueryForPlugin(plugin, query);
|
||||
UpdateResultView(results, plugin.Metadata, query);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Log.Exception("MainViewModel", $"Exception when querying the plugin {plugin.Metadata.Name}", e, "QueryResults");
|
||||
}
|
||||
tasks[i] = QueryTask(i, query, currentCancellationToken);
|
||||
}
|
||||
else tasks[i] = Task.CompletedTask; // Avoid Null
|
||||
});
|
||||
|
||||
// Check the code, WhenAll will translate all type of IEnumerable or Collection to Array, so make an array at first
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
|
||||
// this should happen once after all queries are done so progress bar should continue
|
||||
// until the end of all querying
|
||||
_isQueryRunning = false;
|
||||
if (currentUpdateSource == _updateSource)
|
||||
if (!currentCancellationToken.IsCancellationRequested)
|
||||
{ // update to hidden if this is still the current query
|
||||
ProgressBarVisibility = Visibility.Hidden;
|
||||
}
|
||||
}, currentCancellationToken).ContinueWith(t =>
|
||||
{
|
||||
Log.Exception("MainViewModel", "Error when querying plugins", t.Exception?.InnerException, "QueryResults");
|
||||
}, TaskContinuationOptions.OnlyOnFaulted);
|
||||
|
||||
async Task QueryTask(int pairIndex, Query query, CancellationToken token)
|
||||
{
|
||||
var result = await PluginManager.QueryForPlugin(plugins[pairIndex], query, token);
|
||||
UpdateResultView(result, plugins[pairIndex].Metadata, query);
|
||||
}
|
||||
|
||||
}, currentCancellationToken).ContinueWith(t => Log.Exception("|MainViewModel|Plugins Query Exceptions", t.Exception),
|
||||
TaskContinuationOptions.OnlyOnFaulted);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ namespace Flow.Launcher.Plugin.ControlPanel
|
|||
int cxDesired, int cyDesired, uint fuLoad);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
||||
extern static bool DestroyIcon(IntPtr handle);
|
||||
static extern bool DestroyIcon(IntPtr handle);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
static extern IntPtr FindResource(IntPtr hModule, IntPtr lpName, IntPtr lpType);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Interop;
|
||||
|
|
@ -67,13 +68,15 @@ namespace Flow.Launcher.Plugin.Sys
|
|||
{
|
||||
c.TitleHighlightData = titleMatch.MatchData;
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
c.SubTitleHighlightData = subTitleMatch.MatchData;
|
||||
}
|
||||
|
||||
results.Add(c);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
|
|
@ -94,13 +97,15 @@ namespace Flow.Launcher.Plugin.Sys
|
|||
IcoPath = "Images\\shutdown.png",
|
||||
Action = c =>
|
||||
{
|
||||
var reuslt = MessageBox.Show(context.API.GetTranslation("flowlauncher_plugin_sys_dlgtext_shutdown_computer"),
|
||||
context.API.GetTranslation("flowlauncher_plugin_sys_shutdown_computer"),
|
||||
MessageBoxButton.YesNo, MessageBoxImage.Warning);
|
||||
var reuslt = MessageBox.Show(
|
||||
context.API.GetTranslation("flowlauncher_plugin_sys_dlgtext_shutdown_computer"),
|
||||
context.API.GetTranslation("flowlauncher_plugin_sys_shutdown_computer"),
|
||||
MessageBoxButton.YesNo, MessageBoxImage.Warning);
|
||||
if (reuslt == MessageBoxResult.Yes)
|
||||
{
|
||||
Process.Start("shutdown", "/s /t 0");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
|
@ -111,13 +116,15 @@ namespace Flow.Launcher.Plugin.Sys
|
|||
IcoPath = "Images\\restart.png",
|
||||
Action = c =>
|
||||
{
|
||||
var result = MessageBox.Show(context.API.GetTranslation("flowlauncher_plugin_sys_dlgtext_restart_computer"),
|
||||
context.API.GetTranslation("flowlauncher_plugin_sys_restart_computer"),
|
||||
MessageBoxButton.YesNo, MessageBoxImage.Warning);
|
||||
var result = MessageBox.Show(
|
||||
context.API.GetTranslation("flowlauncher_plugin_sys_dlgtext_restart_computer"),
|
||||
context.API.GetTranslation("flowlauncher_plugin_sys_restart_computer"),
|
||||
MessageBoxButton.YesNo, MessageBoxImage.Warning);
|
||||
if (result == MessageBoxResult.Yes)
|
||||
{
|
||||
Process.Start("shutdown", "/r /t 0");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
|
@ -163,14 +170,16 @@ namespace Flow.Launcher.Plugin.Sys
|
|||
// http://www.pinvoke.net/default.aspx/shell32/SHEmptyRecycleBin.html
|
||||
// FYI, couldn't find documentation for this but if the recycle bin is already empty, it will return -2147418113 (0x8000FFFF (E_UNEXPECTED))
|
||||
// 0 for nothing
|
||||
var result = SHEmptyRecycleBin(new WindowInteropHelper(Application.Current.MainWindow).Handle, 0);
|
||||
if (result != (uint) HRESULT.S_OK && result != (uint)0x8000FFFF)
|
||||
var result = SHEmptyRecycleBin(new WindowInteropHelper(Application.Current.MainWindow).Handle,
|
||||
0);
|
||||
if (result != (uint) HRESULT.S_OK && result != (uint) 0x8000FFFF)
|
||||
{
|
||||
MessageBox.Show($"Error emptying recycle bin, error code: {result}\n" +
|
||||
"please refer to https://msdn.microsoft.com/en-us/library/windows/desktop/aa378137",
|
||||
"Error",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
"Error",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
|
@ -229,9 +238,13 @@ namespace Flow.Launcher.Plugin.Sys
|
|||
{
|
||||
// Hide the window first then show msg after done because sometimes the reload could take a while, so not to make user think it's frozen.
|
||||
Application.Current.MainWindow.Hide();
|
||||
context.API.ReloadAllPluginData();
|
||||
context.API.ShowMsg(context.API.GetTranslation("flowlauncher_plugin_sys_dlgtitle_success"),
|
||||
context.API.GetTranslation("flowlauncher_plugin_sys_dlgtext_all_applicableplugins_reloaded"));
|
||||
|
||||
context.API.ReloadAllPluginData().ContinueWith(_ =>
|
||||
context.API.ShowMsg(
|
||||
context.API.GetTranslation("flowlauncher_plugin_sys_dlgtitle_success"),
|
||||
context.API.GetTranslation(
|
||||
"flowlauncher_plugin_sys_dlgtext_all_applicableplugins_reloaded")));
|
||||
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue