Welcome to the new FlexRadio Community! Please review the new Community Rules and other important new Community information on the Message Board.
If you are having a problem, please refer to the product documentation or check the Help Center for known solutions.
Need technical support from FlexRadio? It's as simple as Creating a HelpDesk ticket.

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

K1FR
K1FR Member ✭✭
edited June 2020 in SmartSDR API
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

Answers

  • Peter K1PGV
    Peter K1PGV Member ✭✭✭
    edited June 2020
    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

  • K1FR
    K1FR Member ✭✭
    edited February 2017
    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
  • K1FR
    K1FR Member ✭✭
    edited February 2017
    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
  • Peter K1PGV
    Peter K1PGV Member ✭✭✭
    edited June 2020
    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

Leave a Comment

Rich Text Editor. To edit a paragraph's style, hit tab to get to the paragraph menu. From there you will be able to pick one style. Nothing defaults to paragraph. An inline formatting menu will show up when you select text. Hit tab to get into that menu. Some elements, such as rich link embeds, images, loading indicators, and error messages may get inserted into the editor. You may navigate to these using the arrow keys inside of the editor and delete them with the delete or backspace key.