0

I have a C# multi-threaded app with a bunch of worker threads in different pools all calling this function to update a textbox in a WinForms app.

It has a very subtle bug (or maybe not if you know what you are doing) that results in an Out-Of-Memory exception over a long period of time (I think because the stack never gets to unroll, but I'm not sure).

What is the correct way to update a textbox from any thread in my app without encountering this issue? The root problem is Application.DoEvents, but I don't know how to force a GUI update without it (if I take it out, the GUI never updates).

Ideally the solution would be a function that I can call from any thread that just does the right thing.

private void WriteStatus(string s)
    {


        if (textBoxStatus.InvokeRequired)
        {
            textBoxStatus.Invoke(new Action<string>(WriteStatus), new object[] { s });
        }
        else
        {
            StringBuilder sb = new StringBuilder(s + Environment.NewLine);
            sb.Append(textBoxStatus.Text.Substring(0, textBoxStatus.Text.Length > 40000 ? 40000 : textBoxStatus.Text.Length));
            textBoxStatus.Text = sb.ToString();

            // don't call DoEvents more than once a second - this prevents stack over flow from frequent updates
            if (DateTime.Now - lastGUIUpdate > TimeSpan.FromSeconds(1))
            {
                Application.DoEvents();
                lastGUIUpdate = DateTime.Now;
            }
        }

    }

I've looked at other SO solutions for this problem, but they seem to ignore the Application.DoEvents step. How are they forcing their GUI to update in this circumstance? In my app commenting this line out fixes my memory problems, but my textbox never updates.

John Shedletsky
  • 7,110
  • 12
  • 38
  • 63
  • `Application.DoEvents()` is potentially dangerous because it can cause events to fire outside of normal program flow. If you need to repaint a control during a multi-threaded operation, you are better off calling the `Control.Refresh` method. – Bradley Smith Jun 23 '15 at 05:03
  • Would you mind also explaining what you actually trying to do? Also showing how you call the function may be useful. – Alexei Levenkov Jun 23 '15 at 05:03
  • I did not know about Control.Refresh(). I will try that. – John Shedletsky Jun 23 '15 at 05:06
  • @Alexei - my typical use pattern is that my main gui thread calls a bunch of functions that have thread pools to do web requests. As web requests complete, I call WriteStatus to update the GUI to show that results are coming in. – John Shedletsky Jun 23 '15 at 05:07
  • I think I could use Tasks, but I don't want async to pollute my entire code base, like it usually does when you start using it. – John Shedletsky Jun 23 '15 at 05:09
  • I see - so basically you trying to workaround bug in your code where you synchronously waiting on UI thread for all requests to finish ... Consider posting that part so people can actually suggest solutions. – Alexei Levenkov Jun 23 '15 at 05:14
  • You might be interested in [my solution](http://stackoverflow.com/a/16745054/643085) which uses newer, better technology as opposed to winforms, allowing much greater performance, and a richer UI. – Federico Berasategui Jun 23 '15 at 05:24
  • @HighCore Maybe for my next project, but I have a fair amount of legacy code in this one. – John Shedletsky Jun 23 '15 at 18:25
  • @JohnShedletsky you might want to just keep your legacy code untouched and do this particular part of your UI in WPF using an [`ElementHost`](https://msdn.microsoft.com/en-us/library/system.windows.forms.integration.elementhost(v=vs.110).aspx) which allows WPF content to be hosted inside winforms. – Federico Berasategui Jun 24 '15 at 02:20

0 Answers0