2008-12-07 7 views
13

Sto osservando un comportamento strano quando lancio le eccezioni e le cattura nel gestore di eventi Application.ThreadException.Perché l'eccezione interna raggiunge il gestore ThreadException e non l'effettiva eccezione generata?

Fondamentalmente, cosa sta succedendo nell'esempio seguente è che un'eccezione viene generata nel gestore di eventi DoWork di BackgroundWorker. Il gestore di eventi RunWorkerCompleted ripropone una nuova eccezione con l'originale come eccezione interna.

Perché l'eccezione interna viene visualizzata nel gestore di eventi ThreadException e non viene generata l'eccezione acutale? Se non fornisco un'eccezione interna nel gestore di eventi RunWorkerCompleted, verrà visualizzata l'eccezione corretta.

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

Sorprendente; questo mi ha morso per anni senza che me ne accorgessi. Vedrei questi rapporti di errore occasionali per cose che potrei giurare che ho gestito. Erano abbastanza rari da non passare molto tempo a guardarli, ma alla fine ha iniziato a succedere qualcosa di significativo, e ho capito che l'eccezione stava cambiando lungo la strada. WTF ?? –

risposta

9

L'evento RunWorkerCompleted viene eseguito il marshalling dal thread BGW al thread dell'interfaccia utente dall'impianto idraulico WF che rende Control.Invoke() funzionante. In sostanza, c'è una coda con i delegati che viene svuotata dal ciclo dei messaggi. Il codice che esegue questo, Control.InvokeMarshaledCallbacks(), lo vedrai sullo stack delle chiamate, ha una clausola catch (Exception) per catturare le eccezioni non gestite. Tale clausola chiama Application.OnThreadException, passando il valore di Exception.GetBaseException().

Bene, questo spiega perché si vede solo l'eccezione interna. Perché è fatto in questo modo è un po 'oscuro. È possibile suddividere i frame di stack del codice nel thread dell'interfaccia utente che altrimenti sarebbero piuttosto confusi poiché la vera eccezione proviene dal thread in background.

+8

Eppure * un altro * bug in WinForms che Microsoft sicuramente non risolve mai! – Joshua

+1

Quanto è stupido! La SM sicuramente non risolverà mai questo dato che sarebbe un "cambio di rottura" nei loro occhi. Consente comunque di accedere a https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=433765 e registrare comunque il problema. –

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

si ottiene "interno interno" alla fine. Sembra che questo sia il comportamento del metodo Application_ThreadException per esaminare l'eccezione più interna.

Problemi correlati