From 1bf01a0a0b4254d1067dbee365536038a588079b Mon Sep 17 00:00:00 2001 From: Hirogen Date: Mon, 8 Jun 2026 22:10:18 +0200 Subject: [PATCH 1/4] first iteration --- .../Services/ToolLaunchServiceTests.cs | 62 +++++++++ .../Dialogs/LogTabWindow/LogTabWindow.cs | 122 +++++++----------- .../Interface/IToolLaunchService.cs | 8 ++ .../ToolLaunchService/ToolLaunchRequest.cs | 14 ++ .../ToolLaunchService/ToolLaunchResult.cs | 14 ++ .../ToolLaunchService/ToolLaunchService.cs | 107 +++++++++++++++ 6 files changed, 251 insertions(+), 76 deletions(-) create mode 100644 src/LogExpert.Tests/Services/ToolLaunchServiceTests.cs create mode 100644 src/LogExpert.UI/Interface/IToolLaunchService.cs create mode 100644 src/LogExpert.UI/Services/ToolLaunchService/ToolLaunchRequest.cs create mode 100644 src/LogExpert.UI/Services/ToolLaunchService/ToolLaunchResult.cs create mode 100644 src/LogExpert.UI/Services/ToolLaunchService/ToolLaunchService.cs diff --git a/src/LogExpert.Tests/Services/ToolLaunchServiceTests.cs b/src/LogExpert.Tests/Services/ToolLaunchServiceTests.cs new file mode 100644 index 00000000..1eb89663 --- /dev/null +++ b/src/LogExpert.Tests/Services/ToolLaunchServiceTests.cs @@ -0,0 +1,62 @@ +using System.Runtime.Versioning; + +using LogExpert.Core.Interfaces; +using LogExpert.UI.Services.ToolLaunchService; + +using Moq; + +using NUnit.Framework; + +namespace LogExpert.Tests.Services; + +[TestFixture] +[Apartment(ApartmentState.STA)] +[SupportedOSPlatform("windows")] +internal class ToolLaunchServiceTests +{ + private Mock _pluginRegistryMock = null!; + private ToolLaunchService _sut = null!; + + [SetUp] + public void SetUp () + { + _pluginRegistryMock = new Mock(); + _ = _pluginRegistryMock.Setup(pr => pr.RegisteredColumnizers).Returns([]); + + _sut = new ToolLaunchService(_pluginRegistryMock.Object); + } + + [Test] + public void Launch_WithEmptyCmd_ReturnsHasErrorTrue () + { + var request = new ToolLaunchRequest { Cmd = string.Empty, Args = string.Empty, SysoutPipe = false }; + + var result = _sut.Launch(request); + + Assert.That(result.HasError, Is.True); + Assert.That(result.ErrorMessage, Is.Not.Null.And.Not.Empty); + } + + [Test] + public void Launch_WithValidCmdAndNoSysoutPipe_ReturnsSuccessWithNullPipeFileName () + { + var request = new ToolLaunchRequest { Cmd = "cmd.exe", Args = "/c exit 0", SysoutPipe = false }; + + var result = _sut.Launch(request); + + Assert.That(result.HasError, Is.False); + Assert.That(result.PipeFileName, Is.Null); + } + + [Test] + public void Launch_WithValidCmdAndSysoutPipe_ReturnsPipeFileNamePointingToExistingFile () + { + var request = new ToolLaunchRequest { Cmd = "cmd.exe", Args = "/c echo hello", SysoutPipe = true }; + + var result = _sut.Launch(request); + + Assert.That(result.HasError, Is.False); + Assert.That(result.PipeFileName, Is.Not.Null.And.Not.Empty); + Assert.That(File.Exists(result.PipeFileName), Is.True); + } +} diff --git a/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.cs b/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.cs index 52466129..f2c142e0 100644 --- a/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.cs +++ b/src/LogExpert.UI/Dialogs/LogTabWindow/LogTabWindow.cs @@ -27,6 +27,7 @@ using LogExpert.UI.Services.MenuToolbarService; using LogExpert.UI.Services.SessionHandlerService; using LogExpert.UI.Services.TabControllerService; +using LogExpert.UI.Services.ToolLaunchService; using LogExpert.UI.Services.ToolWindowCoordinatorService; using NLog; @@ -54,6 +55,7 @@ internal partial class LogTabWindow : Form, ILogTabWindow private readonly ToolWindowCoordinator _toolWindowCoordinator; private readonly FileOperationService _fileOperationService; private readonly SessionHandler _sessionHandler; + private readonly ToolLaunchService _toolLaunchService; private bool _disposed; @@ -114,6 +116,7 @@ public LogTabWindow (string[] fileNames, int instanceNumber, bool showInstanceNu _fileOperationService.FileOpened += OnFileOperationServiceFileOpened; _sessionHandler = new SessionHandler(PluginRegistry.PluginRegistry.Instance, request => _fileOperationService.AddFileTab(request)); + _toolLaunchService = new ToolLaunchService(PluginRegistry.PluginRegistry.Instance); _logWindowCoordinator = new LogWindowCoordinator(configManager, PluginRegistry.PluginRegistry.Instance, this, _tabController, _ledService, _fileOperationService); @@ -1328,102 +1331,69 @@ private void ToolButtonClick (ToolEntry toolEntry) return; } + ToolLaunchRequest request; + if (CurrentLogWindow != null) { var line = CurrentLogWindow.GetCurrentLine(); var info = CurrentLogWindow.GetCurrentFileInfo(); - if (line != null && info != null) + if (line == null || info == null) { - ArgParser parser = new(toolEntry.Args); - var argLine = parser.BuildArgs(line, CurrentLogWindow.GetRealLineNum() + 1, info, this); - if (argLine != null) - { - StartTool(toolEntry.Cmd, argLine, toolEntry.Sysout, toolEntry.ColumnizerName, toolEntry.WorkingDir, true); - } + return; } + + ArgParser parser = new(toolEntry.Args); + var argLine = parser.BuildArgs(line, CurrentLogWindow.GetRealLineNum() + 1, info, this); + if (argLine == null) + { + return; + } + + request = new ToolLaunchRequest + { + Cmd = toolEntry.Cmd, + Args = argLine, + SysoutPipe = toolEntry.Sysout, + ColumnizerName = toolEntry.ColumnizerName, + WorkingDir = toolEntry.WorkingDir + }; } else { - StartTool(toolEntry.Cmd, string.Empty, toolEntry.Sysout, toolEntry.ColumnizerName, toolEntry.WorkingDir); - } - } - - [SupportedOSPlatform("windows")] - private void StartTool (string cmd, string args, bool sysoutPipe, string columnizerName, string workingDir, bool startWithOpenLog = false) - { - if (string.IsNullOrEmpty(cmd)) - { - return; - } + if (toolEntry.Sysout) + { + _ = MessageBox.Show(Resources.LogTabWindow_UI_Message_NoLogfileWithSysOutPipeToolConfigured, Resources.LogExpert_Common_UI_Title_LogExpert); + } - Process process = new(); - ProcessStartInfo startInfo = new(cmd, args); - if (!string.IsNullOrEmpty(workingDir)) - { - startInfo.WorkingDirectory = workingDir; + request = new ToolLaunchRequest + { + Cmd = toolEntry.Cmd, + Args = string.Empty, + SysoutPipe = false, + ColumnizerName = toolEntry.ColumnizerName, + WorkingDir = toolEntry.WorkingDir + }; } - process.StartInfo = startInfo; - process.EnableRaisingEvents = true; + var result = _toolLaunchService.Launch(request); - if (sysoutPipe && !startWithOpenLog) + if (result.HasError) { - _ = MessageBox.Show(Resources.LogTabWindow_UI_Message_NoLogfileWithSysOutPipeToolConfigured, Resources.LogExpert_Common_UI_Title_LogExpert); + _ = MessageBox.Show(result.ErrorMessage, Resources.LogExpert_Common_UI_Title_LogExpert); + return; } - if (sysoutPipe && startWithOpenLog) + if (result.PipeFileName != null) { - var columnizer = ColumnizerPicker.DecideMemoryColumnizerByName(columnizerName, PluginRegistry.PluginRegistry.Instance.RegisteredColumnizers); + var title = CurrentLogWindow!.IsTempFile + ? CurrentLogWindow.TempTitleName + : $"{Util.GetNameFromPath(CurrentLogWindow.FileName)}{Resources.LogTabWindow_UI_LogWindow_Title_ExternalStartTool_Suffix}"; - //_logger.Info($"Starting external tool with sysout redirection: {cmd} {args}")); - startInfo.UseShellExecute = false; - startInfo.RedirectStandardOutput = true; - //process.OutputDataReceived += pipe.DataReceivedEventHandler; - try + var logWin = AddTempFileTab(result.PipeFileName, title); + if (result.Columnizer != null) { - _ = process.Start(); + logWin.ForceColumnizer(result.Columnizer); } - catch (Exception e) when (e is Win32Exception or - InvalidOperationException or - ObjectDisposedException or - PlatformNotSupportedException) - { - _logger.Error(e); - _ = MessageBox.Show(e.Message, Resources.LogExpert_Common_UI_Title_LogExpert); - return; - } - - SysoutPipe pipe = new(process.StandardOutput); - - var logWin = AddTempFileTab(pipe.FileName, - CurrentLogWindow.IsTempFile - ? CurrentLogWindow.TempTitleName - : $"{Util.GetNameFromPath(CurrentLogWindow.FileName)}{Resources.LogTabWindow_UI_LogWindow_Title_ExternalStartTool_Suffix}"); - logWin.ForceColumnizer(columnizer); - - process.Exited += pipe.ProcessExitedEventHandler; - //process.BeginOutputReadLine(); - } - else - { - StartExternalTool(process, startInfo); - } - } - - private static void StartExternalTool (Process process, ProcessStartInfo startInfo) - { - try - { - startInfo.UseShellExecute = false; - _ = process.Start(); - } - catch (Exception e) when (e is Win32Exception or - InvalidOperationException or - ObjectDisposedException or - PlatformNotSupportedException) - { - _logger.Error(e); - _ = MessageBox.Show(e.Message, Resources.LogExpert_Common_UI_Title_LogExpert); } } diff --git a/src/LogExpert.UI/Interface/IToolLaunchService.cs b/src/LogExpert.UI/Interface/IToolLaunchService.cs new file mode 100644 index 00000000..29aad853 --- /dev/null +++ b/src/LogExpert.UI/Interface/IToolLaunchService.cs @@ -0,0 +1,8 @@ +using LogExpert.UI.Services.ToolLaunchService; + +namespace LogExpert.UI.Interface; + +internal interface IToolLaunchService +{ + ToolLaunchResult Launch (ToolLaunchRequest request); +} diff --git a/src/LogExpert.UI/Services/ToolLaunchService/ToolLaunchRequest.cs b/src/LogExpert.UI/Services/ToolLaunchService/ToolLaunchRequest.cs new file mode 100644 index 00000000..ef34d541 --- /dev/null +++ b/src/LogExpert.UI/Services/ToolLaunchService/ToolLaunchRequest.cs @@ -0,0 +1,14 @@ +namespace LogExpert.UI.Services.ToolLaunchService; + +internal sealed record ToolLaunchRequest +{ + public required string Cmd { get; init; } + + public string Args { get; init; } = string.Empty; + + public bool SysoutPipe { get; init; } + + public string? ColumnizerName { get; init; } + + public string? WorkingDir { get; init; } +} diff --git a/src/LogExpert.UI/Services/ToolLaunchService/ToolLaunchResult.cs b/src/LogExpert.UI/Services/ToolLaunchService/ToolLaunchResult.cs new file mode 100644 index 00000000..5ba59456 --- /dev/null +++ b/src/LogExpert.UI/Services/ToolLaunchService/ToolLaunchResult.cs @@ -0,0 +1,14 @@ +using ColumnizerLib; + +namespace LogExpert.UI.Services.ToolLaunchService; + +internal readonly record struct ToolLaunchResult +{ + public bool HasError { get; init; } + + public string? ErrorMessage { get; init; } + + public string? PipeFileName { get; init; } + + public ILogLineMemoryColumnizer? Columnizer { get; init; } +} diff --git a/src/LogExpert.UI/Services/ToolLaunchService/ToolLaunchService.cs b/src/LogExpert.UI/Services/ToolLaunchService/ToolLaunchService.cs new file mode 100644 index 00000000..446e1e7d --- /dev/null +++ b/src/LogExpert.UI/Services/ToolLaunchService/ToolLaunchService.cs @@ -0,0 +1,107 @@ +using System.ComponentModel; +using System.Diagnostics; +using System.Runtime.Versioning; + +using LogExpert.Core.Classes; +using LogExpert.Core.Classes.Columnizer; +using LogExpert.Core.Interfaces; +using LogExpert.UI.Interface; + +using NLog; + +namespace LogExpert.UI.Services.ToolLaunchService; + +[SupportedOSPlatform("windows")] +internal sealed class ToolLaunchService (IPluginRegistry pluginRegistry) : IToolLaunchService +{ + private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); + + public ToolLaunchResult Launch (ToolLaunchRequest request) + { + if (string.IsNullOrEmpty(request.Cmd)) + { + return new ToolLaunchResult + { + HasError = true, + ErrorMessage = "Command must not be empty." + }; + } + + if (request.SysoutPipe) + { + return LaunchWithSysoutPipe(request); + } + + return LaunchExternal(request); + } + + private static ToolLaunchResult LaunchExternal (ToolLaunchRequest request) + { + var startInfo = BuildStartInfo(request); + startInfo.UseShellExecute = false; + Process process = new() { StartInfo = startInfo, EnableRaisingEvents = true }; + + try + { + _ = process.Start(); + } + catch (Exception e) when (e is Win32Exception or + InvalidOperationException or + ObjectDisposedException or + PlatformNotSupportedException) + { + _logger.Error(e); + return new ToolLaunchResult { HasError = true, ErrorMessage = e.Message }; + } + + return new ToolLaunchResult { HasError = false }; + } + + private ToolLaunchResult LaunchWithSysoutPipe (ToolLaunchRequest request) + { + var columnizer = string.IsNullOrEmpty(request.ColumnizerName) + ? null + : ColumnizerPicker.DecideMemoryColumnizerByName(request.ColumnizerName, pluginRegistry.RegisteredColumnizers); + + var startInfo = BuildStartInfo(request); + startInfo.UseShellExecute = false; + startInfo.RedirectStandardOutput = true; + + Process process = new() { StartInfo = startInfo, EnableRaisingEvents = true }; + + try + { + _ = process.Start(); + } + catch (Exception e) when (e is Win32Exception or + InvalidOperationException or + ObjectDisposedException or + PlatformNotSupportedException) + { + _logger.Error(e); + return new ToolLaunchResult { HasError = true, ErrorMessage = e.Message }; + } + + // TODO: SysoutPipe temp file is never deleted — fire-and-forget lifetime by design. + SysoutPipe pipe = new(process.StandardOutput); + process.Exited += pipe.ProcessExitedEventHandler; + + return new ToolLaunchResult + { + HasError = false, + PipeFileName = pipe.FileName, + Columnizer = columnizer + }; + } + + private static ProcessStartInfo BuildStartInfo (ToolLaunchRequest request) + { + var startInfo = new ProcessStartInfo(request.Cmd, request.Args); + if (!string.IsNullOrEmpty(request.WorkingDir)) + { + startInfo.WorkingDirectory = request.WorkingDir; + } + + return startInfo; + } +} From 133dc3f482afdce15ce5b8e827ebc0f44d2f6679 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 8 Jun 2026 20:19:33 +0000 Subject: [PATCH 2/4] chore: update plugin hashes [skip ci] --- .../PluginHashGenerator.Generated.cs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/PluginRegistry/PluginHashGenerator.Generated.cs b/src/PluginRegistry/PluginHashGenerator.Generated.cs index 57823e8f..bbd6ec04 100644 --- a/src/PluginRegistry/PluginHashGenerator.Generated.cs +++ b/src/PluginRegistry/PluginHashGenerator.Generated.cs @@ -10,7 +10,7 @@ public static partial class PluginValidator { /// /// Gets pre-calculated SHA256 hashes for built-in plugins. - /// Generated: 2026-06-08 18:22:43 UTC + /// Generated: 2026-06-08 20:19:32 UTC /// Configuration: Release /// Plugin count: 21 /// @@ -18,27 +18,27 @@ public static Dictionary GetBuiltInPluginHashes() { return new Dictionary(StringComparer.OrdinalIgnoreCase) { - ["AutoColumnizer.dll"] = "FF45688846E19EDDF7244BD9D69254926E32DD5A79730018160696C9F9B5B735", + ["AutoColumnizer.dll"] = "A46326FD9267FD6E71687D3D7A095000A59E35A1CCC50649701387D31B5CC795", ["BouncyCastle.Cryptography.dll"] = "E5EEAF6D263C493619982FD3638E6135077311D08C961E1FE128F9107D29EBC6", ["BouncyCastle.Cryptography.dll (x86)"] = "E5EEAF6D263C493619982FD3638E6135077311D08C961E1FE128F9107D29EBC6", - ["CsvColumnizer.dll"] = "3865D9280E673C03185B2B39053708DD6D782D97C5292AF4DD123AFA19531CD3", - ["CsvColumnizer.dll (x86)"] = "3865D9280E673C03185B2B39053708DD6D782D97C5292AF4DD123AFA19531CD3", - ["DefaultPlugins.dll"] = "36C050D6380D6F7B9403D43AACFA060F542D225E8C6B027B2E4F3CB18C5E298B", - ["FlashIconHighlighter.dll"] = "3A02E81476958DA1C411C8A2425A6482A55C05CADDFEAE60E7E4B282EA8CDBDF", - ["GlassfishColumnizer.dll"] = "135C0D23AB6A44376B01AB3FB19A2EADDDEBC6EDFDEDBA6682F30C57D988DC36", - ["JsonColumnizer.dll"] = "6270BE7D27EE4A5AB5AB1D9967ED97F3EC91B553C7D3A5968350933AB892F772", - ["JsonCompactColumnizer.dll"] = "8E5F12FE7BD86DDC9B3B0B1D3C04B49AD0394C81A9F899684A5F16532A5B0A35", - ["Log4jXmlColumnizer.dll"] = "3C4806DBD51DA3CECF152EB19C0A267DC0F8B977DF9457DAEEFA1F0A099525ED", - ["LogExpert.Resources.dll"] = "2D1743296A62E8BB1089635043A46A036AAD45E92E981C77ECA3373E3D9CCB5B", + ["CsvColumnizer.dll"] = "43D9DAF673632633FA4CED2346C405CFA16163CEA04550932EFA2D18FBE4A108", + ["CsvColumnizer.dll (x86)"] = "43D9DAF673632633FA4CED2346C405CFA16163CEA04550932EFA2D18FBE4A108", + ["DefaultPlugins.dll"] = "116170FCD0CF481179099ED7D12D72FAF4969A5AC457EB3A5A6372D40776AEC7", + ["FlashIconHighlighter.dll"] = "2974A79822FB5FEE133D48CA265471DDC44321548785884EE9C482DFFCFE7F1F", + ["GlassfishColumnizer.dll"] = "6FBDDE6A01132AFD2D15642EFE3FD729950B779889749090E935544BAAF4942B", + ["JsonColumnizer.dll"] = "3CBCB28AB46CFAFA4CD193CCFB2A6A2C61B10A7C48AC5C9E116832D9CEFE7B2C", + ["JsonCompactColumnizer.dll"] = "6E8158EB64D48A7F8F3F7DD5C24BA84150849C6D846356E38BC82FD839696782", + ["Log4jXmlColumnizer.dll"] = "4046BD407C2A814C5EBAAE55AFA8A671D51DAB437B1AD9EAD931A15C1810E61C", + ["LogExpert.Resources.dll"] = "14FFCF4FFBC9EFD9EC6924D91B98FBB20F47DFAF414DACCF372E14561DABD2F3", ["Microsoft.Extensions.DependencyInjection.Abstractions.dll"] = "67FA4325000DB017DC0C35829B416F024F042D24EFB868BCF17A895EE6500A93", ["Microsoft.Extensions.DependencyInjection.Abstractions.dll (x86)"] = "67FA4325000DB017DC0C35829B416F024F042D24EFB868BCF17A895EE6500A93", ["Microsoft.Extensions.Logging.Abstractions.dll"] = "BB853130F5AFAF335BE7858D661F8212EC653835100F5A4E3AA2C66A4D4F685D", ["Microsoft.Extensions.Logging.Abstractions.dll (x86)"] = "BB853130F5AFAF335BE7858D661F8212EC653835100F5A4E3AA2C66A4D4F685D", - ["RegexColumnizer.dll"] = "082BF04B54570B54F4BCF323338174F5818ED1A0992F56E6D7B367D36BAD2D57", - ["SftpFileSystem.dll"] = "5E8378F4FB45D51A635B471F743326F40199AB70FBDB8D2527F8D0A00C33AC78", - ["SftpFileSystem.dll (x86)"] = "9F2C46E0C878EE92BBA225A859872A7BE3E9F6584931DE45FC087192945D41F4", - ["SftpFileSystem.Resources.dll"] = "394C36CE8D4CFD7735E200FEF7ADC6C0F133AD94221B72AE7977600ABB25AA4B", - ["SftpFileSystem.Resources.dll (x86)"] = "394C36CE8D4CFD7735E200FEF7ADC6C0F133AD94221B72AE7977600ABB25AA4B", + ["RegexColumnizer.dll"] = "62E25802F88FC206E8F202B6EE92661F9CC8AEAA081549821E151DD8BB271A86", + ["SftpFileSystem.dll"] = "B005BE7571EF4FA28D6223D792D5CEA9B586E2DC83A5AFD33455345F30BEF1B5", + ["SftpFileSystem.dll (x86)"] = "15282AD1EF2EF8A648F4597699900F87D84D3FFBD1A72E6685AF68CD604113A8", + ["SftpFileSystem.Resources.dll"] = "869762F9B842D69EF05AC6FEFE60201AC8A4B44D62BD64F42882CB17A071E0EB", + ["SftpFileSystem.Resources.dll (x86)"] = "869762F9B842D69EF05AC6FEFE60201AC8A4B44D62BD64F42882CB17A071E0EB", }; } From 46d57061a631e5610e954f05b27ca8532cc61c67 Mon Sep 17 00:00:00 2001 From: BRUNER Patrick Date: Tue, 9 Jun 2026 09:08:01 +0200 Subject: [PATCH 3/4] small optimizations --- .../ToolLaunchService/ToolLaunchService.cs | 50 +++++++++---------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/src/LogExpert.UI/Services/ToolLaunchService/ToolLaunchService.cs b/src/LogExpert.UI/Services/ToolLaunchService/ToolLaunchService.cs index 446e1e7d..0fde51ab 100644 --- a/src/LogExpert.UI/Services/ToolLaunchService/ToolLaunchService.cs +++ b/src/LogExpert.UI/Services/ToolLaunchService/ToolLaunchService.cs @@ -18,28 +18,33 @@ internal sealed class ToolLaunchService (IPluginRegistry pluginRegistry) : ITool public ToolLaunchResult Launch (ToolLaunchRequest request) { - if (string.IsNullOrEmpty(request.Cmd)) - { - return new ToolLaunchResult + return string.IsNullOrEmpty(request.Cmd) + ? new ToolLaunchResult { HasError = true, ErrorMessage = "Command must not be empty." - }; - } - - if (request.SysoutPipe) - { - return LaunchWithSysoutPipe(request); - } - - return LaunchExternal(request); + } + : request.SysoutPipe + ? LaunchWithSysoutPipe(request) + : LaunchExternal(request); } private static ToolLaunchResult LaunchExternal (ToolLaunchRequest request) { var startInfo = BuildStartInfo(request); + startInfo.UseShellExecute = false; - Process process = new() { StartInfo = startInfo, EnableRaisingEvents = true }; + + (bool flowControl, ToolLaunchResult value, _) = LaunchProcess(startInfo); + + return !flowControl + ? value + : new ToolLaunchResult { HasError = false }; + } + + private static (bool flowControl, ToolLaunchResult value, Process process) LaunchProcess (ProcessStartInfo startInfo) + { + using Process process = new() { StartInfo = startInfo, EnableRaisingEvents = true }; try { @@ -51,10 +56,10 @@ ObjectDisposedException or PlatformNotSupportedException) { _logger.Error(e); - return new ToolLaunchResult { HasError = true, ErrorMessage = e.Message }; + return (false, new ToolLaunchResult { HasError = true, ErrorMessage = e.Message }, process); } - return new ToolLaunchResult { HasError = false }; + return (true, default, process); } private ToolLaunchResult LaunchWithSysoutPipe (ToolLaunchRequest request) @@ -67,19 +72,11 @@ private ToolLaunchResult LaunchWithSysoutPipe (ToolLaunchRequest request) startInfo.UseShellExecute = false; startInfo.RedirectStandardOutput = true; - Process process = new() { StartInfo = startInfo, EnableRaisingEvents = true }; + (bool flowControl, ToolLaunchResult value, Process process) = LaunchProcess(startInfo); - try + if (!flowControl) { - _ = process.Start(); - } - catch (Exception e) when (e is Win32Exception or - InvalidOperationException or - ObjectDisposedException or - PlatformNotSupportedException) - { - _logger.Error(e); - return new ToolLaunchResult { HasError = true, ErrorMessage = e.Message }; + return value; } // TODO: SysoutPipe temp file is never deleted — fire-and-forget lifetime by design. @@ -97,6 +94,7 @@ ObjectDisposedException or private static ProcessStartInfo BuildStartInfo (ToolLaunchRequest request) { var startInfo = new ProcessStartInfo(request.Cmd, request.Args); + if (!string.IsNullOrEmpty(request.WorkingDir)) { startInfo.WorkingDirectory = request.WorkingDir; From 942a3a12767a6d3085a1d68dbb0dfed115ff42e4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 9 Jun 2026 07:11:55 +0000 Subject: [PATCH 4/4] chore: update plugin hashes [skip ci] --- .../PluginHashGenerator.Generated.cs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/PluginRegistry/PluginHashGenerator.Generated.cs b/src/PluginRegistry/PluginHashGenerator.Generated.cs index be4694d6..c5abc25e 100644 --- a/src/PluginRegistry/PluginHashGenerator.Generated.cs +++ b/src/PluginRegistry/PluginHashGenerator.Generated.cs @@ -10,7 +10,7 @@ public static partial class PluginValidator { /// /// Gets pre-calculated SHA256 hashes for built-in plugins. - /// Generated: 2026-06-09 06:55:04 UTC + /// Generated: 2026-06-09 07:11:53 UTC /// Configuration: Release /// Plugin count: 21 /// @@ -18,27 +18,27 @@ public static Dictionary GetBuiltInPluginHashes() { return new Dictionary(StringComparer.OrdinalIgnoreCase) { - ["AutoColumnizer.dll"] = "3D6748C5E935505FFBFE24830208B11FAACF4EDCD672FBEABA4DD239B05D927A", + ["AutoColumnizer.dll"] = "F06B072F21BED58FC98DC9FD89C081BB0786DD7AB9326562C4ED2D687598EF6B", ["BouncyCastle.Cryptography.dll"] = "E5EEAF6D263C493619982FD3638E6135077311D08C961E1FE128F9107D29EBC6", ["BouncyCastle.Cryptography.dll (x86)"] = "E5EEAF6D263C493619982FD3638E6135077311D08C961E1FE128F9107D29EBC6", - ["CsvColumnizer.dll"] = "D5435B1886C4FA665A59FB647CE488C092CD2BF0587BB219B3A623C656BA8BF6", - ["CsvColumnizer.dll (x86)"] = "D5435B1886C4FA665A59FB647CE488C092CD2BF0587BB219B3A623C656BA8BF6", - ["DefaultPlugins.dll"] = "675EACAA0B1700FC6163ED29058124553DF9D6E935020CF04B38DAA9F33C279C", - ["FlashIconHighlighter.dll"] = "47605D179B3646F81BF44E8626251CDD30752C0535293AF4C4AA89A1CAF76B06", - ["GlassfishColumnizer.dll"] = "1C30B9D350C46B33A76C96C88DA4B9DCF2B1474BB1ACD92461878365F704A2EC", - ["JsonColumnizer.dll"] = "27F5ED050CEDBC1BD72250DD7C9FC330DB32825FE04FB9C899376BF45F347D92", - ["JsonCompactColumnizer.dll"] = "08F189DE85381709A10DD506DA2DC1F4D9885C68AD0A63799557166295CAD590", - ["Log4jXmlColumnizer.dll"] = "D21498D4B727AA8F2DAF59CD470AEEB11603FF64936E8AB11EDD92A1370BC681", - ["LogExpert.Resources.dll"] = "C8390BB994FACB178D34EC2023F8862F685489549F52726FD9E742B6497B7781", + ["CsvColumnizer.dll"] = "E5020059F5ADC71066E519EB94DF5C2979D3135FAC6E2923B50C3FBB62822987", + ["CsvColumnizer.dll (x86)"] = "E5020059F5ADC71066E519EB94DF5C2979D3135FAC6E2923B50C3FBB62822987", + ["DefaultPlugins.dll"] = "DA4B2B5CFBF0B0FF928C3A1155F94E26622509DADEBDB03D25C25CC8238CC1D0", + ["FlashIconHighlighter.dll"] = "DE097E728DB95A93703DBA54E3D69AADC4B9F1567C89CD2AAF5B942BD4D363E4", + ["GlassfishColumnizer.dll"] = "4A6A43B08AF6808AEC6727B3EAA5D287775FDA16D0443BB113A8ABA2AD426F0A", + ["JsonColumnizer.dll"] = "36325E7C57B08F57DD42EC666AE9072CBC0B0BD407680063CFCBF3696A1D1429", + ["JsonCompactColumnizer.dll"] = "B2301944FB40C4ECC1CD9591AEF49666D394A0D1EC0C08B713D057B0E2569297", + ["Log4jXmlColumnizer.dll"] = "B10511AED306EAC7B875E8AE91CDC2A906FC1B1EF407CD908AAC8530DF52F4D6", + ["LogExpert.Resources.dll"] = "F4ACED3693B4A21BAEE1840FCD039134D78B758C2935561D4B42AF2D1354D6EE", ["Microsoft.Extensions.DependencyInjection.Abstractions.dll"] = "67FA4325000DB017DC0C35829B416F024F042D24EFB868BCF17A895EE6500A93", ["Microsoft.Extensions.DependencyInjection.Abstractions.dll (x86)"] = "67FA4325000DB017DC0C35829B416F024F042D24EFB868BCF17A895EE6500A93", ["Microsoft.Extensions.Logging.Abstractions.dll"] = "BB853130F5AFAF335BE7858D661F8212EC653835100F5A4E3AA2C66A4D4F685D", ["Microsoft.Extensions.Logging.Abstractions.dll (x86)"] = "BB853130F5AFAF335BE7858D661F8212EC653835100F5A4E3AA2C66A4D4F685D", - ["RegexColumnizer.dll"] = "76A856BE58511F40B5E89D7F100AA7843AC62CD380717F924CCFDC234375BDAD", - ["SftpFileSystem.dll"] = "73D71AF4831468722C327DE246879343B9F95E4BA931B07F03A4B60C70B4A2A2", - ["SftpFileSystem.dll (x86)"] = "107BD9DF13EB1C0FB7F8B4C1B0D77B79A62A804FEA5F656FFADEBCDE3FFD4E5B", - ["SftpFileSystem.Resources.dll"] = "12539CBB43138C4D1AF4FFFC0D2D6370B6969E358DF9A03FD29720CA18D34287", - ["SftpFileSystem.Resources.dll (x86)"] = "12539CBB43138C4D1AF4FFFC0D2D6370B6969E358DF9A03FD29720CA18D34287", + ["RegexColumnizer.dll"] = "D8665A762EECCB8516C8D51A0D7099D53C8C515A66DB6D88AA86F60484E6CBB1", + ["SftpFileSystem.dll"] = "D35385D7B4A20946A1CF987C277682FD096FD93A3BB90A4E7944D41556DEED77", + ["SftpFileSystem.dll (x86)"] = "0C9AA4F229A76476257288E683D41830A09AC0E3945A717823193B579A7B3EEB", + ["SftpFileSystem.Resources.dll"] = "EF5A68C3448B3BEDD796BE2BDA85BE7E9242D6A1F63C207D95305BE325E48FCF", + ["SftpFileSystem.Resources.dll (x86)"] = "EF5A68C3448B3BEDD796BE2BDA85BE7E9242D6A1F63C207D95305BE325E48FCF", }; }