Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cross thread error, but not using threads

I keep getting

Cross-thread operation not valid: Control 'keyholderTxt' accessed from a thread other than the thread it was created on.

on various controls on various forms in a project, and I have googled it and found lot's of responses about how to access stuff from various threads, but as far as I know, i'm not using any other threads in my project, and to change the hundreds of possible places in the code would be unmanageable.

It never used to happen, only since I added various code that seems unrelated. I include a sample of places where I get the errors below, but it has occurred in so many places all over the solution.

keyholderTxt.Text = "Keyholders Currently In:\r\n \r\n Nibley 1: + keyholders";

or this, a better example, as you can see everything that happends from the form loading until the error:

      private void Identification_Load(object sender, System.EventArgs e)
    {
        _Timer.Interval = 1000;
        _Timer.Tick += new EventHandler(_Timer_Tick);
        _Timer.Start();

        txtIdentify.Text = string.Empty;
        rightIndex = null;

        SendMessage(Action.SendMessage, "Place your finger on the reader.");

        if (!_sender.OpenReader())
        {
            this.Close();
        }

        if (!_sender.StartCaptureAsync(this.OnCaptured))
        {
            this.Close();
        }
    }

    void _Timer_Tick(object sender, EventArgs e)
    {
        this.theTime.Text = DateTime.Now.ToString();
    }

    private void OnCaptured(CaptureResult captureResult)
    {
       txtIdentify.Clear();
       //other stuff after the cross thread error
    }

Can things like not closing datareaders cause this kind of error?

I am using a Windows Forms Application.

like image 782
Wayneio Avatar asked Dec 16 '22 07:12

Wayneio


2 Answers

I suspect the culprit is this:

if (!_sender.StartCaptureAsync(this.OnCaptured))

I don't know the API you're using, but based on the name, I think the callback method (OnCaptured) is called on a worker thread, not the UI thread. So you need to use Invoke to perform the action on the UI thread:

private void OnCaptured(CaptureResult captureResult)
{
   if (InvokeRequired)
   {
       Invoke(new System.Action(() => OnCaptured(captureResult)));
       return;
   }

   txtIdentify.Clear();
   // ...
}
like image 85
Thomas Levesque Avatar answered Dec 18 '22 12:12

Thomas Levesque


Okay, scratch this. I see you're using System.Windows.Forms.Timer which, as the comment below mentions, already executes on the UI thread. I was thinking you were using System.Timers.Timer.

Wrong answer

The timer callback is executing on a threadpool thread. You can make it execute on the UI thread by setting the SynchronizingObject:

    _Timer.Interval = 1000;
    _Timer.Tick += new EventHandler(_Timer_Tick);
    _Timer.SynchronizingObject = this;
    _Timer.Start();
like image 34
Jim Mischel Avatar answered Dec 18 '22 12:12

Jim Mischel