locked
Stack Overflow when updating winforms label text RRS feed

  • Question

  • I am getting Stack Overflow errors when I update a Windows Forms Label text property 2652 or 2653 times. I assume the stack has that much room, but what stack is it, and is there a way to reset it?

    The calling code looks like so

          private void button1_Click(object sender, EventArgs e)
            {
                Task writePatientsTask = new Task(() => TryToOverflow());
                writePatientsTask.ContinueWith(Continuer);
                writePatientsTask.Start();
            }
    
            private void TryToOverflow()
            {
                for (int iterations = 0; iterations < 10000; iterations++)
                {
                    UpdateMessage($"Iteration {iterations}");
                    System.Threading.Thread.Sleep(10);
                }
            }
            private void UpdateMessage(string message)
            {
                WinTools.SetTextForControl(message, messagesLabel);
            }
    
            public static void SetTextForControl(string text, Control control)
            {
                if (control.InvokeRequired)
                {
                    control.Invoke(new SetTextDelegate(SetTextForControl), text, control);
                }
                else
                {
                    ToolTip controlTip = new ToolTip();
                    controlTip.SetToolTip(control, text);
                    control.Text = text;
                }
            }


    Thanks for any help!

    Ethan


    Ethan Strauss

    Update: I added some code to look at the call stack directly and

            public static void SetTextForControl(string text, Control control)
            {
                if (control.InvokeRequired)
                {
                    control.Invoke(new SetTextDelegate(SetTextForControl), text, control);
                }
                else
                {
                    var tracer = new System.Diagnostics.StackTrace();
                    int counter = tracer.FrameCount;
    
                    int size = tracer.GetFrames().Sum(f => f.ToString().ToCharArray().Length);
                    text += $" Frame count:{counter} Size:{size}";
                    ToolTip controlTip = new ToolTip();
                    controlTip.SetToolTip(control, text);
                    control.Text = text;
                }
            }

    The behavior is the same (actually, it overflew at 2651 instead of 2652). The frame count was constant at 22 and the size (sum of number of characters in all frames. Don't know if that's meaningful) was constant at 1650 ever at the time of over flow.

    This seems to make even less sense to me. Isn't Stack Overflow what the size of the call stack is too large? How can it overflow when it remains the same size?

    Thanks,

    Ethan


    Saturday, April 18, 2020 1:47 AM

All replies

  • Your code is intentionally overflowing the stack, correct?

    I do not know what you are asking. All of the following are questions you ask:

    • what stack is it, and is there a way to reset it?
    • Isn't Stack Overflow what the size of the call stack is too large?
    • How can it overflow when it remains the same size?

    Your title says when updating winforms label text. Is that the fundamental problem? If you need to update a label many times during processing then the stack is not relevant. What you need to do is to use the BackgroundWorker class and then update the label in the progress event.



    Sam Hobbs
    SimpleSamples.Info

    Sunday, April 19, 2020 7:13 PM
  • Thanks Sam,

         The code I posted is intentionally overflowing the stack. I did this to narrow down what was causing the stack overflow in much more complicated code, not because I actually want to use the overflow somehow.

         Do you know why/how the code I wrote is causing a stack overflow? What is important difference between a BackgroundWorker and a Task (which is what I used)?

        Thanks again!

    Ethan


    Ethan Strauss

    Sunday, April 19, 2020 7:48 PM
  • Does this also happen when you use control.BeginInvoke instead of control.Invoke?
    Sunday, April 19, 2020 8:03 PM
  • BeginInvoke causes the page to stop updating visually after about 500 iterations and then gives the same StackOverflow error at about 3000 iterations (I didn't test enough to really tell how many).

    But, I figured out that the error does not seem to have anything to do with the Label per se, but rather with the tooltip. I was creating and setting a new tooltip every time. In retrospect, I now see that that label was getting thousands of associated tooltip objects. Not surprising that is a problem.

         ToolTip controlTip = new ToolTip();
         controlTip.SetToolTip(control, text);

    I would still like to be able to set the tooltip on the control (this helps mitigate against text longer than the control can accommodate), but there does not seem to be a way retrieve the tooltip associated with a control. I guess I will change the method to pass in the tooltip as a parameter.

    Thanks for your help. It got me picking apart what was happening in more detail and led to the solution.

    Ethan


    Ethan Strauss

    • Proposed as answer by BonnieBMVP Tuesday, April 21, 2020 3:52 PM
    Sunday, April 19, 2020 10:01 PM
  • Your question is very unclear and the fundamental problem is correspondingly unclear.

    You are doing 10000 iterations to intentionally overflow the stack. There is no answer to the question because you are intentionally causing the problem. Nothing else matters; intentional iteration for the purpose of overflowing the stack will overflow the stack.



    Sam Hobbs
    SimpleSamples.Info

    Sunday, April 19, 2020 10:36 PM
  • Hi Ethan Strauss,
    Base on your description, you have used Control.Invoke method and task to update winforms label text.
    And you are doing 10000 iterations to intentionally overflow the stack. What is your fundamental requirement?
    >>What is important difference between a BackgroundWorker and a Task (which is what I used)
    The BackgroundWorker is a WinForms component that creates a background thread using the Event-Based Asynchronous pattern, and you can populate the work done on this background thread with your own code in the DoWork event handler. In general, Microsoft no longer recommends using this pattern 
    The Task.Run code uses the more natural and less error-prone try/catch blocks.
    Here is a document you can refer to.
    Best Regards,
    Daniel Zhang


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Tuesday, April 21, 2020 8:46 AM
  • Stack overflows can occur in one of several situations:

    - You recursively call enough functions to run out of stack space, rare but possible. The stack grows up in memory and more memory is allocated until we run out.

    - You corrupt the stack because of bad data management causing the runtime to not be able to figure out the stack. This shouldn't happen in .NET but in unmanaged code it is possible.

    - If you are accessing the UI on a non-UI thread then you'll eventually get an exception. In my experience it is either a stack overflow or invalid operation exception, depending upon what is going on.

    In your sample code neither of these is occurring. You are assuming you are going to cause a stack overflow by using your for loop but that isn't a stack overflow because it isn't a recursive call. Inside your for loop you're only stack frame (relative) from button1_Click. Calling another method inside a for loop simply pushes the UpdateMessage method onto the stack, calls it and then removes it. This isn't replicating a stack overflow/recursive call in any language, net alone C#. You could remove the for loop and have the exact same impact on the stack. You can confirm this by breaking into the debugger while that code is running and looking at the stack.

    When using a Task you are running, by default, on a thread pool thread. Each thread has its own stack which is independent of other stacks. While it is theoretically possible to have a small stack and still get a stack overflow because the rest of the process tasks ate up memory but this would be an odd situation.

    As for what is happening inside your UpdateMessage call it is calling SetTextForControl which calls Invoke and then blocks. The UI thread (your main thread) is then given a message to call it again. At this point your stack isn't that large (on either thread). So this code is probably not the cause of your issues.

    What you didn't provide is this `Continuer` method that is called after the task completes (which would be after all the iterations run). Perhaps that is causing the issue. It is hard to tell at this point. 

    The nice thing about stack overflows is that if you're running this code in the debugger then the debugger puts a buffer on the stack so that when this exception occurs the debugger can better point you to where things went wrong. Therefore I'd recommend you run this code in the debugger, replicate the stack overflow and then look at the stack trace and exception details. Posting that might help clarify where things are going wrong.

    Lastly, since you're using tasks make sure all UI interactions are on the UI thread only. This is a common problem in Winforms.


    Michael Taylor http://www.michaeltaylorp3.net

    Tuesday, April 21, 2020 2:28 PM