web-dev-qa-db-fra.com

Comment traiter les exceptions non gérées? (Arrêtez l'application vs Gardez-la en vie)

Quelle est la meilleure pratique lorsqu'une exception non gérée se produit dans une application de bureau?

Je pensais à montrer un message à l'utilisateur, afin qu'il puisse contacter le support. Je recommanderais à l'utilisateur de redémarrer l'application, mais pas de la forcer. Similaire à ce qui est discuté ici: x.stackexchange.com - Quelle est la meilleure façon de gérer les erreurs d'application inattendues?

Le projet est une application .NET WPF, donc la proposition décrite pourrait ressembler à ceci (Notez qu'il s'agit d'un exemple simplifié. Il serait probablement judicieux de masquer les détails de l'exception jusqu'à ce que l'utilisateur clique sur "Afficher les détails" et fournisse des fonctionnalités à signaler facilement l'erreur):

public partial class App : Application
{
    public App()
    {
        DispatcherUnhandledException += OnDispatcherUnhandledException;
    }

    private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
    {
        LogError(e.Exception);
        MessageBoxResult result = MessageBox.Show(
             $"Please help us fix it and contact [email protected]. Exception details: {e.Exception}" +
                        "We recommend to restart the application. " +
                        "Do you want to stop the application now? (Warning: Unsaved data gets lost).", 
            "Unexpected error occured.", MessageBoxButton.YesNo);

        // Setting 'Handled' to 'true' will prevent the application from terminating.
        e.Handled = result == MessageBoxResult.No;
    }

    private void LogError(Exception ex)
    {
        // Log to a log file...
    }
}

Dans l'implémentation (commandes de ViewModels ou gestionnaire d'événements d'événements externes), je ne capturais alors que l'exception exogène spécifique et je laissais toutes les autres exceptions (exceptionnelles et exceptionnelles) remonter jusqu'au "Gestionnaire de dernier recours" décrit ci-dessus. Pour une définition des exceptions à tête osée et exogènes, consultez: Eric Lippert - Vexing exceptions

Est-il judicieux de laisser l'utilisateur décider si l'application doit être arrêtée? Lorsque l'application est arrêtée, vous n'avez certainement aucun état incohérent. D'autre part, l'utilisateur peut perdre des données non enregistrées ou n'est plus en mesure d'arrêter un processus externe démarré jusqu'à ce que l'application soit redémarrée.

Ou est-ce la décision si vous devez mettre fin à l'application sur des exceptions non gérées en fonction du type d'application que vous écrivez? Est-ce juste un compromis entre "robustesse" et "exactitude" comme décrit dans Code Complete, Second Edition

Pour vous donner un aperçu du type d'application dont nous parlons: L'application est principalement utilisée pour contrôler les instruments de laboratoire de chimie et afficher les résultats mesurés à l'utilisateur. Pour ce faire, les applications WPF communiquent avec certains services (services locaux et distants). L'application WPF ne communique pas directement avec les instruments.

30
Jonas Benz

Vous devez vous attendre à ce que votre programme se termine pour plus de raisons qu'une simple exception non gérée de toute façon, comme une panne de courant ou un processus d'arrière-plan différent qui bloque tout le système. Par conséquent, je recommanderais de terminer et de redémarrer l'application, mais avec certaines mesures pour atténuer les conséquences d'un tel redémarrage et minimiser la perte de données possible.

Commencez par analyser les points suivants:

  • Quelle quantité de données peut effectivement être perdue en cas d'arrêt du programme?

  • Quelle est la gravité d'une telle perte pour l'utilisateur? Les données perdues peuvent-elles être reconstruites en moins de 5 minutes, ou parlons-nous de perdre une journée de travail?

  • Combien d'efforts est-ce pour mettre en œuvre une stratégie de "sauvegarde intermédiaire"? Ne l'excluez pas car "l'utilisateur devrait entrer une raison de changement" lors d'une opération de sauvegarde régulière, comme vous l'avez écrit dans un commentaire. Mieux vaut penser à quelque chose comme un fichier ou un état temporaire, qui peut être rechargé automatiquement après un crash de programme. De nombreux types de logiciels de productivité le font (par exemple, MS Office et LibreOffice ont tous deux une fonction "sauvegarde automatique" et une récupération après incident).

  • Dans le cas où les données étaient erronées ou corrompues, l'utilisateur peut-il le voir facilement (peut-être après un redémarrage du programme)? Si oui, vous pouvez offrir une option pour permettre à l'utilisateur d'enregistrer les données (avec un peu de chance qu'elles soient corrompues), puis forcer un redémarrage, le recharger et laisser l'utilisateur vérifier si les données semblent correctes. Assurez-vous de ne pas écraser la dernière version qui a été enregistrée régulièrement (au lieu d'écrire dans un emplacement/fichier temporaire) pour éviter de corrompre l'ancienne version.

Si une telle stratégie de "sauvegarde intermédiaire" est une option sensée, cela dépend finalement de l'application et de son architecture, ainsi que de la nature et de la structure des données impliquées. Mais si l'utilisateur perd moins de 10 minutes de travail et qu'un tel crash se produit une fois par semaine ou même plus rarement, je n'y investirais probablement pas trop.

47
Doc Brown

Cela dépend dans une certaine mesure de l'application que vous développez, mais en général, je dirais que si votre application rencontre une exception non gérée, vous devez la terminer.

Pourquoi?

Parce que vous ne pouvez plus faire confiance à l'état de l'application.

Certainement, fournissez un message utile à l'utilisateur, mais vous devez finalement mettre fin à l'application.

Compte tenu de votre contexte, je voudrais certainement que la demande se termine. Vous ne voulez pas qu'un logiciel exécuté dans un laboratoire produise une sortie corrompue et comme vous ne pensiez pas gérer l'exception, vous ne savez pas pourquoi il a été lancé et ce qui se passe.

30
Matthew

Étant donné que cela est destiné à un laboratoire de chimie et que votre application ne contrôle pas les instruments directement mais plutôt via d'autres services:

Forcer la résiliation après avoir affiché le message. Après une exception non gérée, votre application est dans un état inconnu. Cela pourrait envoyer des commandes erronées. Il peut même invoquer démons nasaux . Une commande erronée pourrait potentiellement gaspiller des réactifs coûteux ou mettre en danger l'équipement ou les personnes .

Mais vous pouvez faire autre chose: récupérer gracieusement après le redémarrage . Je suppose que votre application ne supprime pas ces services d'arrière-plan lorsqu'elle se bloque. Dans ce cas, vous pouvez facilement récupérer l'état d'eux. Ou, si vous avez plus d'état, pensez à l'enregistrer. Dans un stockage qui a des dispositions pour l'atomicité et l'intégrité des données (SQLite peut-être?).

Éditer:

Comme indiqué dans les commentaires, le processus que vous contrôlez peut nécessiter des modifications assez rapidement pour que l'utilisateur n'ait pas le temps de réagir. Dans ce cas, vous devriez envisager de redémarrer silencieusement l'application en plus de la récupération d'état gracieuse.

12
Jan Dorniak

Essayer de répondre généralement à cette question au niveau supérieur du programme n'est pas un jeu intelligent.

Si quelque chose s'est propagé tout au long du processus, et à aucun moment de l'architecture de l'application, personne n'a considéré ce cas, vous n'avez aucune généralisation à faire sur les actions qui sont ou ne peuvent pas être effectuées en toute sécurité.

Donc, non, ce n'est certainement pas une conception généralement acceptable pour permettre à l'utilisateur de choisir si l'application tente de récupérer ou non, car l'application et les développeurs n'ont manifestement pas fait la diligence nécessaire pour savoir si c'est possible ou même sage .

Cependant, si l'application a des parties à haute valeur de sa logique ou de son comportement qui ont été conçues avec ce type de récupération après défaillance à l'esprit, et qu'il est possible de les exploiter dans ce cas, alors par tous les moyens, faites-le - Dans ce cas , il peut être acceptable d'inviter l'utilisateur à voir s'il souhaite tenter une récupération, ou s'il souhaite simplement l'appeler quitter et recommencer.

Ce type de récupération n'est généralement pas nécessaire ni conseillé pour tous (ou même la plupart) des programmes, mais, si vous travaillez sur un programme pour lequel ce degré d'intégrité opérationnelle est requis, cela pourrait être une circonstance dans laquelle présenter ce type de programme Demander à un utilisateur serait une chose sensée à faire.

À la lumière de toute logique spéciale de récupération après défaillance - Non, ne faites pas cela. Vous n'avez littéralement aucune idée de ce qui se passera, si vous l'aviez fait, vous auriez pris l'exception plus bas et l'auriez gérée.

8
Iron Gremlin

Le problème des "exceptions exceptionnelles", c'est-à-dire des exceptions que vous n'avez pas prévues, est que vous ne savez pas dans quel état se trouve le programme. Par exemple, essayer d'enregistrer les données de l'utilisateur pourrait en fait détruit encore plus de données .

Pour cette raison, vous devez mettre fin à l'application.

Il y a une idée très intéressante appelée Logiciel Crash-only par George Candea et Armando Fox . L'idée est que si vous concevez votre logiciel de manière à ce que la seule façon de le fermer soit de le planter et la seule façon de le démarrer soit de récupérer d'un plantage, alors votre logiciel sera plus résistant et la récupération d'erreur les chemins de code seront testés et exercés de manière beaucoup plus approfondie.

Ils ont eu cette idée après avoir remarqué que certains systèmes ont démarré plus rapidement après un crash qu'après un arrêt ordonné.

Un bon exemple, bien que n'étant plus pertinent, est certaines anciennes versions de Firefox qui non seulement démarrent plus rapidement lors de la récupération après un crash, mais aussi ont une meilleure expérience de démarrage de cette façon ! Dans ces versions, si vous fermez Firefox normalement, il fermerait tous les onglets ouverts et démarrerait avec un seul onglet vide. Alors que lors de la récupération d'un crash, il restaurerait les onglets ouverts au moment du crash. (Et c'était la seule façon de fermer Firefox sans perdre votre contexte de navigation actuel.) Alors, qu'ont fait les gens? Ils n'ont tout simplement jamais fermé Firefox et à la place toujours pkill -KILL firefoxÉditer.

Il y a n bel article sur les logiciels crash uniquement par Valerie Aurora sur Linux Weekly News . Les commentaires méritent également d'être lus. Par exemple, quelqu'un dans les commentaires souligne à juste titre que ces idées ne sont pas nouvelles et sont en fait plus ou moins équivalentes aux principes de conception des applications basées sur Erlang/OTP. Et, bien sûr, en examinant cela aujourd'hui, 10 ans après celui de Valérie et 15 ans après l'article original, nous pourrions remarquer que le battage médiatique actuel des micro-services réinvente encore ces mêmes idées. La conception moderne d'un datacenter à l'échelle du cloud est également un exemple de granularité plus grossière. (Tout ordinateur peut se bloquer à tout moment sans affecter le système.)

Cependant, il ne suffit pas de laisser votre logiciel planter. Il doit être conçu pour cela. Idéalement, votre logiciel serait divisé en petits composants indépendants qui peuvent chacun se bloquer de manière indépendante. En outre, le "mécanisme de plantage" doit être en dehors du composant en cours de plantage.

3
Jörg W Mittag

La bonne façon de gérer la plupart des exceptions devrait être d'invalider tout objet qui pourrait être dans un état corrompu en conséquence, et de poursuivre l'exécution si les objets invalidés ne l'empêchent pas. Par exemple, le paradigme sûr pour la mise à jour d'une ressource serait:

acquire lock
try
  update guarded resource
if exception
  invalidate lock
else
  release lock
end try

Si une exception inattendue se produit lors de la mise à jour de la ressource protégée, la ressource doit être présumée dans un état corrompu et le verrou invalidé, que l'exception soit d'un type qui serait autrement bénin.

Malheureusement, les gardes de ressources implémentés via IDisposable/using seront libérés à chaque sortie du bloc gardé, sans aucun moyen de savoir si le bloc est sorti normalement ou anormalement. Ainsi, même s'il devrait y avoir des critères bien définis pour savoir quand continuer après une exception, il n'y a aucun moyen de dire quand ils s'appliquent.

1
supercat

Vous pouvez utiliser l'approche suivie par chaque application iOS et MacOS: une exception non capturée supprime immédiatement l'application. De plus, de nombreuses erreurs, comme un tableau hors limites ou simplement un dépassement arithmétique dans les applications plus récentes, font de même. Pas d'avertissement.

D'après mon expérience, de nombreux utilisateurs ne le remarquent pas, mais appuyez à nouveau sur l'icône de l'application.

De toute évidence, vous devez vous assurer qu'un tel plantage n'entraîne pas de perte de données importante et ne conduit certainement pas à des erreurs coûteuses. Mais une alerte "Votre application va planter maintenant. Appelez l'assistance si cela vous dérange "n'aide personne.

0
gnasher729