diff --git a/Flow.Launcher.Core/Flow.Launcher.Core.csproj b/Flow.Launcher.Core/Flow.Launcher.Core.csproj
index d7df11f8303..9e932a5085b 100644
--- a/Flow.Launcher.Core/Flow.Launcher.Core.csproj
+++ b/Flow.Launcher.Core/Flow.Launcher.Core.csproj
@@ -55,6 +55,7 @@
+
diff --git a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs
index 431458881ec..22f547a5a75 100644
--- a/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs
+++ b/Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs
@@ -11,7 +11,9 @@
using System.Windows.Forms;
using Flow.Launcher.Infrastructure.Logger;
using Flow.Launcher.Plugin;
+using ICSharpCode.SharpZipLib.Zip;
using JetBrains.Annotations;
+using Microsoft.IO;
namespace Flow.Launcher.Core.Plugin
{
@@ -33,9 +35,11 @@ internal abstract class JsonRPCPlugin : IAsyncPlugin, IContextMenu
protected abstract string ExecuteCallback(JsonRPCRequestModel rpcRequest);
protected abstract string ExecuteContextMenu(Result selectedResult);
+ private static readonly RecyclableMemoryStreamManager BufferManager = new();
+
public List LoadContextMenus(Result selectedResult)
{
- string output = ExecuteContextMenu(selectedResult);
+ var output = ExecuteContextMenu(selectedResult);
try
{
return DeserializedResult(output);
@@ -61,12 +65,23 @@ private async Task> DeserializedResultAsync(Stream output)
{
if (output == Stream.Null) return null;
- var queryResponseModel = await
- JsonSerializer.DeserializeAsync(output, options);
+ try
+ {
+ var queryResponseModel =
+ await JsonSerializer.DeserializeAsync(output, options);
- await output.DisposeAsync();
-
- return ParseResults(queryResponseModel);
+ return ParseResults(queryResponseModel);
+ }
+ catch (JsonException e)
+ {
+ Log.Exception(GetType().FullName, "Unexpected Json Input", e);
+ }
+ finally
+ {
+ await output.DisposeAsync();
+ }
+
+ return null;
}
private List DeserializedResult(string output)
@@ -81,7 +96,6 @@ private List DeserializedResult(string output)
private List ParseResults(JsonRPCQueryResponseModel queryResponseModel)
{
- var results = new List();
if (queryResponseModel.Result == null) return null;
if (!string.IsNullOrEmpty(queryResponseModel.DebugMessage))
@@ -89,7 +103,7 @@ private List ParseResults(JsonRPCQueryResponseModel queryResponseModel)
context.API.ShowMsg(queryResponseModel.DebugMessage);
}
- foreach (JsonRPCResult result in queryResponseModel.Result)
+ foreach (var result in queryResponseModel.Result)
{
result.Action = c =>
{
@@ -114,7 +128,8 @@ private List ParseResults(JsonRPCQueryResponseModel queryResponseModel)
return !result.JsonRPCAction.DontHideAfterAction;
}
- var jsonRpcRequestModel = JsonSerializer.Deserialize(actionResponse, options);
+ var jsonRpcRequestModel =
+ JsonSerializer.Deserialize(actionResponse, options);
if (jsonRpcRequestModel?.Method?.StartsWith("Flow.Launcher.") ?? false)
{
@@ -125,9 +140,12 @@ private List ParseResults(JsonRPCQueryResponseModel queryResponseModel)
return !result.JsonRPCAction.DontHideAfterAction;
};
- results.Add(result);
}
+ var results = new List();
+
+ results.AddRange(queryResponseModel.Result);
+
return results;
}
@@ -217,16 +235,42 @@ protected string Execute(ProcessStartInfo startInfo)
protected async Task ExecuteAsync(ProcessStartInfo startInfo, CancellationToken token = default)
{
+ Process process = null;
+ bool disposed = false;
try
{
- using var process = Process.Start(startInfo);
+ process = Process.Start(startInfo);
if (process == null)
{
Log.Error("|JsonRPCPlugin.ExecuteAsync|Can't start new process");
return Stream.Null;
}
- var result = process.StandardOutput.BaseStream;
+ await using var source = process.StandardOutput.BaseStream;
+
+ var buffer = BufferManager.GetStream();
+
+ token.Register(() =>
+ {
+ // ReSharper disable once AccessToModifiedClosure
+ // Manually Check whether disposed
+ if (!disposed && !process.HasExited)
+ process.Kill();
+ });
+
+ try
+ {
+ // token expire won't instantly trigger the exception,
+ // manually kill process at before
+ await source.CopyToAsync(buffer, token);
+ }
+ catch (OperationCanceledException)
+ {
+ await buffer.DisposeAsync();
+ return Stream.Null;
+ }
+
+ buffer.Seek(0, SeekOrigin.Begin);
token.ThrowIfCancellationRequested();
@@ -245,7 +289,7 @@ protected async Task ExecuteAsync(ProcessStartInfo startInfo, Cancellati
return Stream.Null;
}
- return result;
+ return buffer;
}
catch (Exception e)
{
@@ -254,15 +298,24 @@ protected async Task ExecuteAsync(ProcessStartInfo startInfo, Cancellati
e);
return Stream.Null;
}
+ finally
+ {
+ process?.Dispose();
+ disposed = true;
+ }
}
public async Task> QueryAsync(Query query, CancellationToken token)
{
- var output = await ExecuteQueryAsync(query, token);
try
{
+ var output = await ExecuteQueryAsync(query, token);
return await DeserializedResultAsync(output);
}
+ catch (OperationCanceledException)
+ {
+ return null;
+ }
catch (Exception e)
{
Log.Exception($"|JsonRPCPlugin.Query|Exception when query <{query}>", e);