2008-12-07 21 views
13

Estoy viendo un comportamiento extraño al lanzar excepciones y atraparlas en el controlador de eventos Application.ThreadException.¿Por qué la excepción interna llega al controlador ThreadException y no a la excepción lanzada real?

Básicamente lo que está sucediendo en el ejemplo siguiente es que se produce una excepción en el controlador de eventos DoWork de BackgroundWorker. El controlador de eventos RunWorkerCompleted vuelve a lanzar una nueva excepción con el original como la excepción interna.

¿Por qué aparece la excepción interna en el controlador de eventos ThreadException y no se produce la excepción acutal? Si no proporciono una excepción interna en el controlador de eventos RunWorkerCompleted, se mostrará la excepción correcta.

using System; 
using System.Windows.Forms; 
using System.ComponentModel; 

namespace WierdExceptionApp 
{ 
    class WierdExceptionForm : Form 
    { 
     BackgroundWorker worker = new BackgroundWorker(); 

     public WierdExceptionForm() 
     { 
      worker.DoWork += new DoWorkEventHandler(worker_DoWork); 
      worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); 
      worker.RunWorkerAsync(); 
     } 

     void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
     { 
      if (e.Error != null) 
      { 
       throw new Exception("worker_RunWorkerCompleted", e.Error); 
      } 
     } 

     void worker_DoWork(object sender, DoWorkEventArgs e) 
     { 
      throw new Exception("worker_DoWork"); 
     } 

     [STAThread] 
     static void Main() 
     { 
      Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException); 
      Application.Run(new WierdExceptionForm()); 
     } 

     static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) 
     { 
      MessageBox.Show(e.Exception.Message); 
     } 
    } 
} 
+0

Astounding; esto me ha estado picando durante años sin que me haya dado cuenta. Veía estos informes ocasionales de errores por cosas que podría jurar que manejé. Eran lo suficientemente raros como para no pasar mucho tiempo buscándolos, pero finalmente comenzó a suceder algo significativo, y me di cuenta de que la excepción estaba cambiando a lo largo del camino. ¿WTF? –

Respuesta

9

El evento RunWorkerCompleted se calcula desde el hilo BGW al subproceso de la interfaz de usuario por la fontanería WF que hace que funcione Control.Invoke(). Básicamente, hay una cola con delegados que se vacía por el ciclo de mensajes. El código que hace esto, Control.InvokeMarshaledCallbacks(), lo verá en la pila de llamadas, tiene una cláusula catch (Exception) para capturar las excepciones no controladas. Esa cláusula llama a Application.OnThreadException, pasando el valor de Exception.GetBaseException().

Bueno, eso explica por qué solo ves la excepción interna. Por qué se hace de esta manera es un poco confuso. Posiblemente para cortar los fotogramas de pila del código en el subproceso de interfaz de usuario que, de lo contrario, son bastante confusos ya que la excepción real provino del hilo de fondo.

+8

Sin embargo, * otro * error en WinForms que Microsoft casi nunca soluciona. – Joshua

+1

¡Qué estúpido es eso! MS ciertamente nunca arreglará esto, ya que sería un "cambio radical" en sus ojos. Todavía permite ir a https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=433765 y registrar el problema de todos modos. –

0
 if (e.Error != null) 
     { 
      throw new Exception("worker_RunWorkerCompleted", new Exception("Inner", new Exception("Inner inner"))); 
     } 

que se obtiene "interno interno" al final. Parece que este es el comportamiento del método Application_ThreadException para ver la excepción más interna.

Cuestiones relacionadas