2008-12-07 11 views
13

Widzę pewne dziwne zachowanie podczas rzucania wyjątków i łapania ich w obsłudze zdarzeń Application.ThreadException.Dlaczego wyjątek wewnętrzny osiąga obsługę wątku ThreadException, a nie rzeczywisty zgłoszony wyjątek?

Zasadniczo, co dzieje się w poniższym przykładzie, wyjątek jest zgłaszany do obsługi zdarzeń DoWork z BackgroundWorker. Obsługa zdarzeń RunWorkerCompleted ponownie rzuca nowy wyjątek z oryginałem jako wewnętrznym wyjątkiem.

Dlaczego wewnętrzny wyjątek pojawia się w procedurze obsługi zdarzeń ThreadException, a nie wyjątek acutal jest zgłaszany? Jeśli nie udostępnię wewnętrznego wyjątku w module obsługi zdarzeń RunWorkerCompleted, pojawi się poprawny wyjątek.

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

Zdumiewające; to mnie gryzie przez lata, nie zdając sobie z tego sprawy. Widziałbym te okazjonalne raporty o błędach dotyczące rzeczy, które mogłem przeklinać. Rzadko zdarzało się, że nie spędzali zbyt wiele czasu na ich zaglądanie, ale w końcu zaczęło się to dziać z czymś znaczącym i zdałem sobie sprawę, że wyjątek zmienia się po drodze. WTF ?? –

Odpowiedz

9

Zdarzenie RunWorkerCompleted jest transportowane z wątku BGW do wątku interfejsu użytkownika przez instalację WF, która powoduje, że funkcja Control.Invoke() działa. Zasadniczo istnieje kolejka z delegatami, która jest opróżniana przez pętlę wiadomości. Kod, który to robi, Control.InvokeMarshaledCallbacks(), zobaczysz go na stosie wywołań, ma klauzulę catch (Exception) do przechwytywania nieobsługiwanych wyjątków. Ta klauzula nazywa Application.OnThreadException, przekazując wartość Exception.GetBaseException().

To wyjaśnia, dlaczego widzisz tylko wewnętrzny wyjątek. Dlaczego jest to robione w ten sposób, jest nieco niejasne. Prawdopodobnie, aby odciąć ramki stosów kodu w wątku UI, które w przeciwnym razie są dość mylące, ponieważ prawdziwy wyjątek pochodzi z wątku tła.

+8

Jeszcze * inny * błąd w WinForm, którego Microsoft prawie nigdy nie naprawi! – Joshua

+1

Jakie to głupie! MS na pewno nigdy tego nie naprawi, ponieważ byłoby to "przełomowa zmiana" w ich oczach. Nadal pozwala przejść do https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=433765 i mimo to zarejestrować problem. –

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

dostać "wewnętrzny wewnętrzny" na końcu. Wydaje się, że jest to zachowanie metody Application_ThreadException, aby spojrzeć na najbardziej wewnętrzny wyjątek.

Powiązane problemy