views:

208

answers:

5

This one is sort of esoteric. I ran into a NullReferenceException while trying to open a form (in the winforms designer) in a winforms project in visual studio 2008. The stack trace points to the fourth line of the following code:

public static class Logger
{
    public static void LogMethodEnter()
    {
        var frame = new StackFrame(1);
        var method = frame.GetMethod();
        Trace.TraceInformation("{0}.{1}.{2}()", method.DeclaringType.Namespace, method.DeclaringType.Name, method.Name);
        Trace.Indent();
    }

    public static void LogMethodExit()
    {
        Trace.Unindent();
    }
}

...meaning the line with the opening curly brace. I've run into the same issue (but not involving the winforms designer) on other projects, and I think it was a threading related issue, but I don't have the code to replicate it.

Why does this happen and why does the exception stack trace point to the line with the curly brace?

Clarification: The null reference exception only happens in the winforms designer. When the application is run, it doesn't throw that error.

A: 

Pointing to a curly bracket/seemingly incorrect line of code can sometime happen. I think it's simply that the exception occurred on the previous line of code, and Visual Studio for some reason highlights the next line.

At a guess, the program may not break exactly on the line where the exception occurred due to any number of internal and external factors.

Sorry I can't explain this very well.

cofiem
Visual Studio Highlight the next line when the execution is past the previous line but has not yet reached the next line. You will mostly see this when Visual Studio highlight the line in green; meaning that the error occurred inside the method called by the previous line.
Pierre-Alain Vigeant
+1  A: 

I think the problem is related to the static method being called before the static object is constructed. I fixed the issue in the winforms project by adding a static constructor.

If I remember correctly, the static constructor locks the entire object while executing.

Zachary Yates
Does your method access any static fields of the class?
0xA3
No, the only things the method uses are a StackFrame and the Trace object, neither of which are static fields of the class.
Zachary Yates
And anything else in your class? I'm just curious about why the static constructor solved the problem. It seems possible because adding a static constructor changes the behavior how a static class is initialized.
0xA3
I'm thinking this may be a quirk related to creating a StackFrame from a static method that has implicitly invoked the static constructor from accessing the method. When you created an explicit static constructor, this may have resulted in the class being initialized sooner. Another experiment you may try is adding a different method on the static class that doesn't use StackFrame, then call that method before calling your `LogMethodEnter()`.
Dan Bryant
+3  A: 

It might be that the .pdb file containing the line information is out of date.

To fix this, rebuild your project and make sure that the creation of .pdb files is enabled in the project settings. For C# projects this can be configured on the Build tab by setting Advanced -> Debug Info to either full or pdb-only.

0xA3
Good suggestion, I have run into that before. But I did try that already, no effect.
Zachary Yates
+4  A: 

I'm guessing that the line numbers are off (the actual reason for that is not as important) and the exception is actually thrown by this expression:

method.DeclaringType.Namespace

And the reason you might see a NullReference exception there is because the new StackFrame(1) expression a couple lines previous can sometimes return an empty frame. An empty frame means the call to .GetMethod() will return null, and there you go.

The reason you sometimes get an empty frame is that the just-in-time compiler can choose to inline short, repeatedly-called methods like the one in your code. That will throw off your call stack so at best you get a higher-level method than you intended, or at worst (in your Main method) there is no higher method and you get null.

Joel Coehoorn
Interesting. So wouldn't that mean the application throws that exception at runtime? I didn't see that behavior, I suppose I should clarify that the exception ONLY occurs when I'm trying to open the form in the winforms designer.
Zachary Yates
@Zach I think the exception is possible at runtime, but you'll likely never see it in winforms because you're unlikely to use call this from the Main method at the top of the stack. Instead, you might see some incorrect log entries in places where this is inlined. You see it in the designer because visual studio of how visual studio runs the code to figure out designer behavior/appearance. Suddenly an inlined call is running right at the top of the stack.
Joel Coehoorn
@Joel Thanks for the insight, I didn't know about the inlining - I'll try the [MethodImpl] attribute and some null checking.
Zachary Yates
I had the same thought, but how can adding a static constructor fix this problem? And I doubt that the designer inlines code...
0xA3
Zachary Yates
+4  A: 

My guess is that you have an initialization of a static member somewhere in your class, and that initializer throws a NullReferenceException. Furthermore, I guess you have no static constructor, so your object is marked as beforefieldinit and therefore the NullReferenceException is thrown while your method that uses it is JITed.

Something like:

public static class Logger
{
    private static object x = InitObjectX();
    private static object InitObjectX() {
        x.GetHashCode(); // Will throw since x is null.
    }

    public static void LogMethodEnter() 
    { 
        var frame = new StackFrame(1); 
        var method = frame.GetMethod(); 
        Trace.TraceInformation("{0}.{1}.{2}()", method.DeclaringType.Namespace, method.DeclaringType.Name, method.Name); 
        Trace.Indent(); 
    } 

    public static void LogMethodExit() 
    { 
        Trace.Unindent(); 
    } 
} 
erikkallen
I like this guess.
Hans Passant
Hmm, there are a couple of static fields, but they are initialized like so: static string Name = "xyz"; Would that cause the same problem?
Zachary Yates
I was thinking the same, but an exception during the initialization of a static class would normally result in a `TypeInitializationException` (with the NRE as inner exception).
0xA3