QuickStart: Reading the call stack

 Sept 2010: v2 of VBA Global Error Handler is now available vbWatchdog (no DLLs!)

What is a call stack?

They say a picture speaks a thousand words, so let's take a look at the VB call stack window:

VBE CallStack picture

This window shows you the procedure that we’re in (the top item), and the previous function calls that occurred to get there.

In other words, a call stack provides you with the path that was taken to get to where you are now.

Sadly, it is not normally possible to access the call stack in VBA (nor VB6). However, with our Global Error Handler, this is now a possibility...

How to read the call stack at runtime programmatically

ErrEx.CallStack gives you programmatic access to the VBA (or VB6) call stack details at runtime from within your global error handler.

The object returned from ErrEx.CallStack exposes two methods and six properties that relate directly to the call stack:

.FirstLevel (method): puts you at the top of the stack i.e. the procedure that caused the error
.NextLevel (method): moves you to the next stack level, returns False if at the end of stack
.ProcedureName (read-only property): returns the current stack item procedure name, as a string
.ModuleName (read-only property): returns the current stack item module name, as a string
.ProjectName (read-only property): returns the current stack item project name, as a string
.LineNumber (read-only property): returns the current stack item source code line number - see QuickStart: Automatic line numbering
.LineCode (read-only property): returns the current stack item source code line - see QuickStart: Automatic line numbering
.HasActiveErrorHandler (v1.3+) (read-only property): indicates whether the procedure has an active error handler (On Error Goto X, or On Error Resume Next) or not. This is useful for logging propagating errors - see QuickStart: Error Propagation

Furthermore, the ErrEx.CallStack class also exposes the VariablesInspector method which returns a class object for enumerating through the local variables that have been declared in the procedure at the current stack level.

Example of using ErrEx.CallStack

Let's look at a way of logging the call stack to a log file (as seen earlier in QuickStart: Enabling and disabling):

Public Sub LogErrorToFile()
    Dim FileNum As Long
    Dim LogLine As String
    On Error Resume Next ' If this procedure fails, something fairly major has gone wrong.
    FileNum = FreeFile
    Open "C:\ErrorLog.txt" For Append Access Write Lock Write As FileNum

        Print #FileNum, Now() & " - " & CStr(ErrEx.Number) & " - " & CStr(ErrEx.Description)
        'We will separate the call stack onto separate lines in the log
	With ErrEx.CallStack
                Print #FileNum, "       --> " & .ProjectName & "." & _
                                .ModuleName & "." & _
                                .ProcedureName & ", " & _
                                "#" & .LineNumber & ", " & _
                                .LineCode & vbCrLf
            Loop While .NextLevel
	End With

    Close FileNum

End Sub

The above Do-loop is iterating through the call stack, logging to a file - outputting like this:

27/08/2008 12:30:00 - 11 - Division by zero
       --> MyVBAProject.Module1.FnC, #1, Debug.Print 1 / 0
       --> MyVBAProject.Module1.FnB, #2, Call FnC
       --> MyVBAProject.Module1.FnA, #2, Call FnB
Take a look at the VariablesInspector guide (VBA only) where we enhance this logging further to include a dump of all the variables at each procedure stack level.