Algmene beschouwingen over foutbehaandeling.

Na jaren lange ervaring met het schrijven van VBA code in Access ontwikkelt men zich een stijl die men consequent volgt. Ik denk aan het toepassen van Benamingsconventies, het voldoende documenteren door commentaar in de code te voorzien, het intensief testen van de toepassing, en andere technieken, zie ook Optimaliseren VBA-code en nuttige tips.
Met deze strategie vermijdt men ongewenste fouten. Maar ondanks alle inspanninge om foutvrije code te schrijven dient men er rekening mee te houden dat onverwacht er zich een fout bij de gebruikers kan voordoen en deze met een onbegrijpelijke boodschap geconfronteerd worden. Met alle vervelende implicaties vandien.
Daarom is foutbehandeling in de code een absolute vereiste.

Men kan 3 types van mogelijke fouten onderscheiden:

  1. Syntax fouten, komen voor bij het schrijven van de code. In het algemeen wordt men erop gewezen bij compileren van de code (in de VBA-editor via met Menu Debug -> Compile.
    Voorbeelden een If constructie zonder Then en/of End If, verkeerde schrijfwijze van een variabele. Het laaste kan vermeden worden door het Option Explicit statement te gebruiken. Syntax fouten worden (moeten) dus rechtgezet bij het schrijven van de code.
  2. Onverwachte fouten die zich voordoen bij het gebruik van de toepassing, bijvoorbeeld een deling door nul, een variabele van het type integer die een waarde groter als 32.767 toegewezen wordt.Deze fouten moet men via een degelijke foutbehandeling verwerken en de gebruiker een duidelijke boodschap bezorgen.
  3. Fouten die men wel kan verwachten, bijvoorbeeld wil men een tekstbestand inlezen maar om één of andere reden is het bestand verwijderd.
    Bij het schrijven van de code moet men met die mogelijkhedi rekening houden.

Een belangrijk verklaring of statement bij fout behandeling is :
On Error GoTo, waarna men verwijst naar een label in de code, bijvoorbeeld FoutBehandeling :
On Error GoTo Foutbehandeling
In de code voorziet men na het Label Foutbehandeling hoe men met de fout zal omgaan, hier speelt het Err object een belangrijke rol:
FoutBehandeling :
MsgBox "Er heeft zich een fout voorgedaan" _
& vbCrLf & Err.Number & " " & Err.Description, _
vbCritical + vbOKOnly, "Fout in Toepassing"
Hier krijgt men een bericht met het nummer en de omschrijving van de fout.

Een eenvoudige procedure voor foutbehandeling :

Sub sTest

On Erro GoTo FoutBehandeling


Verlaten:

Exit Sub (of Function)


FoutBehandeling :
	 MsgBox "Er heeft zich een fout voorgedaan" _
        & vbCrLf & Err.Number & " " & Err.Description, _
        vbCritical + vbOKOnly, "Fout in Toepassing"

Resume Verlaten (waar Resume wil zeggen herneem..en verwijzing naar een label, hier Verlaten.)
End Sub

Voor gegevens over VBA fouten zie tabel.

Natuurlijk moet men er naar streven om zoveel als mogelijk fouten te voorkomen. Fouten opsporen 'Debugging' tijdens de ontwikkelings fase is van cruciaal belang. De VBA-editor, de zogenaamde IDE (integrated devlopment environment) voorziet de daartoe nodige instrumenten.

Bij gebrek aan foutbehandeling zal bij het uitvoeren van de code getoond worden waar de fout zich voordoet. Dit mag natuurlijk niet bij een applicatie die in productie is, maar maar het is wel handing in de ontwikkelings fase. Daarom voorzie ik in mijn toepassingen een globale constante :
Public Const gInProducie As Boolean = False (in ontwikkelingsfase) True (in productie)
In de foutbehandeling dan:
If gInProducie Then On Error GoTo FoutBehandeling
Wat toelaat de foutbehandeling in de ontwikkelingsfase uit te schakelen en zo te ontdekken waar de fout zich voordoet en hoe ze te vermijden.

On Error Resume next, komt erop neer dat men de instructie geeft de fout te negeren en de code verder uit te voeren, is ook soms handig maar moet toch met de nodige omzichtigheid gebruikt worden.
Bijvoorbeeld men wil alvorens een tekstbestand te creëren en er naar te schrijven eerst het oude verwijderen, maar omwille van één of andere reden is het al verwijderd. Met On Error Resume next zal de code dus verder lopen ondanks de fout op de lijn waarmee het tekstbestand wil verwijderen.

Om daarna de foutbehandeling weer in te schakelen met
Err.clear
On Error GoTo FoutBehandeling
Of ook met
On Error GoTo 0

Technieken te gebruiken tijdens de ontwikkelings fase.

  • Plaatsen van breekpunten 'Breakpoints', de code stopt de uitvoering op het breekpunt, daarna kan men met de F8-toets de code lijn per lijn verder uitvoeren
  • Gebruikmaken van Debug.print naamVariable, tijdens de uitvoeringziet men de waarde van de variabele in de Immediate Window.
  • Met de Locals Window krijgt men een overzicht van alle variabelen van de procedure als ook van de variabelen gedeclareerd in de declaratiesectie.
  • Met Watch Windows kan men één variabele volgen doorheen verschillende procedures. Men selecteert de variabele die men wil volgen en via Debug menu kiest men Add Watch.

Voorbeeld van foutbehandeling die ik toepas.

Doorheen de jaren heb ik een eigen foutbehandeling, geïnspireerd op verschillende bronnen, waarbij de fouten weggeschreven worden naar een tekstbestand. Gebruikers kunnen mij dat doorsturen. Zodoende weet ik waar en wanneer de fout zich heeft voorgedaan en welke oplossing er moet aan gegeven worden.
In de toepassing voorzie ik een tabel tblFoutLog :

Tijdstip Date/Time
FoutNummer Long
FoutOmschr Tekst
ModuleNaam Tekst
ProcedureNaam Tekst
AlineaNummer Integer
Gebruiker Tekst
PCNaam Tekst
VersieToepassing Tekst
Notitie Tekst

Een module basGlobalErr met één constante :

Option Compare Database
Option Explicit
' wanneer de toepassing volledig afgewerkt en voor zover gekend fout vrij is
'Public Const gInProducie As Boolean = True
' Wanneer men de toepassing aan het bouwen is zodanig dat men in de IDE kan zien waar de fout zich voordoet
Public Const gInProducie As Boolean = False

Een module basFoutlog met daarin:

  • een globale constante gstrVersieToep met naam en Versie van de toepassing
  • een publieke routine Sub Log_Fout die de vereiste gegevens naar de tabel tblFoutLog stuurt
  • een publieke functie fFoutVerwerking, die gebruik maakt van de routine Log_Fout, en waar naar verwezen wordt in de template of sjabloon van mijn Foutbehandeling

Hier de code van de module:

Option Compare Database
Option Explicit
Const gstrVersieToep As String = "Versie 13/01/2012"
---------------------------------------------------------------------------------------------------
Public Sub Log_Fout( _
    Optional ByVal lngFoutNummer As Long = -1, _
    Optional ByVal strOmschrijving As String = "Onbekend", _
    Optional ByVal strModuleNaam As String = "Onbekend", _
    Optional ByVal strProcedureNaam As String = "Onbekend", _
    Optional ByVal lngAlineaNummer As Long = -1, _
    Optional ByVal strNotitie As String = "Niet voorzien", _
    Optional ByVal blnInfoGebruiker As Boolean = True)


  Dim strSQL As String
  Dim db As DAO.Database

On Error GoTo gFoutBehandeling

  If blnInfoGebruiker = True Then
    'Toon frm_av_fout om gebruiker te informeren
    DoCmd.OpenForm FormName:="frm_av_Fout"
    Call Forms("frm_av_Fout").FoutBericht( _
      strLabelTekst:="Er heeft zich een fout voorgedaan!", _
      strBoodschap:=strModuleNaam & " : " _
      & strProcedureNaam & " : " & lngFoutNummer _
      & vbCrLf & strOmschrijving)
  End If  'If blnInfoGebruiker ...

  strSQL = "INSERT INTO tblFoutLog " _
      & "(" _
      & "[Tijdstip], [FoutNummer], " _
      & "[FoutOmschr], [ModuleNaam], " _
      & "[ProcedureNaam], [AlineaNummer], " _
      & "[Gebruiker], [PCNaam], " _
      & "[VersieToepassing], [Notitie]" _
      & ") VALUES (" _
      & "'" & CStr(Now()) & "', " _
      & "'" & lngFoutNummer & "', " _
      & "'" & strOmschrijving & "', " _
      & "'" & strModuleNaam & "', " _
      & "'" & strProcedureNaam & "', " _
      & "'" & lngAlineaNummer & "', " _
      & "'" & Environ$("UserName") & "', " _
      & "'" & Environ$("ComputerName") & "', " _
      & "'" & gstrVersieToep & "', " _
      & "'" & strNotitie & "')"

  Set db = CurrentDb

  Call db.Execute(strSQL, dbFailOnError)

Verlaten:

  Exit Sub

gFoutBehandeling:

  Call MsgBox(Err.Number & ":" & Err.Description, vbCritical, "Fout in log-toepassing!")
  Resume Verlaten

End Sub
---------------------------------------------------------------------------------------------------
Public Function fFoutVerwerking(strProc As String, Optional varModuleNaam As Variant _
, Optional varNotitie As Variant) As Boolean
    Dim strBoodschap As String
    Dim strTitel As String
    Dim intStijl As Integer


'Indien er geen fout optreedt verwerk ze, anders geef True terug
If Err.Number = 0 Then
    fFoutVerwerking = True
Else
'Maak titel en boodschap
    strTitel = "Fout " & Err.Number & " in " & Nz(strProc, "niet opgegeven routine")
    strBoodschap = " Volgende fout gebeurde:" & vbCrLf & vbCrLf & Err.Description
    fFoutVerwerking = (MsgBox(strBoodschap, vbExclamation, strTitel) = vbOK)
    Call Log_Fout(Err.Number _
    , Err.Description _
    , strModuleNaam:=Nz(varModuleNaam, "module naam niet opgegeven") _
    , strProcedureNaam:=strProc _
    , lngAlineaNummer:=-1 _
    , strNotitie:=Nz(varNotitie, "") _
    , blnInfoGebruiker:=True)

End If
End Function

Tenslotte gebruik ik twee sjablonen één voor Sub en één voor Function om in iedere procedure te voorzien en die gebruikmaken van de functie fFoutVerwerking

'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
'Project :
'Omschrijving:
'Auteur:
'Datum creatie:
'Datum Herzien:
'Kommentaar:
'
'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

If gInProducie Then On Error GoTo FoutBehandeling



Verlaten:
On Error Resume Next

Exit Sub

FoutBehandeling:
'argumenten procedure, module, notitie
If Not fFoutVerwerking("","","") Then
   Stop
End If

Resume Verlaten
---------------------------------------------------------------------------------------------------
'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
'Project :
'Omschrijving:
'Auteur:
'Datum creatie:
'Datum Herzien:
'Kommentaar:
'
'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

If gInProducie Then On Error GoTo FoutBehandeling



Verlaten:
On Error Resume Next

Exit Function

FoutBehandeling:
'argumenten procedure, module, notitie
If Not fFoutVerwerking("","","") Then
   Stop
End If


  
Begin