VS2010 n’affiche pas le message d’exception non géré dans une application WinForms sur une version 64 bits de Windows

Lorsque je crée un nouveau projet, j’obtiens un comportement étrange pour les exceptions non gérées. Voici comment je peux reproduire le problème:

1) créer une nouvelle application Windows Forms (C #, .NET Framework 4, VS2010)

2) ajoutez le code suivant au gestionnaire Form1_Load :

 int vara = 5, varb = 0; int varc = vara / varb; int vard = 7; 

Je m’attendrais à ce que VS casse et affiche un message d’exception non géré à la deuxième ligne. Cependant, la troisième ligne est simplement ignorée sans message et l’application continue de fonctionner.

Je n’ai pas ce problème avec mes projets C # existants. Donc, je suppose que mes nouveaux projets sont créés avec des parameters par défaut étranges.

Est-ce que quelqu’un a une idée de ce qui ne va pas avec mon projet ???

J’ai essayé de cocher les cases dans Debug-> Exceptions. Mais alors les exécutions se brisent même si je gère l’exception dans un bloc try-catch ; ce qui n’est pas non plus ce que je veux. Si je me souviens bien, il y avait une colonne appelée “exceptions non gérées” ou quelque chose du genre dans cette boîte de dialog, qui ferait exactement ce que je veux. Mais dans mes projets, il n’y a qu’une seule colonne (“Thrown”).

C’est un problème désagréable induit par la couche d’émulation wow64 qui permet au code 32 bits de s’exécuter sur la version 64 bits de Windows 7. Elle engloutit les exceptions dans le code qui s’exécute en réponse à une notification générée par le gestionnaire de fenêtres 64 bits. , comme l’événement Load . Empêcher le débogueur de le voir et d’intervenir. Ce problème est difficile à résoudre, les groupes Windows et DevDiv chez Microsoft vont de l’avant. DevDiv ne peut rien y faire, Windows pense que c’est le comportement correct et documenté, aussi mystérieux que cela puisse paraître.

Il est certainement documenté mais à peu près personne ne comprend les conséquences ou pense que c’est un comportement raisonnable. Surtout pas lorsque la procédure de la fenêtre est masquée, bien sûr, comme dans tout projet qui utilise des classes d’encapsulation pour masquer la plomberie de la fenêtre. Comme toute application Winforms, WPF ou MFC. Le problème sous-jacent est que Microsoft n’a pas trouvé comment transférer les exceptions du code 32 bits vers le code 64 bits qui a déclenché la notification en code 32 bits qui tente de gérer ou de déboguer l’exception.

Ce n’est qu’un problème avec un débogueur attaché, votre code va bombarder comme d’habitude sans un.

Projet> Propriétés> onglet Construire> Cible de plate-forme = AnyCPU et décochez Préférer 32 bits. Votre application fonctionnera désormais en tant que processus 64 bits, éliminant le mode de défaillance wow64. Quelques conséquences, il désactive Edit + Continue pour les versions VS antérieures à VS2013 et peut ne pas être toujours possible lorsque vous avez une dépendance sur le code 32 bits.

Autres solutions possibles:

  • Debug> Exceptions> cochez la case Thrown pour les exceptions CLR afin de forcer le débogueur à s’arrêter à la ligne de code qui génère l’exception.
  • Écrivez try / catch dans le gestionnaire d’événements Load et failfast dans le bloc catch.
  • Utilisez Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException) dans la méthode Main() afin que l’ Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException) exception dans la boucle de message ne soit pas désactivée en mode débogage. Cela rend cependant toutes les exceptions non ThreadException difficiles à déboguer, l’événement ThreadException est plutôt inutile.
  • Déterminez si votre code appartient vraiment au gestionnaire d’événements Load . Il est très rare d’en avoir besoin, il est cependant très populaire dans VB.NET et un morceau de cygne car c’est l’événement par défaut et un double-clic ajoute sortingvialement le gestionnaire d’événement. Vous n’avez vraiment besoin de Load lorsque vous êtes intéressé par la taille réelle de la fenêtre après les préférences de l’utilisateur et la mise à l’échelle automatique. Tout le rest appartient au constructeur.
  • Mise à jour vers Windows 8 ou version ultérieure, ils ont résolu ce problème wow64.

D’après mon expérience, je ne vois ce problème que lorsque j’utilise un débogueur connecté. L’application se comporte de la même façon lorsqu’elle est exécutée en mode autonome: l’exception n’est pas avalée.

Avec l’introduction de KB976038 , vous pouvez faire en sorte que cela fonctionne à nouveau. Je n’ai jamais installé le correctif, donc je suppose qu’il fait partie de Win7 SP1.

Cela a été mentionné dans ce post:

  • Le cas de l’exception OnLoad en voie de disparition – exceptions de rappel en mode utilisateur dans x64

Voici un code qui activera le correctif:

 public static class Kernel32 { public const uint PROCESS_CALLBACK_FILTER_ENABLED = 0x1; [DllImport("Kernel32.dll")] public static extern bool SetProcessUserModeExceptionPolicy(UInt32 dwFlags); [DllImport("Kernel32.dll")] public static extern bool GetProcessUserModeExceptionPolicy(out UInt32 lpFlags); public static void DisableUMCallbackFilter() { uint flags; GetProcessUserModeExceptionPolicy(out flags); flags &= ~PROCESS_CALLBACK_FILTER_ENABLED; SetProcessUserModeExceptionPolicy(flags); } } 

Appelez-le au début de votre application:

  [STAThread] static void Main() { Kernel32.DisableUMCallbackFilter(); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } 

J’ai confirmé (avec l’exemple simple montré ci-dessous) que cela fonctionne, comme on peut s’y attendre.

 protected override void OnLoad(EventArgs e) { throw new Exception("BOOM"); // This will now get caught. } 

Donc, ce que je ne comprends pas, c’est pourquoi il était auparavant impossible pour le débogueur de gérer le croisement des frameworks de stack en mode kernel, mais avec ce correctif, ils l’ont en quelque sorte compris.

Comme Hans le mentionne, comstackz l’application et exécutez le fichier EXE sans débogueur associé.

Pour moi, le problème était de changer un nom de propriété de classe auquel un contrôle BindingSource était lié. En cours d’exécution sans l’IDE, j’ai pu voir l’erreur:

Impossible de lier la propriété ou la colonne SendWithoutProofReading sur la source de données. Nom du paramètre: dataMember

La correction du contrôle BindingSource pour qu’il se lie au nom de propriété mis à jour a résolu le problème: entrer la description de l'image ici

J’utilise WPF et j’ai rencontré ce même problème. J’avais déjà essayé les suggestions de Hans 1-3, mais je ne les aimais pas car studio ne s’arrêtait pas là où se trouvait l’erreur (donc je ne pouvais pas voir mes variables et voir quel était le problème).

J’ai donc essayé la 4ème suggestion de Hans. J’ai été surpris par la quantité de mon code pouvant être déplacé vers le constructeur MainWindow sans aucun problème. Je ne sais pas pourquoi j’ai pris l’habitude de mettre autant de logique dans l’événement Load, mais apparemment, une grande partie peut être faite dans le ctor.

Cependant, cela a eu le même problème que 1-3. Les erreurs qui se produisent pendant le ctor pour WPF sont incorporées dans une exception Xaml générique. (une exception interne a la vraie erreur, mais encore une fois je voulais que le studio se casse juste au point critique).

Ce qui a fini par fonctionner pour moi a été de créer un fil de discussion, de dormir 50ms, de retourner au fil principal et de faire ce dont j’ai besoin …

  void Window_Loaded(object sender, RoutedEventArgs e) { new Thread(() => { Thread.Sleep(50); CrossThread(() => { OnWindowLoaded(); }); }).Start(); } void CrossThread(Action a) { this.Dispatcher.BeginInvoke(a); } void OnWindowLoaded() { ...do my thing... 

De cette façon, le studio se briserait là où une exception non capturée se produit.

Une Form_Shown simple pourrait être si vous pouviez déplacer votre code d’initialisation vers un autre événement tel que Form_Shown qui appelait plus tard que Form_Load et utiliser un indicateur pour exécuter le code de démarrage au premier formulaire affiché:

 bool firstLoad = true; //flag to detect first form_shown private void Form1_Load(object sender, EventArgs e) { //firstLoad = true; //dowork(); //not execute initialization code here (postpone it to form_shown) } private void Form1_Shown(object sender, EventArgs e) { if (firstLoad) //simulate Form-Load { firstLoad = false; dowork(); } } void dowork() { var f = File.OpenRead(@"D:\NoSuchFile756.123"); //this cause an exception! }