6

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.

Zachary Yates
  • 12,966
  • 7
  • 55
  • 87
  • 1
    Have you tried looking at the disassembly? You might be able to see what it's doing there (although i haven't had much success getting symbols to work in the disassembly view myself). There's also the slim possibility you're running code that doesn't matches your source. – Rup Aug 26 '10 at 13:23
  • 2
    Have you tried cleaning the solution and rebuilding? – FrustratedWithFormsDesigner Aug 26 '10 at 13:24
  • 1
    @Rup: Agree about the code that VS is complaining about not in sync with the file - it might be working with something that's cached? – FrustratedWithFormsDesigner Aug 26 '10 at 13:25
  • @Rup, @Frustrated: Yes, tried clean/rebuild. Also tried closing VS and reopening the project. I have had the situation you describe happen before, but the close/reopen usually worked. Any ideas on how to make sure the assembly/object code isn't cached? – Zachary Yates Aug 26 '10 at 13:28
  • Can you show what else is in your class? – Dirk Vollmar Aug 26 '10 at 13:32
  • 1
    Avoid running code like this at design time. Use the Control.DesignMode property. – Hans Passant Aug 26 '10 at 14:26
  • 1
    You also need to suppress inlining with the [MethodImpl] attribute. – Hans Passant Aug 26 '10 at 14:26
  • Would it be possible to share a minimal sample to reproduce the behavior? With the current sample everything runs fine for me in the designer. – Dirk Vollmar Aug 26 '10 at 15:18

5 Answers5

4

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.

Zachary Yates
  • 12,966
  • 7
  • 55
  • 87
Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
  • 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 Aug 26 '10 at 14:16
  • @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 Aug 26 '10 at 14:24
  • 1
    Thanks for the insight, I didn't know about the inlining - I'll try the [MethodImpl] attribute and some null checking. – Zachary Yates Aug 26 '10 at 14:37
  • I had the same thought, but how can adding a static constructor fix this problem? And I doubt that the designer inlines code... – Dirk Vollmar Aug 26 '10 at 15:19
  • I fixed the problem, through a combination of answers from everyone - I accepted this one because it's got the most info. I used @Hans Control.DesignMode suggestion, @0xA3 .pdb out of date suggestion to get the error to change when I changed the code, @Joel null checking & compiler inlining suggestion – Zachary Yates Aug 27 '10 at 12:47
4

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
  • 33,800
  • 13
  • 85
  • 120
  • 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 Aug 26 '10 at 14:40
  • 1
    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). – Dirk Vollmar Aug 26 '10 at 15:10
3

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.

Dirk Vollmar
  • 172,527
  • 53
  • 255
  • 316
1

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
  • 12,966
  • 7
  • 55
  • 87
  • Does your method access any static fields of the class? – Dirk Vollmar Aug 26 '10 at 13:30
  • 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 Aug 26 '10 at 13:33
  • 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. – Dirk Vollmar Aug 26 '10 at 13:52
  • 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 Aug 26 '10 at 13:54
0

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
  • 1,384
  • 1
  • 17
  • 29
  • 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 Aug 26 '10 at 13:58