Bug: Only first thrown exception shown in click callback
I came across an obscure bug in a script I had and was able to reproduce:
async Task Main() { var keepRunning = Util.KeepRunning(); var button = new Button("Run"); button.Click += async (_, _) => await Run(); button.Dump(); async Task Run() { try { "Throwing inner".Dump(); throw new Exception("Inner"); } catch (Exception ex) { "Caught inner, throwing outer".Dump(); throw new Exception("Outer", ex); } finally { keepRunning.Dispose(); } } }
After clicking on "Run", the script stops with the "Inner" exception shown. The outer exception is not shown at all. The code did fall into the block as you can see both messages.
I guess the problem was with the asynchronous event handler? When using the button constructor's onClick
handler, the exception isn't shown at all (which is also a problem).
var button = new Button("Run", _ => Run());
Comments
This is not easy to fix because it's actually a bug in WinForms:
https://stackoverflow.com/questions/347502/why-does-the-inner-exception-reach-the-threadexception-handler-and-not-the-actual
(LINQPad does not use BackgroundWorker but the marshaling mechanism appears to be the same for async void.)
LINQPad uses WinForms to provide a message loop and synchronization context so that you can dump WinForms and WPF controls. Obviously in this case you are not dumping WinForms or WPF controls, but LINQPad has no reliable way of knowing that. For instance, you could call SomeExternalLibrary.Method().Dump() inside the event handler of your button, and if SomeExternalLibrary.Method() returned a WinForms or WPF control, it would crash unless a WinForms message loop was already active.