8

Background

I have a DataGridView control which I am using, and I added my handler below to the DataGridView.CellFormatting event so the values in some cells can be made more human-readable. This event handler has been working great, formatting all values without issue.

Recently however, I have discovered a very rare circumstance causes an unusual bug. The column in my DataGridView for the item's due date always has an int value. 0 indicates the event is never due, any other value is a UTC timestamp for the due date. The MySQL db column corresponding doesn't allow nulls. When the user has moved from one DataGridView row with a due date, to another DataGridView row with a due date (at this point everything is still appears fine), and then presses a button which reloads the data from the database (without sending updates, essentially calling DataAdapter.Fill()), the program generates a StackOverflowException**.

No recursion?

What is so unusual to me is that I do not see where the recursion or infinte-looping is. I added int cellFormatCallCount as a class member, and increment it during each call, but at the time the exception is thrown, the debugger shows the value of that int as 1, which I would expect since I wasn't under the impression and recursion was occuring.

Can somebody help me?

How can I view a stack trace? In VS2008 it says: {Cannot evaluate expression because the current thread is in a stack overflow state.}

Best regards,

Robinson

private int cellFormatCallCount = 0;
private void myDataGridView_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)  {
    try {
        // to format the cell, we will need to know its value and which column its in
        string value = "";
        string column = "";
        
        // the event is sometimes called where the value property is null
        if (e.Value != null) {
            cellFormatCallCount++; // here is my test for recursion, this class member will increment each call

            // This is the line that throws the StackOverflowException
            /* ********************* */
            value = e.Value.ToString();
            /* ********************* */

            column = actionsDataGridView.Columns[e.ColumnIndex].Name;
        } else {
            return; // null values cannont be formatted, so return
        }

        if (column == "id") {
            // different code for formatting each column
        } else if (column == "title") {
            // ...
        } else {
            // ...
        }
    } finally {
        cellFormatCallCount = 0; // after we are done with the formatting, reset our recursion counter
    }
}
Community
  • 1
  • 1
gnirts
  • 83
  • 1
  • 1
  • 5
  • Can you post the stack trace? That would help a lot. – Michael Myers Apr 22 '09 at 20:09
  • I thought this thread was about this website. Boy, I need a break =p – JoshJordan Apr 22 '09 at 20:10
  • @JoshJordan - Me too! I was trying to figure out how he got an exception from the website. Sheesh! – Nick DeVore Apr 22 '09 at 20:11
  • If you put a breakpoint on the offending line, can you step into the property access of e.Value? Can you Quickview e.Value? If so, what do you see? – JP Alioto Apr 22 '09 at 20:16
  • It's a long shot, but what is in e.Value? If the ToString on that is defined recursively then you could have a stackoverflow. Yes it sounds crazy, but easier than you might think especially when you have an object with a property that allows you to do a "set" with type Object that might just happen to contain itself. – Brad Barker Apr 22 '09 at 20:26
  • If you break right before the ToString call and put e.Value.ToString() in your watch window, does it cause the stackoverflow from the debugger? – Brad Barker Apr 22 '09 at 20:33
  • Would trying CStr(e.Value) provide any clues? – rvarcher Apr 22 '09 at 21:37
  • @gnirts, you accepted the answer, care to let us know what was happening? – H H Apr 23 '09 at 17:52

7 Answers7

5

Apparently e.Value.ToString() invokes the CellFormatting event again. That seems somewhat logical. It should be easy enough to find out with a debugger.

But the actual recursion could be caused somewhere else, like in the per-column formatting that you omitted.

Your recursion check isn't reliable since Value==null will also reset it, and it appears to be shared by all columns. Make it surround the e.Value.ToString() more tightly:

if (e.Value != null) 
{
   cellFormatCallCount++; 
   System.Diagnostics.Debug.Assert(cellFormatCallCount <= 1, "Recursion");
   value = e.Value.ToString();
   cellFormatCallCount--; 
   ...
} 
H H
  • 263,252
  • 30
  • 330
  • 514
2

Totally random guess (with no stack trace it's all I can do)...

Are you attempting to display/format a type which has a potentially recursive ToString()?

public string ToString()
{
   return ... this.ToString() ...
   // or
   return String.Format("The value is {0}", this);
}

A typo/error like that could cause a StackOverflowException...

Daniel LeCheminant
  • 50,583
  • 16
  • 120
  • 115
  • See: http://stackoverflow.com/questions/62188/stack-overflow-code-golf/62195#62195 – C. K. Young Apr 22 '09 at 20:16
  • if you want to see the stack overflow, you need to put a break point where it's happening, rather than on the exception. i would try putting it on the line where it's saying SO is, and trying to see where it goes from there. – Darren Kopp Apr 22 '09 at 20:19
  • No, I didn't subclass DataGridViewCellFormattingEventArgs. – gnirts Apr 22 '09 at 20:20
  • 1
    @gnirts: If you set a (possibly conditional) breakpoint on the line that is causing the SO, you should be able to step into it ... that may help you identify the recursive path... – Daniel LeCheminant Apr 22 '09 at 20:22
  • Well you can test a negative on the ToString() method by placing an equivalent e.Value.ToString() ealier in the method and see if you get the stackoverflow there... If you don't it cannot be the ToString. I jut think it would be strange if the .net provided DataGridViewCellFormattingEventArgs class implemented ToString this way :) – asgerhallas Apr 22 '09 at 20:45
  • Oh. Brad Barker just said the same thing in a comment on the question... sorry for duplicating. – asgerhallas Apr 22 '09 at 20:47
1

@Daniel: If that were the issue, wouldn't it already raise the exception in the line:

if (e.Value != null) {

@gnirts: Could you post the full method and stack trace too?

@BCS (below): I think that might be it, but it might easily be in some of the code that is not shown in the deo posted.

PS. I'm sorry, this should have been a comment, but I have not enough reps :-D

asgerhallas
  • 16,890
  • 6
  • 50
  • 68
1

Given that this is an event, might it be triggering its self?

BCS
  • 75,627
  • 68
  • 187
  • 294
  • I don't think its firing itself since the counter would increment, correct? The MSDN pages say the event is fired every time the form needs to be re-painted---quite frequently, which makes using break points in this code block very time consuming. Im still looking for a good way to debug this. – gnirts Apr 22 '09 at 20:30
  • But you are not forcing it to repaint from inside the method with som eof the not shown code? Another thing, have you tried with out the try/finally block, just in case it's setting it to 0 without you knowing it. – asgerhallas Apr 22 '09 at 20:35
1

Try making the cellFormatCallCount variable static so that it shared by all instances of the class. I suspect that somehow the event is triggering itself but you aren't seeing it because cellFormatCallCount is only local to each instance of the class handling the event and thus is never incremented beyond 1. If that's the case, then the actual trigger for the stackoverflow (recursion) could be anywhere in the method and it just happens to run out of stack space at that line.

Once you've made the variable static, you could throw an exception when it exceeds a certain (small) value, like 2. That exception should leave a viewable stack trace around.

tvanfosson
  • 524,688
  • 99
  • 697
  • 795
  • I added the static modifier to the class member, but the count is still 1 at the time of the stack overflow. – gnirts Apr 22 '09 at 20:28
0

It's unrelated to the problem but you can actually have a StackOverflowException without recursion at all just with:

throw new StackOverflowException();
Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
0

I Have just had a similar problem of stackoverflow with no trace details.

My problem was due to a instance of an object which should not have been instantiated. I removed the offending new object and all was fine.

In the circumstance above without seeing any more code, ensure multiple events are not fired using the =- to cancel the events accordingly.

I hope this might help someone.

27k1
  • 2,212
  • 1
  • 22
  • 22