Error handling strategy

hbrems

has no clue...
Local time
Today, 01:29
Joined
Nov 2, 2006
Messages
181
I'm trying to come up with a strategy for error handling but I guess I can use some tips.

Basically I see using a general module would help me out a lot. I would not have to create in dept error handling in my procedures.

Currently I have a public sub which logs the error in a text file. I could add a msgbox too if necessary.

I guess my question is if anyone can give me more information in regards to a good strategy or could point me to some reference.
 
Early in my VB career (early 90s) I found that simpler is better.

What happens if you have cascading errors and your error handling routines are just adding to the fun and excitement? I'm talking about things beyond your control (like, but not limited to, a down network). So, I do a lot of checking for errors before they get a chance to happen and I tend to use local error handling routines for the ones that "got away".
 
IMO, I would much rather to be able to handle errors that I definitely should expect locally and everything else with a generic handler.

For example, with a routine dealing with writing/reading a text file, I would put in error handling specific to problems such as missing file, bad filename, or unreadable filename or lacking the permission to the file.

Generic error handler should be a *last* resort for something totally unexpected, that you just don't what the #!@# to do with it. So suppose in that file handling routine, I ran out of free memory, the local handling should be able to throw its hand up and throw it to the generic error handler, and you can try to decide for certain cases whether to force application to quit, or reset, or simply do nothing. And by "certain cases", I'm talking about errors that are application-wide, not routine-wide, such as out of memory, full drive, unable to read hard drive- that kind of stuff.

Finally, to guarantee that you get the full information (Note: You can never ask your users. They'll meander and babble but never tell you anything useful), you definitely will want to write it to either local table and text file, and figure out how you are going to get that text file/local table (whether by asking your user to click a button to send an email after the reboot or by you physically going to the user's PC and reading it yourself). I wouldn't not use anything on network as there is no guarantee that network is up (indeed, it may be the *case* of the error!).

HTH.

A quick, off-the-top code snippet. NOTE: This is just my convention- this is not what most people usually write for error handling; I just like the way it flow. You do not have to follow the ordering, but the basic principle is same.

Code:
Private Sub Doh()

On Error Goto Oops

'Declare a bunch of variables
Dim i As Integer
Dim j As Integer
Dim frm As Form

'Do something stupid
i=0
j=1

Set frm= Forms(j/i)

Goto ExitProc
Oops:

Case Select Err.Number
      Case 11
         Resume Next
      Case Else
         Call GenericError
End Select

ExitProc:

'Close whatever objects need closing, and set objects to nothing as necessary
Set frm = Nothing

End Sub
 
Last edited:
So the both of you prefer to have a local error handling system for things you expect and use a generic one for unexpected errors. The last one would log the errors to a place where you can recover the information.

It seems logical but still it creates a lot of work. Take some silly issues which return in each form and you know what I mean.

My situation is that I'm using a database for 1 year now. And I never put in too much error handling because I know it inside out. Now I want to distribute it to more users so I will have to take their slippery fingers into account. Which means editing about 15 forms and lots of procedures. I guess it's my own fault. :rolleyes:

I'm really interested to see more reactions on this!
 
Download MZ-Tools. It's free and can automate inserting the error handling template to your liking.
 
So the both of you prefer to have a local error handling system for things you expect and use a generic one for unexpected errors.

No. I prefer to do error handling (especially unexpected errors) as close to the error as possible. My fear is that going "global" will cause cascading errors (been there, done that). This is a really difficult situation because you never know what's going to happen in the environment you're working in.

In my first "real" VB programming job, my program was going into the field in the desert on a touch screen using a private radio data network with no commercial IP stack available, and a "database" that wasn't designed to work in Windows (3.1). At that time, all of these technologies were bleeding edge. Since there were so many problems (mostly caused by hardware and driver failures), I decided to create a "global" error handler that also handled GPFs (remember those?). Big mistake.

Now I want to distribute it to more users so I will have to take their slippery fingers into account. Which means editing about 15 forms and lots of procedures.

Back in the Windows 3.1 programming days, I was able to use a built in Windows program called the macro recorder to put error handling into all of my VB subroutines/functions. In those days, there were more restrictions on tag names within VB. I created a macro that worked like this: I would double click on the subroutine name, I would invoke the macro with a set of key strokes, the macro would copy the selected text into the buffer, move the cursor down a line, write the "on error goto" line (based on the value in the buffer), create the error routine, put the tags and gotos in place, etc. It took me a couple of hours to do the whole program, with breaks.

You can still download the macro recorder but it doesn't work right in 32 bit windows. I haven't found a satisfactory solution since then, but I'm willing to bet that a bright young person could write a program that could implement error handling in a more automated fashion.
 
No. I prefer to do error handling (especially unexpected errors) as close to the error as possible. My fear is that going "global" will cause cascading errors (been there, done that). This is a really difficult situation because you never know what's going to happen in the environment you're working in.

If you don't mind, can you give a specific example of "cascading examples"?

I wouldn't want a global handler for any errors, but always thought global generic handler made sense for errors that I didn't plan for or anticipate; basically a giant, bright red flashing "ABORT" button for which failure is the only option (e.g. forcing the application to quit, or throw everything out and return to switchboard or something like that).
 
If you don't mind, can you give a specific example of "cascading examples"?

If, inside your generic routine you were writing to a file/network/database and something about that device failed, and you used your generic routine to handle the error, you would get a cascading error handling problem. When I ran into this, I had had enough foresight to realize this could happen, so I used incredibly simple local error handling in my generic error handler. However, the desert had an impact on the machine(s) and some of them would get run-away errors, resulting in page upon page of error dumps to the screen/disk, potentially filling up the disk (dasd was quite small back then), causing even more errors. This was exascerbated by the requirement to maintain total control over the environment, experimental internal private radios, not allowing the program to exit to windows or dos, a DOS based "database" that wasn't approved for Windows, determining when the unit was attached to an external nic card, and disabling the power button. So much crap would break all at once (usually because of the weather) that the failures would go from things that weren't working (usually the radio or screen), to the things that were working (usually the disk drive).
 
Just to be clear, even within that generic handler, if all I did for an error caused by the handler itself was to flush everything away and save nothing, log nothing, stop doing anything, you get the idea, there's still a risk of a cascading error? This is what I don't get. I can see how logging a error could be a risk in itself and cause a cascading error, but if the error handler within that generic handler simply forced the application to quit, it'd be all dandy and fine, right? (Well, except for the fact that vital debugging information has been lost, of course)
 
i use MZTools (as previously mentioned) and a standard error trapping sub that logs everything into a table

Code:
'---------------------------------------------------------------------------------------
' Procedure : ErrorLog
' DateTime  : 11/01/2007 14:22
' Author    : darth vodka
' Purpose   : Writes an error log into the error table, called from all procedures
'---------------------------------------------------------------------------------------
'
Sub ErrorLog(lngErrorNumber As Long, _
                strErrorDesc As String, _
                strModule As String, _
                strProcedure As String)

    Const strErrorTable     As String = "tbl_ErrorLog"
    Dim strSQL              As String
    Dim StrErrText          As String
    Dim strTitle            As String
    
    On Error GoTo ErrorPlace
    
    strTitle = vbNullString
    strTitle = "Error Notification  (" & strProcedure & ")"

    StrErrText = vbNullString
    StrErrText = StrErrText & "The following error occurred..."
    StrErrText = StrErrText & vbCrLf & vbCrLf
    StrErrText = StrErrText & "Error Number:  " & CStr(lngErrorNumber)
    StrErrText = StrErrText & vbCrLf
    StrErrText = StrErrText & "Error Description:  " & strErrorDesc
    StrErrText = StrErrText & vbCrLf & vbCrLf
    StrErrText = StrErrText & "In the procedure: " & strProcedure
    StrErrText = StrErrText & vbCrLf & vbCrLf
    StrErrText = StrErrText & "In the module: " & strModule
    
    'MsgBox StrErrText, vbOKCancel, StrTitle
    
    strSQL = "INSERT INTO " & strErrorTable & " ( nbkid, ErrorNumber, ErrorDescription, ErrorTime, [Module], [Procedure],[Database] ) "
    strSQL = strSQL & " SELECT """ & NetworkID() & """, "
    strSQL = strSQL & lngErrorNumber & ", """
    strSQL = strSQL & strErrorDesc & """, #"
    strSQL = strSQL & Format(Now(), "mm/dd/yyyy hh:mm:ss") & "#, """
    strSQL = strSQL & strModule & """, """
    strSQL = strSQL & strProcedure & """, """
    strSQL = strSQL & CurrentDb.Name & """ ;"
    With DoCmd
        .SetWarnings (False)
        .Hourglass (True)
        .RunSQL strSQL
        .SetWarnings (True)
        .Hourglass (False)
    End With
    
    MsgBox "An error '" & strErrorDesc & "' occurred in some code, things may not have run completely"
    
    Exit Sub
    
ErrorPlace:
    'funnily enough you can *still* get errors here. e.g. you have no permissions to write to the errotable
    'MsgBox "Error in creating ErrorLog: " & Err.Description
    MsgBox "An error occurred in some code, things may not have run completely"
    DoCmd.SetWarnings (True)
    DoCmd.Hourglass (False)
End Sub

called the same sort of way as Banana's

the users still get error message pop ups, but i get to record who, where and why
 
but if the error handler within that generic handler simply forced the application to quit, it'd be all dandy and fine, right? (Well, except for the fact that vital debugging information has been lost, of course)

That is true and what I intended to convey, though not very eloquently.
 
I spent some time adjusting some of my forms today to adapt this way of working. The funny thing is, I'm now eager to see users make mistakes and see the log fill up. :)
 
Hi there,
thanks for the helpful code about error trapping. Is there a way of retrieving automatically the name of the procedure, where the error occurred? This would allow to locate the error quickly in an error message, without having to type in the procedure name in the error trapping function (in the example above 'strProcedure').
Any thoughts?
marcus
 
Well, the simplest way is to use MZ-Tools, a free download, which will insert the error handling for you and fill in the procedure name for you automatically.
 
this was an oldish thread, but the discussion on external disk errors discussed was interesting

i suppose most apps do data validation at point of entry, and prevent any rogue data entering the system - so the capacity for unhandled errors needing a generic handler is far less - surely there can't be too many circumstances where you would expect a generic handler to be needed, or can there?
 
Well, a while ago, I had discussion with other programmers about the usage of try/catch blocks. He pointed out that people "abuse" the try/catch block and use it like a messaging system to notify users when it really should be handling errors and also that exceptions should be allowed to bubble up so for totally unexpected errors or abnormal state, the whole program should halt.

The thing, though, is that Access doesn't give you full control of the whole process; you can control the event that Access hands off to you via VBA, but there's nothing stopping Access from jamming up at say, Network or Disk Error.

But there's some applications. Suppose the application I was writing would be dealing with several Excel files. When I open the files, I would definitely want to run it through a function that is customized toward opening the Excel file properly and handle the error that are usually associated with opening files within the same procedures. Likewise, the function that I use to save Excel file would have its own error handler to specifically handle the expected errors associated with saving a file.

A point in case: You can open a file that's being used and locked, but you can't save a file that's being used and locked by someone else. Ergo, the saving procedure requires that we account for the possibility of the file being locked, whereas we wouldn't be as concerned when we are opening the file, especially if we intend to do a SaveAs method with a different filename or at least get the notification when the file is now write-able and thus we can lock it for write.

But even so, neither procedures doesn't have to make the assumptiont that there is danger of a full disk. That's a different kind of error and unexpected one because it doesn't pertain specifically to opening or saving file but rather the state of hard drive, and can only be fixed by user intervention (e.g. deleting some files). For such errors, they probably are best passed off to a generic error handler because a full disk can raise error at any point, even something as benign as saving a new record to the .mdb could fill up the disk.

So, my view is that specific errors that arise out because of specific actions taken in this procedures should be handled there. If you will be doing the same action several times, this may be best as a function so you only have one error handling code, and it's more maintainable because you just pass off the file as a argument to the function without having to adding error handling and various checks.

But for "aw, I give up. I have no freaking idea what I'm supposed to do with this errors", those should go to generic error handler, and that would be few and far between.

(Side note: Even if we implemented specific error handling, some may find it handy to always call a logging function within every error handling for future debugging, but that's not really a error handling; just logging what happened so you don't have to rely on users' blathering on how they were doing their thing and it all broken down, without any specific explanation of what they actually did and what they actually saw.)
 
i use MZTools (as previously mentioned) and a standard error trapping sub that logs everything into a table

Code:
'---------------------------------------------------------------------------------------
' Procedure : ErrorLog
' DateTime  : 11/01/2007 14:22
' Author    : darth vodka
' Purpose   : Writes an error log into the error table, called from all procedures
'---------------------------------------------------------------------------------------
'
Sub ErrorLog(lngErrorNumber As Long, _
                strErrorDesc As String, _
                strModule As String, _
                strProcedure As String)

    Const strErrorTable     As String = "tbl_ErrorLog"
    Dim strSQL              As String
    Dim StrErrText          As String
    Dim strTitle            As String
    
    On Error GoTo ErrorPlace
    
    strTitle = vbNullString
    strTitle = "Error Notification  (" & strProcedure & ")"

    StrErrText = vbNullString
    StrErrText = StrErrText & "The following error occurred..."
    StrErrText = StrErrText & vbCrLf & vbCrLf
    StrErrText = StrErrText & "Error Number:  " & CStr(lngErrorNumber)
    StrErrText = StrErrText & vbCrLf
    StrErrText = StrErrText & "Error Description:  " & strErrorDesc
    StrErrText = StrErrText & vbCrLf & vbCrLf
    StrErrText = StrErrText & "In the procedure: " & strProcedure
    StrErrText = StrErrText & vbCrLf & vbCrLf
    StrErrText = StrErrText & "In the module: " & strModule
    
    'MsgBox StrErrText, vbOKCancel, StrTitle
    
    strSQL = "INSERT INTO " & strErrorTable & " ( nbkid, ErrorNumber, ErrorDescription, ErrorTime, [Module], [Procedure],[Database] ) "
    strSQL = strSQL & " SELECT """ & NetworkID() & """, "
    strSQL = strSQL & lngErrorNumber & ", """
    strSQL = strSQL & strErrorDesc & """, #"
    strSQL = strSQL & Format(Now(), "mm/dd/yyyy hh:mm:ss") & "#, """
    strSQL = strSQL & strModule & """, """
    strSQL = strSQL & strProcedure & """, """
    strSQL = strSQL & CurrentDb.Name & """ ;"
    With DoCmd
        .SetWarnings (False)
        .Hourglass (True)
        .RunSQL strSQL
        .SetWarnings (True)
        .Hourglass (False)
    End With
    
    MsgBox "An error '" & strErrorDesc & "' occurred in some code, things may not have run completely"
    
    Exit Sub
    
ErrorPlace:
    'funnily enough you can *still* get errors here. e.g. you have no permissions to write to the errotable
    'MsgBox "Error in creating ErrorLog: " & Err.Description
    MsgBox "An error occurred in some code, things may not have run completely"
    DoCmd.SetWarnings (True)
    DoCmd.Hourglass (False)
End Sub

called the same sort of way as Banana's

the users still get error message pop ups, but i get to record who, where and why


Hi Darth Vodka,

I really like the idea of logging errors into a table, and I like how MZ-Tools' error handler describes the error in a message box. I am still unclear as to how to call the above procedure to log it into a table. Do I create a module and paste the above code into it? And where in the error handler to I call it??

Thanks a bunch!
ScrmingWhisprs
 
OK, old thread, but since it is about the subject I am working on...

I get the need to trap and record/report errors and I am working on adding code to my database.

Now, some pieces of the code are more complex than others so my question is....

Does it make sense to add error trapping code to a one-line sub? Something like Do.maximize on form load?

mafhobb
 

Users who are viewing this thread

Back
Top Bottom