Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

CoqBlog

.NET is good :-)
{ Blog de coq }

Actualités

A propos des notifications d’exceptions non gérées - 3 - Zoom sur Application.ThreadException (WinForms)

Cet article est composé de plusieurs parties :

 

L’évènement System.Windows.Forms.Application.ThreadException (WinForms)

Cet évènement static de la classe Application permet de traiter les éventuelles exceptions non gérées levées dans le thread de l’interface graphique, celui sur lequel la boucle de message est démarrée (et donc celui à partir duquel l’abonnement a été effectué).
Les exceptions levées sur un éventuel autre thread d’UI ne seront pas notifiées sur le gestionnaire d’évènement Application.ThreadException du premier.

De plus, il est important de garder à l’esprit que certains paramétrages peuvent jouer sur le comportement de cet évènement (dans le code via Application.SetUnhandledExceptionMode) mais aussi sur le comportement de l’application si aucun gestionnaire n’est associé à l’évènement Application.ThreadException (en fichier de configuration via l’attribut “jitDebugging” de l’élément “system.windows.forms”).

Un point important à garder à l’esprit à propos de Application.ThreadException est qu’il permet à l’application de survivre à l’exception non gérée, contrairement à AppDomain.UnhandledException qui ne fonctionne qu’en tant que notification.

S’abonner plusieurs fois à l’évènement Application.ThreadException ne sert à rien : à l’heure actuelle tout abonnement à l’évènement écrase le précédent, il n’y a pas combinaison de delegate.

Exemple d’utilisation :

static class Program
{

 
/// <summary>
 
/// The main entry point for the application.
 
/// </summary>
  [STAThread]

 
static void Main()

 
{

   
// Définition du "mode application"
    Application.SetUnhandledExceptionMode(

     
UnhandledExceptionMode.ThrowException,
 
     
false);


   
// Définition du "mode thread"
    Application.SetUnhandledExceptionMode(

     
UnhandledExceptionMode.CatchException,
 
     
true);


   
// Abonnement à l'event
    Application.ThreadException += Application_ThreadException;


   
Application.EnableVisualStyles();

   
Application.SetCompatibleTextRenderingDefault(false);

    
   
// Lancement de la boucle de message sur le thread courant
    Application.Run(new Form1());

 
}
 

 
static void Application_ThreadException(Object sender, ThreadExceptionEventArgs e)

 
{

   
// TODO : code de traitement de l'exception non gérée
  }

}

 

Quelques notes sur la méthode Application.SetUnhandledExceptionMode

Cette méthode permet de définir 2 choses :

  • le mode de traitement utilisé pour les exceptions non gérées qui sont levées dans le thread d’UI courant, c’est à dire celui appelant la méthode
  • le mode de traitement utilisé par défaut pour tout nouveau thread d’UI dont le mode est laissé à Automatic

Les différents modes disponibles sont définis par l’énumération UnhandledExceptionMode :

  • Automatic : toutes les exceptions non gérées sur le thread d’UI provoquent la levée de l’évènement AppDomain.UnhandledException, sauf si le fichier de configuration spécifie le contraire
  • CatchException : toutes les exceptions non gérées sur le thread d’UI provoquent la levée de l’évènement AppDomain.UnhandledException, peu importe ce qui est spécifié en fichier de configuration
  • ThrowException : AppDomain.UnhandledException ne sera jamais levé, peu importe ce qui est spécifié en fichier de configuration

 

La surcharge Application.SetUnhandledExceptionMode(UnhandledExceptionMode) se contente d’appeler Application.SetUnhandledExceptionMode(UnhandledExceptionMode, Boolean) avec la valeur true pour le paramètre threadScope.

Le paramètre threadScope de Application.SetUnhandledExceptionMode(UnhandledExceptionMode, Boolean) sert à définir si nous modifions le mode du thread courant ou le mode “application”, c’est-à-dire la valeur par défaut utilisée pour les threads démarrés après le thread effectuant l’appel : si le nouveau thread d’UI ne spécifie pas de mode thread (il est donc en mode thread Automatic) et que le mode application est ThrowException alors le mode du nouveau thread sera au final ThrowException.
Naturellement, si le thread lancé effectue une affectation de mode sur son scope (threadScope à true) avec une valeur différente de Automatic, c’est cette dernière qui prévaudra.

Il faudra aussi tenir compte de l’effet de l’attribut jitDebugging qui va influer sur le comportement lorsque le mode Automatic est actif à la fois sur le mode application et le mode thread.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

 
<system.windows.forms jitDebugging
="true"/>
</
configuration>

Sa valeur va déterminer si il y a affichage de la boite de dialogue permettant à l’utilisateur de faire un choix (figure 1) ou celle de crash (figure 2) dépendant de l’OS et de la configuration de reporting d’erreur de l’utilisateur. A noter que si le reporting d’erreur est en mode désactivé sans notification, le fait de paramétrer jitDebugging à true ne permettra pas de forcer son apparition : l’application disparaitra sans notification.

Figure 1 :
Figure 1 : Boite de dialogue d'exception non gérée

Figure 2 (Windows XP SP3) :
Figure 2 : fenêtre de crash avec reporting activé pour les programmes
Figure 2 : fenêtre de crash avec reporting désactivé mais avec notification de crash

 

Le tableau suivant tente de résumer ce qui se passe :

Tableau décrivant le comportement de Application.ThreadException

 

 

Petite remarque sur l’affectation du mode application :
Plus haut, je disais que le paramètre threadScope permet de modifier le mode du thread courant ou le mode “application”, c’est-à-dire la valeur par défaut utilisée pour les threads démarrés après le thread effectuant l’appel : il ne s’agit en fait pas réellement d’une valeur devenant immuable au moment du démarrage du thread.
On parle plutôt, dans les faits (mais nous ne sommes pas à l‘abri d’un changement interne), d’effectuer cette modification de valeur par défaut avant l’appel à la méthode NativeWindow.AssignHandle (y compris, et surtout, la surcharge internal). C’est à dire, du point de vue du lancement de la boucle de message, de l’appel à Application.Run.
Dans l’absolu si depuis votre thread principal vous lancez 10 threads dont vous bloquez l’exécution avant leur appel à Application.Run, puis que le thread principal modifie la valeur par défaut avant de débloquer les autres threads, les 10 threads en question bénéficieront de la nouvelle valeur par défaut, pas de celle en vigueur au moment de leur lancement.
La documentation dit que la valeur du mode application ne peut plus être changée après qu’un thread quelconque a crée sa première fenêtre (soit de manière générale un contrôle, “fenêtre” != “Form”) dans l’application : par “application” nous parlons ici du domaine d’application (AppDomain), pas du processus.
Donc faites attention à ce qu’un thread ne déclenche pas un appel direct ou indirect à NativeWindow.AssignHandle avant votre appel, bien que logiquement si vous avez besoin de changer cette valeur par défaut, vous allez le faire avant que le thread principal en démarre un autre.
Si vous le faites après, vous pouvez être soumis à des comportements différents (allant jusqu’au crash si vous ne gérez pas l’exception InvalidOperationException qui peut être levée) suivant les machines, leur nombre de CPU/Core, leur charge de travail etc

La documentation dit aussi que le changement du mode application ne modifie pas le mode du thread courant (“Setting the application exception mode does not affect the setting of the current thread.”). Attention avec cette phrase : ce n’est à mon avis pas totalement vrai, ou c’est du moins assez ambigu.
En effet, selon mes observations (et c’est assez logique si on regarde l’exemple des 10 threads abordé ci-dessus), si vous changez le mode application (pour un mode CatchException ou ThrowException) et que le thread courant est sur un mode Automatic, il héritera au final du mode application changé et pas de celui qui était défini à son lancement comme peut le laisser entendre la documentation.

 

La suite : 4 - Zoom sur Application.DispatcherUnhandledException et Dispatcher.UnhandledException (WPF)

Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :
Posted: dimanche 26 octobre 2008 12:59 par coq
Classé sous : , ,

Commentaires

Pas de commentaires

Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- « Naviguer vers le haut » dans une librairie SharePoint par Blog de Jérémy Jeanson le 10-07-2014, 13:21

- PowerShell: Comment mixer NAGIOS et PowerShell pour le monitoring applicatif par Blog Technique de Romelard Fabrice le 10-07-2014, 11:43

- ReBUILD 2014 : les présentations par Le blog de Patrick [MVP Office 365] le 10-06-2014, 09:15

- II6 Management Compatibility présente dans Windows Server Technical Preview avec IIS8 par Blog de Jérémy Jeanson le 10-05-2014, 17:37

- Soft Restart sur Windows Server Technical Preview par Blog de Jérémy Jeanson le 10-03-2014, 19:43

- Non, le certificat public du CA n’est pas un certificat client !!! par Blog de Jérémy Jeanson le 10-03-2014, 00:08

- Windows Server Technical Preview disponible via MSDN par Blog de Jérémy Jeanson le 10-02-2014, 19:05

- Focus Sauvegardes SharePoint par Le blog de Patrick [MVP Office 365] le 10-02-2014, 13:11

- Technofolies, votre évènement numérique de l'année par Le Blog (Vert) d'Arnaud JUND le 09-26-2014, 18:40

- Xamarin : From Zero to Hero par Fathi Bellahcene le 09-24-2014, 17:35