Accessing call stack from code

jjjamie

Active member
Joined
Aug 30, 2002
Messages
34
Location
England
Does anyone know how to fetch the name of the previously called subroutine from code?

e.g.

Code:
Sub One
   Two()
End Sub

Sub Two
    Code here should print One when called via Sub One
End Sub
 
You really shouldnt want to do something like this. Use a parameter to give your call context instead.
 
Yes I do want to do something like this and Ive managed to work out how to do it. All I needed to do in the end was use the StackTrace class to retrieve the previous frame and then examine it to get the MethodBase object and in turn the name of the method. Here is the code I used:

Code:
Public Sub Enter(Optional ByVal Message As String = "")
    Dim Caller As StackFrame
    Dim StackTrace As New StackTrace()
    Dim CallerName As String
    Dim Indent As String
    Dim FileName As String

    Caller = StackTrace.GetFrame(1)
    CallerName = Caller.GetMethod.Name  Get name of calling method
    FileName = Caller.GetFileName  ** Method not yet implemented **

    Indent = IndentEnter(Caller.GetMethod)

    If Message <> "" Then
        WriteLine(Indent + "Entering " + CallerName + "() <" + Message + ">")
    Else
        WriteLine(Indent + "Entering " + CallerName + "()")
    End If
End Sub

The reason I wanted to do it is because I am implementing a trace listener to log to a file when certain methods are entered and exited (in order to aid the debugging of my program). I feal this is a good method to use because I only have to disable the TRACE compilation constant to prevent the logging.

Here is the code that I used for those that are interested:

Code:
Imports System.IO
Imports System.Reflection
Imports System.Diagnostics

Public Class MainTraceListener
    Inherits TraceListener

    Private m_IndentStorer As New ArrayList()

    Private Structure MethodIndent
        Dim IndentLevel As Integer
        Dim Method As MethodBase
    End Structure

     Create a log file named PROFILE.LOG
    Dim m_StreamWriter As New StreamWriter("PROFILE.LOG")

    Public Overloads Overrides Sub Write(ByVal Message As String)
         Display indentation and time only at beginning of line
        If NeedIndent Then
            m_StreamWriter.Write("[" + Now.ToString + " " + _
             CStr(Now.Millisecond) + "] ")
            WriteIndent()
        End If
        m_StreamWriter.Write(Message)
        m_StreamWriter.Flush()
    End Sub

    Public Overloads Overrides Sub WriteLine(ByVal Message As String)
         Use the Write method to display an extire line
        Write(Message + ControlChars.CrLf)
         Next line must be indented and current time must be displayed
        NeedIndent = True
    End Sub

    Public Sub Note(ByVal Note As String)
        WriteLine("***** " + Note + " *****")
    End Sub

    Private Function IndentEnter(ByVal Method As MethodBase) As String
        Dim MethodIndent As New MethodIndent()
        Dim Indent As String

        If m_IndentStorer.Count = 0 Then
            MethodIndent.IndentLevel = 0
            MethodIndent.Method = Method
            m_IndentStorer.Add(MethodIndent)
            Exit Function
        Else
            MethodIndent.IndentLevel = _
             CType(m_IndentStorer(m_IndentStorer.Count - 1), _
            MethodIndent).IndentLevel + 1
            MethodIndent.Method = Method
            m_IndentStorer.Add(MethodIndent)
        End If

        Indent = Space(IndentSize * MethodIndent.IndentLevel)

        Return Indent
    End Function

    Private Function IndentLeave(ByVal Method As MethodBase) As String
         Search for method
        Dim SearchMethodIndent As MethodIndent
        Dim Indent As String

        For Each SearchMethodIndent In m_IndentStorer
            If SearchMethodIndent.Method Is Method Then
                 Found method. Do indent
                Indent = Space(IndentSize * SearchMethodIndent.IndentLevel)

                 Remove method indent data from arraylist
                m_IndentStorer.Remove(SearchMethodIndent)

                Return Indent
            End If
        Next
    End Function

    Public Sub Enter(Optional ByVal Message As String = "")
        Dim Caller As StackFrame
        Dim StackTrace As New StackTrace()
        Dim CallerName As String
        Dim Indent As String
        Dim FileName As String

        Caller = StackTrace.GetFrame(1)
        CallerName = Caller.GetMethod.Name
        FileName = Caller.GetFileName  ** Method not yet implemented **

        Indent = IndentEnter(Caller.GetMethod)

        If Message <> "" Then
            WriteLine(Indent + "Entering " + CallerName + "() <" + Message + ">")
        Else
            WriteLine(Indent + "Entering " + CallerName + "()")
        End If
    End Sub

    Public Sub Leave(Optional ByVal Message As String = "")
        Dim Caller As StackFrame
        Dim StackTrace As New StackTrace()
        Dim CallerName As String
        Dim Indent As String

        Caller = StackTrace.GetFrame(1)
        CallerName = Caller.GetMethod.Name

        Indent = IndentLeave(Caller.GetMethod)

        If Message <> "" Then
            WriteLine(Indent + "Exiting " + CallerName + "() <" + Message + ">")
        Else
            WriteLine(Indent + "Exiting " + CallerName + "()")
        End If
    End Sub

End Class

I then instantiated the class in a shared module:

Code:
Module TracerObject

     Trace listener
    Public Tracer As New MainTraceListener()

    Sub New()
        Trace.Listeners.Add(Tracer)
    End Sub

End Module

Then, I used the tracer (in a different class) as follows:

Code:
Public Sub Start()
    Tracer.Enter()  Log method start

     Only start server if it is stopped
    If ServerState.CurrentState <> ObjectState.Stopped Then Exit Sub

     Server is now starting
    ServerState.ClearError()
    ServerState.ChangeState(ObjectState.Starting, True)

    Tracer.Note("Starting listener")

    StartListener()

    Tracer.Leave()  Log method end
End Sub

Here is the resulting output that has been sent to the file PROFILE.LOG (along with the output of a few other methods):

Code:
[07/05/2003 19:55:25 779] Entering .ctor()
[07/05/2003 19:55:25 809] Exiting .ctor()
[07/05/2003 19:55:28 142] Entering Start()
[07/05/2003 19:55:28 152]     Entering doStateChanged()
[07/05/2003 19:55:28 182]     Exiting doStateChanged()
[07/05/2003 19:55:28 192] ***** Starting listener *****
[07/05/2003 19:55:28 192] Exiting Start()
[07/05/2003 19:55:28 373] Entering doStateChanged()
[07/05/2003 19:55:28 393] Exiting doStateChanged()
[07/05/2003 19:55:28 423] Server queue processor started.
[07/05/2003 19:55:46 649] Entering Shutdown()
[07/05/2003 19:55:46 649]     Entering doStateChanged()
[07/05/2003 19:55:46 649]     Exiting doStateChanged()
[07/05/2003 19:55:46 689] Server queue processor exiting.
[07/05/2003 19:55:46 699]     Entering doStateChanged()
[07/05/2003 19:55:46 839]     Exiting doStateChanged()
[07/05/2003 19:55:46 839] Exiting Shutdown()

Methods markey .ctor are constructor methods. I would have like to have included the name of the file that the method was running from but after checking the documentation, Microsoft hasnt implemented the StackFrame.GetFileName method.

I hope that this has explained what I am up to and maybe a few people might find it interesting. The code isnt up to my ideal standard because I needed a quick and dirty solution but if anyone wants me to explain anything just ask.

Jamie
 
Last edited by a moderator:
jjjamie,

Just wondering, I am not experenced but I am picking up as I go.

I am looking to do the same thing, for loging.

Just quickly do u have to call the class/module when ever you want it to run or what?? If so how do I doing it.

Thanks.......Dale
 
Back
Top