Question

I was getting a weird error in a legacy application (not written by myself), where I was getting a StackOverflow exception when I changed the date on a calendar.

A simplified version is below. This is the code-behind of a Windows Form containing two controls, a Label called label2 and a calendar called MonthCalendar called monthCalendar1.

I think the idea here was to create a typewriter effect. I am on XP, my colleague on Windows 7 is able to run this ok:

private void monthCalendar1_DateChanged(object sender, DateRangeEventArgs e)
{
    const string sTextDisplay = "Press Generate button to build *** Reports ... ";

    for (var i = 0; i < 45; i++)
    {
        label2.Text = Mid(sTextDisplay, 1, i);
        System.Threading.Thread.Sleep(50);

        //Error on this line
        //An unhandled exception of type 'System.StackOverflowException' occurred in System.Windows.Forms.dll
        Application.DoEvents();
    }
}

public static string Mid(string s, int a, int b)
{
    var temp = s.Substring(a - 1, b);
    return temp;
}

I can't see the stack trace, all I see is:

{Cannot evaluate expression because the current thread is in a stack overflow state.}

Also, I'm interested in the comments asking why I haven't checked the stack trace of my StackOverflow exception, as it looks like this isn't possible without third party tools at least.

What could be causing this? Thanks

Was it helpful?

Solution

Remember, programs are stack-based. As your program runs, every function call places a new entry on the stack. Every time a function completes, you pop from the stack to see where to go back to, so you can continue the prior method. When a function completes and the stack is empty, the program ends.

It's important to remember the program stack is generous, but finite. You can only put so many function calls on the stack before it runs out of space. This is what happens when we say the stack overflows.

DoEvents() is just another function call. You might put it in a long-running task to allow your program to handle messages from the operating system about user activity: things like clicks, keystrokes, etc. It also allows your program to handle messages from the operating system, for example if the program needs to re-draw it's windows.

Normally, there will only be one or two (or even zero) messages waiting for a DoEvents() call. Your program handles these, the DoEvents() call is popped off the stack, and the original code continues. Sometimes, there may be many messages waiting. If any of those messages also results in code running that again calls to DoEvents(), we are now another level deep in the call stack. And if that code in turn finds a message waiting which causes DoEvents() to run, we'll be yet another level deep. Maybe you can see where this is going.

DoEvents(), used in conjuction with the MouseMove event, is a common source of problems like this. MouseMove events can pile up on you very quickly. This can also happen with KeyPress events, when you have a key that is held down.

Normally, I wouldn't expect a Calendar DateChanged event to have this kind of problem, but if you have DoEvents() somewhere else, or drive another event (perhaps on your label) that in turn updates your calendar, you can easily create a cycle that will force your program to spiral into a stack overflow situation.

What you want to do instead is explore the BackgroundWorder component, or the newer Task and async patterns.

You may also want to read my write-up on DoEvents() for this question:

How to use DoEvents() without being "evil"?

OTHER TIPS

Normally you have a message pump pretty close to the top of the stack. Adding lots of messages isn't ever resulting in a "deep" stack, as they are all processed by a top level pump. Using DoEvents is creating a new message pump at a point deeper in the stack. If one of the messages that you are pumping also calls DoEvents, you now have a message pump even deeper on in the stack. If that message pump has another message that calls DoEvents ... and you get the idea.

The only way for the stack to clear up again is for the message queue to be empty, at which point you start calling back up the stack until you get to the top level message pump.

The problem here is that your code doesn't make it easy. It calls DoEvents a lot in a loop, so it needs to have an idle queue for quite some time to actually get back out of that loop. On top of that, if you happen to have an "active" application that's sending lots of messages to the message queue, possibly lots of monthCalendar1_DateChanged events, or even other events using DoEvents in a loop, or just other events to keep the queue from being empty, it's not particularly hard to believe that your stack would get deep enough to result in an SOE.

The ideal solution of course is to not use DoEvents. Write asynchronous code instead, so that your stack depth never exceeds a constant value.

DoEvents shouldn't use in any case and you don't require substring to archive a TypeWriting effect

Here is the best way I know at the moment:

    using System.Threading;



    private string text = "this is my test string";
    private void button1_Click(object sender, EventArgs e)
    {
        new Thread(loop).Start();

    }

    private void loop()
    {
        for (int i = 0; i < text.Length; i++)
        {
            AddChar(text[i]);
            Thread.Sleep(50);
        }
    }

    private void AddChar(char c)
    {
        if (label1.InvokeRequired)
            Invoke((MethodInvoker)delegate { AddChar(c); });
        else
            label1.Text += c;
    }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top