Control.EndInvoke resets call stack for exception

I do not deal with a large number of programs for the Windows GUI, so all this can be well known to people more familiar with WinForms than I am. Unfortunately, I could not find any resources to explain the problems that I encountered today during debugging.

If we call EndInvoke for the async delegate. We will get any exception that occurred during the execution of the re-throw method. The call stack will reflect the original source of the exception.

However, if we do something similar in Windows.Forms.Control, the Control.EndInvoke implementation drops the call stack. This can be observed by a simple test or by viewing the code in Reflector. The relevant code excerpt from EndInvoke is here:

if (entry.exception != null) { throw entry.exception; } 

I understand that Begin / EndInvoke on Control and asynchronous delegates are different, but I would expect similar behavior on Control.EndInvoke.

Is there any reason the control does not do what asynchronous input delegates do to keep the original call stack?

+7
asynchronous exception winforms
source share
4 answers

I don’t know the real reason, but I can guess that asynchronous delegates are akin to RPC, while management delegates can be based on sending Win32 messages. Different technologies, so the effect of this function may not be the same. Async Assistant will benefit from all the remote access code for which the developer would write code to pass an exception call stack between different processes or computers, while management delegates would simulate RPC with PostMessage within the same process. Different teams, different codes.

+1
source share

Note that Control.EndInvoke is one of the few managed EndInvokes in the Framework (so you can see the code in Reflector). They probably should have an unmanaged helper that drops the original stack in place.

In fact, I think this is the only managed EndInvoke , but there are other managed End* managed routines with the IAsyncResult parameter. I have not tested all of them, but it seems that all those that I have examined just throw an exception or effectively use the Stephen Cleary distraction solution to use .NET 4 GetWaiter.GetResult , which has some managed and unmanaged shenanigans to try to restore stack for exceptions.

+1
source share

I'm not sure why Control does not do this (maybe just supervision), but you can get around it in .NET 4.0 by scheduling a task in the form of a user interface:

  private BackgroundWorker bgw; private TaskFactory uiTaskFactory; private void Form1_Load(object sender, EventArgs e) { this.uiTaskFactory = new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext()); this.bgw = new BackgroundWorker(); this.bgw.DoWork += bgw_DoWork; this.bgw.RunWorkerAsync(); } void bgw_DoWork(object sender, DoWorkEventArgs e) { var task = this.uiTaskFactory.StartNew(this.OuterTaskFunction); try { task.Wait(); } catch (Exception ex) { // Note: Full stack trace preserved MessageBox.Show(ex.InnerException.ToString()); } } void OuterTaskFunction() { this.InnerTaskFunction(); } void InnerTaskFunction() { throw new InvalidOperationException("Blah."); } 
+1
source share

I have not read 100% of your post, so I'm not sure if this helps, or I just say the obvious things, but when the exception is caught and you write

"throw iAmAnCaughtExceptionInstance;"

the call stack will not be saved, you just have to write

"throw";

and then the call stack is saved

-3
source share

All Articles