Why does the inner exception reach the ThreadException handler and not the actual thrown exception?

The RunWorkerCompleted event is marshaled from the BGW thread to the UI thread by the WF plumbing that makes Control.Invoke() work. Essentially, there’s a queue with delegates that is emptied by the message loop. The code that does this, Control.InvokeMarshaledCallbacks(), you’ll see it on the call stack, has a catch (Exception) clause to catch unhandled exceptions. That clause calls Application.OnThreadException, passing the value of Exception.GetBaseException().

Well, that explains why you only see the inner exception. Why it is done this way is a bit unclear. Possibly to slice off the stack frames of the code in the UI thread that are otherwise pretty confusing since the real exception came from the background thread.

Leave a Comment