From 4ef69e91a52398d24786356e36070a51864a51b5 Mon Sep 17 00:00:00 2001 From: David Maas Date: Tue, 23 Apr 2024 16:44:31 -0500 Subject: [PATCH] Fix workflows run from the command line not exiting upon completion. This also indirectly fixes exceptions during cleanup getting swallowed unceremoniously. Also fixes leaked NotifyIcon. Fixes https://github.com/bonsai-rx/bonsai/issues/1740 --- Bonsai.Editor/WorkflowRunner.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Bonsai.Editor/WorkflowRunner.cs b/Bonsai.Editor/WorkflowRunner.cs index 8af0ae442..e3ed7e0a4 100644 --- a/Bonsai.Editor/WorkflowRunner.cs +++ b/Bonsai.Editor/WorkflowRunner.cs @@ -70,20 +70,28 @@ editorSettings.Tag is ExpressionBuilder builder && contextMenu.Items.Add(new ToolStripSeparator()); contextMenu.Items.Add(new ToolStripMenuItem("Stop", null, (sender, e) => cts.Cancel())); - var notifyIcon = new NotifyIcon(); + using var notifyIcon = new NotifyIcon(); notifyIcon.Icon = Properties.Resources.Icon; notifyIcon.Text = Path.GetFileName(fileName); notifyIcon.ContextMenuStrip = contextMenu; notifyIcon.Visible = true; + + using var synchronizationContext = new WindowsFormsSynchronizationContext(); runtimeWorkflow.Finally(() => { - notifyIcon.Visible = false; - Application.Exit(); + // Posting the exit to the main thread's winforms sync context is important for two reasons: + // 1) When this finally action executes on the main thread we need to defer exiting until + // Application.Run, otherwise we're trying to exit a message loop which hasn't even started. + // 2) When this finally action executes it will be on a background thread, we need to exit from the main + // thread. While Application.Exit can be called from any thread, it directly calls FormClosed callbacks + // of any open forms, and many visualizers assume they'll only be called from the main thread. + synchronizationContext.Post(_ => Application.Exit(), null); }).Subscribe( unit => { }, ex => { Console.Error.WriteLine(ex); }, () => { }, cts.Token); + Application.Run(); }