0

I'm trying to get the very first method name that initialed multiple method calls.

for example I have the below,

    static void Main(string[] args)
    {
        Test1();
    }

    static void Test1()
    {
        Test2();
    }
    static void Test2()
    {
        Console.WriteLine("Soemthing");
    }

In the Test2 method, I need to know the Main Method has initiated this chain. I tried using the StackTrace and Reflection as below.

        StackTrace stackTrace = new StackTrace();
        MethodBase methodBase = stackTrace.GetFrame(1).GetMethod();
        Console.WriteLine(methodBase.Name); 

but the stackTrace.getFrame(1) is not what I expect. It will give me the previous method which is Test1.

I do not want to hard code the getFrame value to 2 to get the Main method.

Is there any way to get the parent method from any of the sub methods without hard coding the Frame value?

Kasun Koswattha
  • 2,302
  • 2
  • 12
  • 20
  • If you want to do it manually you can Right click on the method then click Go To Deifnition, or Peek Definition, and keep going though like that until you find the parent that you want. But automatically im not sure – Brendon May 20 '16 at 13:20
  • 4
    What is your criteria for "the initial method"? In a single threaded console application it's always `Main`, no need to determine it...in a web service, you may crawl up through the IIS assemblies... – René Vogt May 20 '16 at 13:20
  • I did this just for the research. But ultimately we are gonna use this with a MVC 6 WebAPi.@RenéVogt – Kasun Koswattha May 20 '16 at 13:23
  • You should try posting the actual problem you are trying to solve, I doubt this is the best approach. – Jonesopolis May 20 '16 at 13:28
  • There is no *difference* between the method one step up on the callstack and two steps up but you seem to think there is one. *Why* do you not want `Test2`? What makes this the wrong answer? – Lasse V. Karlsen May 20 '16 at 13:29
  • this is kind of a POC. Before doing the actual thing, I wanted to see whether its possible of not @Jonesopolis – Kasun Koswattha May 20 '16 at 13:29
  • If the `Test2`-method needs to know what was done before it was called, you should add a new parameter to it, like `source` or `status` etc. I'd avoid reflection until there's no other choice. – W0lfw00ds May 20 '16 at 13:30
  • @LasseV.Karlsen This is a sample. I just want to know who initialed the chain. I can get the main method by doing getframe(2). But I should know its two levels up. What if I dont know? – Kasun Koswattha May 20 '16 at 13:34
  • Why the down vote ? Is this a bad question? If you dont want to help, just ignore. Dont downvote. its bad and rude. No one knows programming to the fullest. – Kasun Koswattha May 20 '16 at 13:41
  • It is entirely normal, inlining is a standard jitter optimization. Nothing you should fix but if you have to then put the [MethodImpl(MethodImplOptions.NoInlining)] attribute on Test1(). – Hans Passant May 20 '16 at 20:35

4 Answers4

3

This will do what you want:

var stackTrace = new StackTrace();
string lastCSharpMethodName = null;
for (int i = 0;; i++)
{
    if (stackTrace.GetFrame(i).GetILOffset() == StackFrame.OFFSET_UNKNOWN)
        break;

    lastCSharpMethodName = stackTrace.GetFrame(i).GetMethod().Name;
}

Console.WriteLine(lastCSharpMethodName);

If you look closely at the debugger in VS, you will see that the first method called for a console app (as an example) is the native mscorlib.dll!System.Threading.ThreadHelper.ThreadStart() which is flagged as Unknown in the Language section:

enter image description here .

What the code above does is to filter out the unknown ones and return the first known line (See: https://msdn.microsoft.com/en-us/library/system.diagnostics.stackframe.getiloffset(v=vs.110).aspx).

which is your case is Main()

kha
  • 19,123
  • 9
  • 34
  • 67
1

You can use this method to determine the name of the method on top (or better bottom) of your call stack:

private static string GetInitialCaller()
{
    StackTrace trace = new StackTrace();
    StackFrame frame = trace.GetFrames()?.LastOrDefault();
    return frame?.GetMethod()?.Name;
}

Tested it in a console app like that

private static void TestIt()
{
    string caller = GetInitialCaller();
    Console.WriteLine($"Initial caller: {caller}");
}
private static void Main()
{
    TestIt();
}

Output:

Initial caller: Main
Manfred Radlwimmer
  • 13,257
  • 13
  • 53
  • 62
René Vogt
  • 43,056
  • 14
  • 77
  • 99
  • It's the main caller, if you up of one level, you should find the main. But even if you try my method, you will have some issues. Like I said in my answer, the stack can be cut depending on where/when you call it. – romain-aga May 20 '16 at 13:29
  • By the way @René Vogt, nice use of 4.6 features :) – romain-aga May 20 '16 at 13:30
  • @KasunKoswattha thatswhy I asked below your question. In my debug version it gives `Main`, but it really depends on optimization and other (jit) compiler stuff how your call stack really looks. I once did something like this for a logger to write the name of the logging method into the log. You need some criteria of where on the stack to stop. Your `Main` (or the initial method of your thread) was obviously called by `ThreadStart`, so the code works as intended. – René Vogt May 20 '16 at 13:34
  • @RenéVogt I did not say your answer is wrong. What I wanted to imply was its not what I looked for. Maybe my comment was not clear. Anyway, I just need the very first method. I'm trying to find a way. – Kasun Koswattha May 20 '16 at 13:37
  • @KasunKoswattha I understood that, your welcome, don't worry :) – René Vogt May 20 '16 at 13:38
0

I guess you are not looking for the first frame on your stack, but the last one. With the FrameCount property, you could write something like this:

StackTrace stackTrace = new StackTrace();
MethodBase methodBase = stackTrace.GetFrame(stackTrace .FrameCount - 1).GetMethod(); // Not sure about the "- 1"
Console.WriteLine(methodBase.Name); 

However, if i remember well, there is something with the stack call that cut the stack for the next calls. (You shouldn't be able to see the previous method calls when you do a second call on the stack.)

But I'm not sure about that.

romain-aga
  • 1,441
  • 9
  • 14
  • You can use "- 2" instead. But it will work only for this case. Maybe you will have to browse all the stack until you reach the ThreadStart or the end, and save the previous item to find what you are looking for. – romain-aga May 20 '16 at 13:33
0

Using @kha 's answer, this should do the trick with out a loop.

            List<StackFrame> list = new StackTrace().GetFrames().ToList();
            int index = list.FindIndex(p => p.GetILOffset() == StackFrame.OFFSET_UNKNOWN);
            var methodName = list.Select(p => p.GetMethod().Name).ElementAt(index-1);
Kasun Koswattha
  • 2,302
  • 2
  • 12
  • 20