Gestion des erreurs en VBA

mromain

XLDnaute Barbatruc
Bonjour à tous, ce post est dédié à présenter une méthode de gestion des erreurs en VBA que j’utilise depuis quelques temps sur mes projets (professionnels et personnels).

Précédemment, j’utilisais cette méthode classique :
VB:
Sub Procedure()
    On Error GoTo GestionErreur
 
    'code "métier"
 
 
QuitterProcedure:
    On Error Resume Next
    'fermer proprement la procédure :
     '  > détruire les objets
     '  > fermer les fichiers ouverts au sein de la procédure
     '  > ...
 
    Exit Sub
 
GestErreur
    MsgBox "Erreur n° " & Err.Number & " : " & Err.Description
    GoTo QuitterProcedure
End Sub

Cette méthode a pour avantage le fait qu’on passe forcément dans la zone QuitterProcedure, qu’une erreur se soit déclenchée ou pas.
Elle a comme inconvénient le fait que le programme ne sait pas si une erreur est survenue dans une sous-procédure appelée.


Cette contrainte implique le fait qu’il faille, à chaque appel de sous-procédure, contrôler si celle-ci s’est bien déroulée ou pas.

La méthode présentée ici permet entre autres de résoudre ce problème. Si une erreur survient dans une sous-procédure, celle-ci est renvoyée à la procédure appelante.
L’affichage de l’erreur en question se fait dans la procédure de plus haut niveau.


Cette gestion des erreurs permet également de tracer dans quelle procédure l’erreur est survenue.
Dans cet exemple schématisé, on est bien passé dans les zones QuitterProcedure des procédures :
  • Sous-procédure 2
  • Sous-procédure 1
  • Procédure principale
Vu qu’une erreur est survenue avant son appel, la Sous-procédure 3 n’a pas été appelée.

Concrètement, voir le code de l’exemple n°1 du fichier joint.

Un autre avantage de cette gestion des erreurs est qu’on peut choisir d’envoyer des erreurs au sein même du code. Par exemple, plutôt que d’écrire ce code :
VB:
If Dir(l_s_filePath) = vbNullString Then
    MsgBox "Le fichier '" & l_s_filePath & "' n'existe pas."
Else
    'ouvrir le  fichier et le traiter
     '...
     '...
End If
On peut plutôt déclencher une erreur utilisateur (G_L_ERR_USER) :
VB:
 l_s_filePath = InputBox("Saisir un emplacement de fichier :", "Saisie utilisateur")
If Dir(l_s_filePath) = vbNullString Then _
    Err.Raise G_L_ERR_USER, , "Le fichier '" & l_s_filePath & "' n'existe pas."

'ouvrir le  fichier et le traiter
'...
'...

Dans la pratique, cela permet d’éviter pas mal de fois des boucles If … Else … End If. Quel que soit la procédure d’où est déclenchée l’erreur, elle sera affichée à la procédure de plus haut niveau dans un MsgBox et le code suivant l’erreur ne sera pas exécuté.

Deux types d’erreurs peuvent être déclenchées :
  • les erreurs utilisateur
    Elles sont plus destinées à identifier les erreurs de saisie de l’utilisateur comme dans l’exemple ci-dessus. Lors de leur affichage, une simple MsgBox s’affiche en mode vbExclamation et rajoute "Action annulée" au message d’erreur. Concrètement, voir le code de l’exemple n°2 du fichier joint.
  • les erreurs critiques
    Elles sont destinées à afficher des erreurs personnalisées et plus compréhensibles pour l’utilisateur. Lors de leur affichage, une simple MsgBox s’affiche en mode vbCritical avec la source de l’erreur et sa description de l’erreur. Concrètement, voir le code de l’exemple n°3 du fichier joint.
    VB:
    Err.Raise G_L_ERR_CRITICAL, , "Le fichier '" & l_s_filePath & "' n'existe pas."
Le seul point noir de cette nouvelle méthode de gestion des erreurs consiste dans sa mise en œuvre. Il faut en effet :
  • ajouter un module dédié à la gestion des erreurs. Celui-ci contient :
    • un type de données particulier (ERR_Mem) utilisé dans la gestion des erreurs pour stocker les informations de l’erreur
    • une fonction StoreErrInfo utilisée dans la gestion des erreurs
    • une procédure DisplayError utilisée pour afficher les erreurs (dans la procédure principale).
    • deux constantes (G_L_ERR_USER et G_L_ERR_CRITICAL) utilisées pour déclencher les erreurs personnalisées.
    Il s’agit du module de code Mod_Err présent dans le fichier joint.
  • gérer deux modèles de gestion des erreurs :
    • celui utilisé dans les sous-procédures, qui sert à renvoyer l’erreur à la procédure appelante
    • celui utilisé dans les procédures principales (celles déclenchées par des actions de l’utilisateur), qui sert à afficher les erreurs
Au final, avec MZ-Tool bien paramétré, l’utilisation des deux types de gestion d’erreur est assez simple…

Avec plusieurs mois de recul, je trouve cette méthode très pratique et au final, je l’utilise dans tous mes projets, même les plus petits.

A+
 

Pièces jointes

  • AncienneGestion.png
    AncienneGestion.png
    52.4 KB · Affichages: 200 834
  • NouvelleGestion.png
    NouvelleGestion.png
    62.6 KB · Affichages: 11 297
  • Err_User.png
    Err_User.png
    10.5 KB · Affichages: 11 216
  • Err_Critique.png
    Err_Critique.png
    10.7 KB · Affichages: 11 114
  • Gestion des erreurs.xlsm
    24.6 KB · Affichages: 952
Dernière édition:

capsman

XLDnaute Nouveau
Bonjour, merci pour ce tuto et ces explications. Par contre je ne vois pas le fichier joint pour voir les exemples.
 
Dernière édition:

ThomasR

XLDnaute Occasionnel
Hello,

Question :
- Que ce passe t'il si tu rencontres plusieurs erreurs avant de remonter dans la procédure mère ?
- Comment fais tu la distinction entre une erreur "métier" que tu valorises toi même (err.raise) et d'une erreur classique ?

Idée :
- Je pense qu'il serait intéressant de pouvoir catcher le type d'erreur
- créer une classe pour la gestion des erreurs dans une collection afin d'avoir un tableau des erreurs, de sorte à pouvoir utiliser la gestion d'erreur non pas uniquement comme une gestion d'erreur mais également pour la signalétique (la gestion des messages).

Sinon je trouve le principe très intéressant.

Thomas
 

mromain

XLDnaute Barbatruc
Bonjour à tous,

Merci pour vos retours.

@ capsman :
Effectivement, le fichier a disparu, c’est étrange…
J’ai recréé un fichier exemple que tu trouveras ci-joint. A l’intérieur, il y a :
  • l’exemple complet présenté dans le premier post
  • un exemple de déclenchement d’erreur critique
  • un exemple de déclenchement d’erreur utilisateur

@ Thomas :
  • Que ce passe t'il si tu rencontres plusieurs erreurs avant de remonter dans la procédure mère ?
    Avec ce mécanisme de gestion des erreurs, le traitement s’arrête dès la première erreur rencontrée. On ne peut donc pas rencontrer plusieurs erreurs avant de remonter.
  • Comment fais tu la distinction entre une erreur "métier" que tu valorises toi même (err.raise) et d'une erreur classique ?
    Avec ce mécanisme de gestion des erreurs, il y a au final 3 sources possibles d’erreurs :
    • une erreur "métier" critique, déclenchée effectivement par Err.Raise G_L_ERR_CRITICAL (voir le second exemple du fichier ci-joint)
    • une erreur "métier" utilisateur, déclenchée effectivement par Err.Raise G_L_ERR_USER (voir le dernier exemple du fichier ci-joint)
    • une erreur classique, comme par exemple une division par 0 (voir le premier exemple du fichier ci-joint).
Tes idées d’évolutions sont intéressantes. Je m’étais posé ces questions au moment où j’ai mis en place ce mécanisme, notamment utiliser un module de classe et gérer une pile d’erreur.
Au final, je me suis focalisé sur la problématique de faire remonter l’erreur et fermer "proprement" toutes les procédures et ai opté pour un simple module, plus facile à mettre en œuvre…

J’ai depuis fait légèrement évoluer cette gestion des erreurs afin d’ajouter un debug mode permettant de s’arrêter sur la ligne qui déclenche l’erreur, où qu’elle se situe dans le code. Je pourrai bien sûr le transmettre s’il y a des intéressés.

A+
 

Pièces jointes

  • Gestion Erreurs.xlsm
    33.6 KB · Affichages: 203

Guy_M

XLDnaute Occasionnel
Bonjour,

J'avais commencé un créer un module de classe pour gérer les erreurs "métier" et avoir un mode debug pour les erreur "classiques".

C'est beaucoup plus rustique que ce que vous faites (le résultat me satisfaisait), et mon mode debug est foireux (j'ai l'impression d'exécuter 2 fois la procédure dans laquelle l'erreur se produit) et je ne sort pas proprement des procédures (c'était pas mon objectif mais avec l'expérience, cela aurait dû en être un).

Je suis donc intéressé par votre gestion d'erreur avec debug mode car j'ai des progrès à faire dans la gestion des erreurs.

Par avance, merci pour votre réponse.

A bientôt
GM
 

mromain

XLDnaute Barbatruc
Bonjour Guy, le forum,

Tu trouveras ci-joint la version avec debug mode.
Il y a quelques changements par rapport à la version précédente :
  • ajout de la constante M_B_DEBUGMODE
    Lorsque celle-ci est mise à True et qu'une erreur survient, l'exécution du code va s'arrêter dans la procédure contenant l'erreur (au niveau de sa gestion d'erreur).
    Dans ce cas :
    • appuyer deux fois sur [F8] pour revenir sur la ligne ayant déclenché l'erreur
    • une fois l'erreur identifiée, appuyer sur [F5] pour terminer proprement le code
      Il est important de terminer proprement le code pour plusieurs raisons :
      • fermer proprement toutes les procédures appelantes
      • bien afficher l’erreur dans la procédure principale
        Au moment de l’affichage (lors de l’exécution de la procédure Mod_Err.DisplayError), le debug mode est "réinitialisé".
        Si cette "réinitialisation" n’a pas lieu, il se peut que le debug mode ne fonctionne pas malgré que la constante soit à True. Dans ce cas, il faut recompiler proprement le projet VBA (ou fermer/ré-ouvrir le fichier).
  • les deux modèles de gestion des erreurs ont légèrement évolué
  • la manière de déclencher des erreurs a également changé :
    • RaiseError UserError, "Intitulé de l’erreur" pour les erreurs utilisateur
    • RaiseError CriticalError, "Intitulé de l’erreur" pour les erreurs critiques

Comme pour la précédente version, il faut avouer que la mise en œuvre ce cette gestion des erreurs est un peu contraignante (un module dédié, plusieurs modèles de gestion d’erreur, …).
Par contre, c’est un bonheur une fois en place. Le débogage est grandement facilité.

Voili voilou. N’hésitez pas à me demander s’il faut plus d’informations.

Bonne fin de journée et bonnes fêtes de fin d’année !
 

Pièces jointes

  • Gestion Erreurs.xlsm
    33.1 KB · Affichages: 77

Discussions similaires

Statistiques des forums

Discussions
311 713
Messages
2 081 806
Membres
101 819
dernier inscrit
lukumubarth