Recherche…


Éviter les conditions d'erreur

Lorsqu'une erreur d'exécution se produit, un code correct doit le gérer. La meilleure stratégie de gestion des erreurs consiste à écrire du code qui vérifie les conditions d'erreur et évite simplement d'exécuter du code entraînant une erreur d'exécution.

Un élément clé dans la réduction des erreurs d'exécution est la rédaction de petites procédures qui font une chose . Moins il y a de raisons pour lesquelles les procédures doivent échouer, plus le code dans son ensemble est facile à déboguer.


Éviter l'erreur d'exécution 91 - La variable Object ou With block n'est pas définie:

Cette erreur sera déclenchée lorsqu'un objet est utilisé avant que sa référence soit affectée. On peut avoir une procédure qui reçoit un paramètre d'objet:

Private Sub DoSomething(ByVal target As Worksheet)
    Debug.Print target.Name
End Sub

Si la référence n'est pas affectée à une target , le code ci-dessus générera une erreur facilement évitée en vérifiant si l'objet contient une référence d'objet réelle:

Private Sub DoSomething(ByVal target As Worksheet)
    If target Is Nothing Then Exit Sub
    Debug.Print target.Name
End Sub

Si la target n'est pas attribuée à une référence, la référence non attribuée n'est jamais utilisée et aucune erreur ne se produit.

Cette méthode de sortie anticipée d'une procédure lorsqu'un ou plusieurs paramètres n'est pas valide s'appelle une clause de garde .


Éviter l'erreur d'exécution 9 - Indice en dehors des limites:

Cette erreur est déclenchée lorsqu'un tableau est accessible en dehors de ses limites.

Private Sub DoSomething(ByVal index As Integer)
    Debug.Print ActiveWorkbook.Worksheets(index)
End Sub

Étant donné un index supérieur au nombre de feuilles de calcul dans ActiveWorkbook , le code ci-dessus génère une erreur d'exécution. Une simple clause de garde peut éviter que:

Private Sub DoSomething(ByVal index As Integer)
    If index > ActiveWorkbook.Worksheets.Count Or index <= 0 Then Exit Sub
    Debug.Print ActiveWorkbook.Worksheets(index)
End Sub

La plupart des erreurs d'exécution peuvent être évitées en vérifiant soigneusement les valeurs que nous utilisons avant de les utiliser, et en les ramenant en conséquence à l'aide de simples clauses If statement - in guard qui ne font aucune hypothèse et valident les paramètres d'une procédure. corps de procédures plus grandes.

En cas d'erreur

Même avec les clauses de garde , on ne peut pas toujours rendre compte de manière réaliste de toutes les conditions d'erreur possibles pouvant être soulevées dans le corps d'une procédure. L'instruction On Error GoTo indique à VBA de passer à un libellé de ligne et de passer en "mode de traitement des erreurs" chaque fois qu'une erreur inattendue se produit au moment de l'exécution. Après avoir traité une erreur, le code peut reprendre une exécution "normale" à l'aide du mot-clé Resume .

Les étiquettes de ligne indiquent des sous - routines : comme les sous-programmes proviennent du code BASIC hérité et utilisent des sauts GoTo et GoSub et des instructions Return pour revenir à la routine "principale", il est assez facile d'écrire du code spaghetti difficile à suivre . Pour cette raison, il est préférable que:

  • une procédure a un et un seul sous-programme de gestion des erreurs
  • le sous-programme de gestion des erreurs ne s'exécute jamais que dans un état d'erreur

Cela signifie qu'une procédure qui gère ses erreurs doit être structurée comme suit:

Private Sub DoSomething()
    On Error GoTo CleanFail

    'procedure code here

CleanExit:
    'cleanup code here
    Exit Sub

CleanFail:
    'error-handling code here
    Resume CleanExit
End Sub

Stratégies de gestion des erreurs

Parfois, vous voulez gérer des erreurs différentes avec des actions différentes. Dans ce cas, vous inspecterez l'objet Err global, qui contiendra des informations sur l'erreur qui a été générée - et agissez en conséquence:

CleanExit:
    Exit Sub

CleanFail:
    Select Case Err.Number
        Case 9
            MsgBox "Specified number doesn't exist. Please try again.", vbExclamation
            Resume
        Case 91
            'woah there, this shouldn't be happening.
            Stop 'execution will break here
            Resume 'hit F8 to jump to the line that raised the error
        Case Else
            MsgBox "An unexpected error has occurred:" & vbNewLine & Err.Description, vbCritical
            Resume CleanExit
    End Select
End Sub

En règle générale, envisagez d'activer la gestion des erreurs pour l'ensemble de la sous-routine ou de la fonction, et gérez toutes les erreurs pouvant survenir dans sa portée. Si vous ne devez gérer que les erreurs dans la petite section du code - activez et désactivez la gestion des erreurs au même niveau:

Private Sub DoSomething(CheckValue as Long)

    If CheckValue = 0 Then
        On Error GoTo ErrorHandler   ' turn error handling on
        ' code that may result in error
        On Error GoTo 0              ' turn error handling off - same level
    End If

CleanExit:
    Exit Sub

ErrorHandler:
    ' error handling code here
    ' do not turn off error handling here
    Resume

End Sub

Numéros de ligne

VBA prend en charge les numéros de ligne de style ancien (par exemple QBASIC). La propriété masquée Erl peut être utilisée pour identifier le numéro de ligne qui a généré la dernière erreur. Si vous n'utilisez pas de numéros de ligne, Erl ne retournera jamais que 0.

Sub DoSomething()
10 On Error GoTo 50
20 Debug.Print 42 / 0
30 Exit Sub
40
50 Debug.Print "Error raised on line " & Erl ' returns 20
End Sub

Si vous utilisez les numéros de ligne, mais pas toujours, alors Erl retournera le dernier numéro de la ligne avant que l'instruction qui a soulevé l'erreur.

Sub DoSomething()
10 On Error GoTo 50
   Debug.Print 42 / 0
30 Exit Sub

50 Debug.Print "Error raised on line " & Erl 'returns 10
End Sub

Gardez à l'esprit Erl ne dispose que d'une précision Integer et débordera silencieusement. Cela signifie que les numéros de ligne en dehors de la plage entière donneront des résultats incorrects:

Sub DoSomething()
99997 On Error GoTo 99999
99998 Debug.Print 42 / 0
99999
      Debug.Print Erl   'Prints 34462
End Sub

Le numéro de ligne n'est pas tout à fait aussi pertinent que l'affirmation qui a provoqué l'erreur, et les lignes de numérotation deviennent rapidement fastidieuses et peu conviviales.

Reprendre le mot clé

Un sous-programme de gestion des erreurs sera:

  • exécuter jusqu'à la fin de la procédure, auquel cas l'exécution reprend dans la procédure d'appel.
  • ou, utilisez le mot-clé Resume pour reprendre l' exécution dans la même procédure.

Le mot-clé Resume ne doit être utilisé que dans un sous-programme de gestion des erreurs, car si VBA rencontre Resume sans être dans un état d'erreur, l'erreur d'exécution 20 "Reprendre sans erreur" est générée.

Un sous-programme de gestion des erreurs peut utiliser le mot-clé Resume plusieurs manières:

  • Resume utilisé seul, l'exécution continue sur l'instruction qui a provoqué l'erreur . Si l'erreur n'est pas réellement gérée avant cela, la même erreur sera à nouveau déclenchée et l'exécution pourra entrer dans une boucle infinie.
  • Resume Next continue l'exécution sur l'instruction immédiatement après l'instruction qui a provoqué l'erreur. Si l'erreur n'est pas réellement gérée avant cela, l'exécution peut continuer avec des données potentiellement invalides, ce qui peut entraîner des erreurs logiques et un comportement inattendu.
  • Resume [line label] continue l'exécution à l'étiquette de ligne spécifiée (ou au numéro de ligne, si vous utilisez des numéros de ligne hérités du style). Cela permet généralement d'exécuter du code de nettoyage avant de quitter la procédure proprement, par exemple en veillant à ce qu'une connexion à la base de données soit fermée avant de retourner à l'appelant.

On Error Resume Next

L'instruction On Error elle-même peut utiliser le mot clé Resume pour indiquer à l'environnement d'exécution VBA d' ignorer efficacement toutes les erreurs .

Si l'erreur n'est pas réellement gérée avant cela, l'exécution peut continuer avec des données potentiellement invalides, ce qui peut entraîner des erreurs logiques et un comportement inattendu .

L'emphase ci-dessus ne peut pas être assez soulignée. On Error Resume Next ignore effectivement toutes les erreurs et les place sous le tapis . Un programme qui explose avec une erreur d'exécution à cause d'une entrée invalide est un meilleur programme que celui qui continue à fonctionner avec des données inconnues / involontaires - que ce soit uniquement parce que le bogue est beaucoup plus facilement identifiable. On Error Resume Next peut facilement masquer les bogues .

Le On Error déclaration est scope procédure - c'est pourquoi il devrait normalement y avoir qu'un seul, unique comme On Error instruction dans une procédure donnée.

Cependant, parfois, une condition d'erreur ne peut pas être complètement évitée, et passer à un sous-programme de gestion des erreurs uniquement pour Resume Next ne semble pas correct. Dans ce cas spécifique, l'instruction known-to-event-fail peut être encapsulée entre deux instructions On Error :

On Error Resume Next
[possibly-failing statement]
Err.Clear 'resets current error
On Error GoTo 0

L'instruction On Error GoTo 0 réinitialise la gestion des erreurs dans la procédure en cours, de sorte que toute instruction supplémentaire provoquant une erreur d'exécution ne serait pas gérée dans cette procédure et passait la pile d'appels jusqu'à ce qu'elle soit interceptée par un gestionnaire d'erreur actif. S'il n'y a pas de gestionnaire d'erreurs actif dans la pile d'appels, il sera traité comme une exception non gérée.

Public Sub Caller()
    On Error GoTo Handler
    
    Callee
    
    Exit Sub
Handler:
    Debug.Print "Error " & Err.Number & " in Caller."
End Sub

Public Sub Callee()
    On Error GoTo Handler
    
    Err.Raise 1     'This will be handled by the Callee handler.
    On Error GoTo 0 'After this statement, errors are passed up the stack.
    Err.Raise 2     'This will be handled by the Caller handler.    
    
    Exit Sub
Handler:
    Debug.Print "Error " & Err.Number & " in Callee."
    Resume Next
End Sub

Erreurs personnalisées

Souvent, lors de l'écriture d'une classe spécialisée, vous souhaiterez qu'il génère des erreurs spécifiques et que vous souhaitiez un moyen propre pour l'utilisateur / le code d'appel de gérer ces erreurs personnalisées. Pour ce faire, vous devez définir un type Enum dédié:

Option Explicit
Public Enum FoobarError
    Err_FooWasNotBarred = vbObjectError + 1024
    Err_BarNotInitialized
    Err_SomethingElseHappened
End Enum

L'utilisation de la vbObjectError intégrée vbObjectError garantit que les codes d'erreur personnalisés ne chevauchent pas les codes d'erreur réservés / existants. Seule la première valeur d'énumération doit être explicitement spécifiée, car la valeur sous-jacente de chaque membre Enum est supérieure de 1 au membre précédent. La valeur sous-jacente de Err_BarNotInitialized est donc implicitement vbObjectError + 1025 .

Augmenter vos propres erreurs d'exécution

Une erreur d'exécution peut être Err.Raise l'aide de l'instruction Err.Raise , afin que l'erreur Err_FooWasNotBarred personnalisée puisse être Err_FooWasNotBarred comme suit:

Err.Raise Err_FooWasNotBarred

La méthode Err.Raise peut également prendre les paramètres Description et Source personnalisés. Pour cette raison, il est Err.Raise définir également des constantes pour contenir la description de chaque erreur personnalisée:

Private Const Msg_FooWasNotBarred As String = "The foo was not barred."
Private Const Msg_BarNotInitialized As String = "The bar was not initialized."

Et ensuite, créez une méthode privée dédiée pour générer chaque erreur:

Private Sub OnFooWasNotBarredError(ByVal source As String)
    Err.Raise Err_FooWasNotBarred, source, Msg_FooWasNotBarred
End Sub

Private Sub OnBarNotInitializedError(ByVal source As String)
    Err.Raise Err_BarNotInitialized, source, Msg_BarNotInitialized
End Sub

L'implémentation de la classe peut alors simplement appeler ces procédures spécialisées pour générer l'erreur:

Public Sub DoSomething()
    'raises the custom 'BarNotInitialized' error with "DoSomething" as the source:
    If Me.Bar Is Nothing Then OnBarNotInitializedError "DoSomething"
    '...
End Sub

Le code client peut alors gérer Err_BarNotInitialized comme toute autre erreur, dans son propre sous-programme de gestion des erreurs.


Remarque: le mot clé Error hérité peut également être utilisé à la place de Err.Raise , mais il est obsolète / obsolète.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow