22
votes

I know. A similar question has already asked.

but I haven't got the exact solution from that.

I have a button click event in which I have a method FillCombo().

Button Click event

private void button1_Click(object sender, EventArgs e)
{
      try
      {
          cmbTemplates.Items.Clear();
          lstFiles.Clear();
          FillCombo();                
      }
      catch (Exception ex)
      {
           MethodBase site = ex.TargetSite;
           Log(ex.ToString(), site == null ? null : site.Name);                
      }
}

When I debugged, I found that the exception occurs from FillCombo() method. After that I get the value of site.Name as WinIOError instead of FillCombo.

I tried another method GetExecutingMethodName() which was answered by Chris Gessler in How to get the name of the method that caused the exception question. So I tried sending the name of method that caused exception using GetExecutingMethodName() method

Log(ex.ToString(), GetExecutingMethodName());

But I got the result as System.Windows.Forms.Control.OnClick instead of FillCombo.

How do I get the actual name of method that caused exception?

3
use breakpoints to debug your code and look at stacktracesaurabh64
I can know in Dubug mode. But I need to log the errors to a file. In production environment who is going to sit and Debug!!! @saurabh64Sarath KS
You should log the entire stack trace. This will usually give enough context to determine the issue.Richard Schneider
I get the value of site.Name as WinIOError instead of FillCombo. That's likely, because your FillCombo does not throw exception, but deeper into that when we call into the framework code which throws the exception.Khanh TO

3 Answers

24
votes

.net supports getting the stack trace information from an exception. You could filter out the method (and its name) by examining the first frame (origin).

new StackTrace(ex).GetFrame(0).GetMethod().Name

This would probably give you exactly the same as the targetsite (the win io), but you can examine the stacktrace for the first user code, or the first frame in your type, or whichever your needs.

For example, getting the name of the culprit thrower in your current assembly:

var s = new StackTrace(ex);
var thisasm = Assembly.GetExecutingAssembly();                
var methodname = s.GetFrames().Select(f => f.GetMethod()).First(m => m.Module.Assembly == thisasm).Name;
11
votes

It is important to understand what is meant by "the method that threw the exception". When an exception occurs, there is a specific method that is actually executing. Just because at some point before the exception, you called your own FillCombo() method, that doesn't mean that's the method that threw the exception.

The FillCombo() method will however (in the case you care about here) be in the stack trace. That's why it's useful to log the entire stack trace. Indeed, I just generally log the whole Exception object (i.e. ex.ToString(), or just pass the exception object to string.Format() or similar which will call ToString() for you). This will include the exception type, the message, the entire stack trace, and even inner exception information if present.


The code you got from the other question, for the GetExecutingMethodName() method, is not really all that useful IMHO. You'll note that what it really does is crawl through the stack trace of the current executing location, looking for the first method that is declared in a type other than the one where the GetExecutingMethodName() was declared.

This is wrong for your purpose for two reasons:

  1. It appears you've declared that method in the same class where your Click event handler is declared. This means the event handler method is ignored, and so you get the caller of that method, which is the Control.OnClick() method (i.e. the method that actually raises the event).

Frankly, I find that particular answer odd, because .NET already provides an API for retrieving the MethodInfo of the currently executing method: MethodBase.GetCurrentMethod. And this is way more reliable than the code Chris Gessler wrote.

  1. More problematic, you don't have an opportunity to call this method at the point where the exception is thrown! At best (i.e. even if you deal with the question of where the helper method is declared), all that calling that will tell you is that you are in your button1_Click() method. But you already know that, because the code you're writing to handle the exception is in that method.


If you want to know the name of the method in your currently executing method that was called before the exception occurred, you can combine the two techniques: get the name of the currently executing method, and then pass that to a method that takes both that and the stack trace string from the Exception object, and let that method parse the stack trace string to find the frame just before the currently executing method in the trace.

It's a bit of a pain, but it could be done. Here's an example of what that would look like (simple proof-of-concept console program):

static void Main(string[] args)
{
    try
    {
        CallForException();
    }
    catch (Exception e)
    {
        Console.WriteLine("Exception occurred calling {0} method", GetCallForExceptionThisMethod(MethodBase.GetCurrentMethod(), e));
    }
}

private static string GetCallForExceptionThisMethod(MethodBase methodBase, Exception e)
{
    StackTrace trace = new StackTrace(e);
    StackFrame previousFrame = null;

    foreach (StackFrame frame in trace.GetFrames())
    {
        if (frame.GetMethod() == methodBase)
        {
            break;
        }

        previousFrame = frame;
    }

    return previousFrame != null ? previousFrame.GetMethod().Name : null;
}

private static void CallForException()
{
    DoActualException();
}

private static void DoActualException()
{
    throw new NotImplementedException();
}

Finally, keep in mind that due to method inlining and other optimizations, even a full stack trace may have some irregularities in it, including not even having the actual name of the method where the exception was thrown. That's another reason that logging the entire Exception object is usually much more useful; the more context, the more likely you are to be able to reconstruct what happened.

4
votes

Just try this:

var methodFullName = exception.TargetSite.ReflectedType.FullName