views:

2547

answers:

6

Hello,

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
    }
}
+2  A: 

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
See: http://stackoverflow.com/questions/62188/stack-overflow-code-golf/62195#62195
Chris Jester-Young
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
No, I didn't subclass DataGridViewCellFormattingEventArgs.
gnirts
@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
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
Oh. Brad Barker just said the same thing in a comment on the question... sorry for duplicating.
asgerhallas
A: 

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

throw new StackOverflowException();
Mehrdad Afshari
See: http://stackoverflow.com/questions/62188/stack-overflow-code-golf/62244#62244 :-)
Chris Jester-Young
this would have a stack trace though. a true stack overflow doesn't let you evaluate anything.
Darren Kopp
@Darren Kopp: A "true" StackOverflow gives you the ability to ask, answer and comment on questions :)
Mehrdad Afshari
+1  A: 

@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
Now you do :-] (have enough rep for a comment that is). Welcome to SO...
Daniel LeCheminant
Oh that's so nice! Thank you :-D
asgerhallas
And you were 100% correct that the earlier line would have raised the exception ;]
Daniel LeCheminant
+1  A: 

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

BCS
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
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
+1  A: 

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
I added the static modifier to the class member, but the count is still 1 at the time of the stack overflow.
gnirts
+1  A: 

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--; 
   ...
}
Henk Holterman