SSDR API - Getting Exception Due to Cross-Thread Error WithTextBox

  • 1
  • Question
  • Updated 4 years ago
Have been doing well with writing simple apps with the API until now. Started a new app to save and store frequency memories and scan.  However, ran into issues with just simple things I already had working - display frequency, power, temperature, etc.  Am getting the following error in debug mode:

"An exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll but was not handled in user code
Additional information: Cross-thread operation not valid: Control 'modelTextBox' accessed from a thread other than the thread it was created on."

This happens in the first call to set the text in the radio model text box which is a line early in the "RadioAdded()" event handler.  I have not found the source of the problem yet.

I looked at some of the sample code others have written and found the following in the code to load the windows form:

        private void Form1_Load(object sender, EventArgs e)        {

            CheckForIllegalCrossThreadCalls = false;
        }

Found if I replaced the "false" with "true" the sample code app (which had been working just fine in debug and non-debug) starting crashing as well.  In my new app, it blows up regardless of whether set to "true" or "false", so I have something more going on it seems as well.

Anyone else seeing similar issue that might be able to help me with the underlying cause?  Just getting started with C# and threading is still an unknown.  

Gotta admit though, playing with the API is great fun.

73, Tom
K1FR
Photo of K1FR

K1FR

  • 109 Posts
  • 9 Reply Likes

Posted 4 years ago

  • 1
Photo of Peter K1PGV

Peter K1PGV, Elmer

  • 541 Posts
  • 315 Reply Likes
What you're seeing is a common question among people who are just starting to write C# GUI apps.

The basic problem is that the .Net controls themselves are not "thread safe", meaning they have internal state (parameters) that is changed/updated without acquiring any locks.  This can lead to problems if two threads simultaneously try to update the same control.

The exception you're seeing is thrown when the thread that created the control is different from the thread that updates the control.  IIRC, this exception is thrown when you do NOT run the program under the control of the debugger.

There are two classic ways to handle this problem:

  • The wrong way: Disable the check.  This option leaves you open to all manner of race conditions on the control.  The problem is, disabling the check is easy and mostly works.

    If I'm just fooling around for my own benefit, or trying to get something working, this is the option I choose.

  • When you update the control, if you're not certain you're in the same thread as the thread that created the control... check.  If you're not in the proper context, execute a method that runs in the context of the creating thread to update the control.  There are a couple of discrete ways to do this. Both are annoying and cumbersome, but safe.  Assuming you're using WinForms, you can find more (including examples) on MSDN here.

    If I'm writing a "real" program that I intend to distribute to the world, or on which I intend to rely, this is what I do.
Note that directing all the updates to one, dedicated, thread (other than the creating thread) or acquiring your own locks around updates to the control do not fix the problem.  While these approaches fix the problem of you potentially doing simultaneous updates on multiple threads, they do not handle issues of the CLR or WinForms code manipulating the control (such as during setup or tear down).

WinForms programming in C# is incredibly pleasant and easy.  Except, you know, when it isn't.

Hope that helps,

Peter
K1PGV
Photo of K1FR

K1FR

  • 109 Posts
  • 9 Reply Likes
Peter:  Many thanks!  Very helpful, indeed.  For now I am going to take the course of disabling the check and see if I can find why my code won't run like the sample code.  But, do intend to study the link you sent and try to understand the more robust approach.

At the moment, I am trying to sort through why the exception is thrown in my RadioAdded code and not in the sample code (WorkingCSharpFlexSample) when the illegal cross thread call check is disabled.  I thought I had pretty much mimicked the sample.  Think I will walk away from it for awhile and then maybe some obvious mistake will show up!  :)

Thanks again for the help.  73, Tom, K1FR
Photo of K1FR

K1FR

  • 109 Posts
  • 9 Reply Likes
Peter:  Well, succeeded in getting the new app stable again.  I had gooned up one of the event handlers.  Also, appears changing the default names suggested by VS for the handlers is not a good idea.  Not sure of cause and effect there, but when I got creative about the method names started getting exceptions again.

Anyway, thanks once more for the help.  <Think> i am back on track now.  Still need to figure out how to implement the safer method of avoiding cross thread calls for the future.  Guess that is why I still love ham radio after 56 years - one never stops learning (though in my case it appears one slows down in learning!).

73, Tom
K1FR
Photo of Peter K1PGV

Peter K1PGV, Elmer

  • 541 Posts
  • 315 Reply Likes
Yay!  Glad to hear you're back on track.

Doing the thread safe callback is really easy (if a bit annoying).  Instead of simply doing:
        textBox1.Text = "Put this in my text box";
That's NOT thread safe.  You write a function like this:
private void SetText(string text)
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
Invoke(d, new object[] { text });
}
else
{
textBox1.Text = text;
}
}
And then call it whenever you want to change the content of your text box:
    SetText("Put this is my text box");
Like I said... a bit annoying.  The above is based loosely on the MSDN example at the link I provided.

Enjoy,

Peter K1PGV
(Edited)