Très étrange comportement Application.ThreadException

J’utilise l’événement Application.ThreadException pour gérer et consigner les exceptions inattendues dans mon application Winforms.

Maintenant, quelque part dans mon application, j’ai le code suivant (ou plutôt quelque chose d’équivalent, mais ce code factice suffit à reproduire mon problème):

try { throw new NullReferenceException("test"); } catch (Exception ex) { throw new Exception("test2", ex); } 

Je m’attends clairement à ce que mon gestionnaire Application_ThreadException reçoive l’exception “test2”, mais ce n’est pas toujours le cas. Généralement, si un autre thread dirige mon code vers l’interface utilisateur, mon gestionnaire reçoit l’exception “test”, exactement comme si je n’avais pas capturé “test” du tout.

Voici un court exemple reproduisant ce problème. J’ai omis le code du designer.

  static class Program { [STAThread] static void Main() { Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException); //Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); // has no impact in this scenario, can be commented. AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { //this handler is never called } static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) { Console.WriteLine(e.Exception.Message); } } public partial class Form1 : Form { public Form1() { InitializeComponent(); button1.Click+=new EventHandler(button1_Click); } protected override void OnLoad(EventArgs e) { System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(ThrowEx)); t.Start(); } private void button1_Click(object sender, EventArgs e) { try { throw new NullReferenceException("test"); } catch (Exception ex) { throw new Exception("test2", ex); } } void ThrowEx() { this.BeginInvoke(new EventHandler(button1_Click)); } } 

La sortie de ce programme sur mon ordinateur est:

 test ... here I click button1 test2 

Je l’ai reproduit sur .net 2.0,3.5 et 4.0. Est-ce que quelqu’un a une explication logique?

Il y a un bogue dans votre code qui rend difficile le débogage de ce qui se passe: vous démarrez le thread avant la création du handle du formulaire. Cela fera échouer BeginInvoke. Réparer:

  protected override void OnLoad(EventArgs e) { System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(ThrowEx)); t.Start(); } 

Anyhoo, c’est un comportement conçu. Le code Windows Forms qui exécute la cible BeginInvoke ressemble à ceci:

  try { this.InvokeMarshaledCallback(tme); } catch (Exception exception) { tme.exception = exception.GetBaseException(); } ... if ((!NativeWindow.WndProcShouldBeDebuggable && (tme.exception != null)) && !tme.synchronous) { Application.OnThreadException(tme.exception); } 

C’est l’appel exception.GetBaseException () qui bousille votre message d’exception. Pourquoi les concepteurs Windows Forms ont choisi de faire cela n’est pas tout à fait clair pour moi, il n’y a pas de commentaire avec le code dans la source de référence. Je peux seulement deviner que sans cette exception, l’exception serait plus difficile à déboguer, au cas où elle serait générée par le code de plomberie de Windows Forms au lieu du code de l’application. Pas une bonne explication.

Ils ont déjà dit qu’ils ne régleraient rien , peut-être pourriez-vous append votre vote. Ne lève pas tes espoirs.

La solution de contournement consiste à ne pas définir l’exception InnerException. Pas une bonne option bien sûr.

Exception n ° 1: Invoke ou BeginInvoke ne peuvent pas être appelés sur un contrôle tant que le BeginInvoke fenêtre n’a pas été créé.

Donc, n’essayez pas d’appeler depuis le constructeur. Faites-le dans OnLoad() :

 public partial class Form1 : Form { public Form1() { InitializeComponent(); this.Load += new EventHandler(Form1_Load); button1.Click += new EventHandler(button1_Click); } private void Form1_Load(object sender, EventArgs e) { System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(ThrowEx)); t.Start(); } ... } 

Vous devez appeler

Application.SetUnhandledExceptionMode (UnhandledExceptionMode.CatchException);

d’abord dans votre méthode Main ().