2

It seems a random chance that I will get a cross thread exception upon execution of my win forms application. Here's how I'm trying to manage it:

private void ToOutput(string s)
{
    if (!this.IsHandleCreated)
        this.CreateHandle();

    if (FormOutputArea.InvokeRequired)
    {
        FormOutputArea.Invoke(new Action(delegate ()
        {
            FormOutputArea.AppendText(s + Environment.NewLine);
        }));
    }
    else
    {
        FormOutputArea.AppendText(s + Environment.NewLine);
    }
}

It appears InvokeRequired is not always accurate. I tried BeginInvoke with the same result.

EDIT: Even when I check IsHandleCreated and InvokeRequired using breakpoints they are set to true, yet the else branch of the condition is executed.

Here's a screenshot showing where the exception is now thrown:

enter image description here

Marc
  • 3,905
  • 4
  • 21
  • 37
Lee
  • 3,869
  • 12
  • 45
  • 65
  • You might want to look at the following: http://stackoverflow.com/questions/808867/invoke-or-begininvoke-cannot-be-called-on-a-control-until-the-window-handle-has – Styxxy Sep 14 '13 at 14:15
  • Even checking control handle has been created doesn't help. I'm baffled why the thread exception is thrown once when run five times (~20% chance) in succession. – Lee Sep 14 '13 at 14:27
  • *Even when I check IsHandleCreated and InvokeRequired using breakpoints they are set to true, yet the else branch of the condition is executed.* Can you show this code what you tried in this? – Sriram Sakthivel Sep 14 '13 at 14:35
  • what does `this` refers here? – Sriram Sakthivel Sep 14 '13 at 14:35
  • I expect `this` would refer to the class this method resides in. I initially used `FormOutputArea.IsHandleCreated` however changed it to `this` when the form area object did not have a `CreateHandle` method, to no avail. – Lee Sep 14 '13 at 14:41
  • @SriramSakthivel It was within the code block shown above, however the `e` is thrown within the win form constructor now.. – Lee Sep 14 '13 at 14:43
  • You shouldn't have to rely on a property to know if you should invoke to the UI thread. If you're calling the method from a background thread then you should be invoking, without checking, because you're not in the UI. If your code is designed such that you really don't know if it's going to be called from the UI thread or not then you should fix that problem. Ensure that it can only be called from one or the other. – Servy Sep 14 '13 at 14:45
  • I have a separate thread running a loop checking for internal messages and then prints them to the form area. This is why there are cross thread exceptions. – Lee Sep 14 '13 at 14:51
  • "Even when I check ... InvokeRequired .. set to true, yet the else branch of the condition is executed" That is clearly not true. `if` does not have a bug. You must be misinterpreting what happens. Maybe the exception comes from a different place that you think. Turn on "break on all exceptions" and see where the exception exaclty is occurring. (The screenshot does not show the place where it is thrown.) – usr Sep 14 '13 at 15:02

1 Answers1

5

Putting CreateHandle() and InvokeRequired in the same method is fundamentally wrong. This will explode when the handle isn't created yet, you will create the native window on the wrong thread. A child control's window must be owned by the same thread that owns the form and that thread must pump a message loop (Application.Run).

The simple workaround is to ensure that the thread will not be started until the form's window is created. Which will also create the windows of all the controls embedded in that form. The earliest that happens is in the form's Load event. At that point you can trust InvokeRequired to be accurate.

And beware of trouble when the user closes the form, your original code will behave very poorly if you allow the thread to continue running. It is not yet clear whether you bomb the code before the form's window is created or after it is closed. You must ensure that the thread has either stopped or cannot call ToOutput() anymore. The subject of this answer.

Community
  • 1
  • 1
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Thanks Hans, I think I've sorted the problem. I had background threads initialized and started in the form's constructor. I've moved these to the _Load event, which appears to have sorted it! Thankfully the problem is more simple than I anticipated. – Lee Sep 14 '13 at 15:17