0 votes
by (470 points)
edited

Dear Sir/Madam,

I'm testing the 'TerminalControl' component and I would like to ask regarding the 'multithreading'. In my case I've created the Form with TerminalControl and from the other thread I am trying to use the commands SendToServer/Expect. Finally I got the following error

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

I am using the following code but with no success:

private void CurrentTelnetTerminal_OnDataReceived(object sender, EventArgs e) private void CurrentTelnetTerminal_OnDataReceived(object sender, EventArgs e) {

            try
            {
                if (OnReceivedDataToTelnetTerminal != null) OnReceivedDataToTelnetTerminal(mTerminalControl, e);

                if (mTerminalControl.InvokeRequired)
                {
                    ChangeMonitoringTelnetObject MethodCallback = new ChangeMonitoringTelnetObject(CurrentTelnetTerminal_OnDataReceived);
                    mTerminalControl.Invoke(MethodCallback, e);    
                }

            }
            catch (Exception exc)
            {
               string s=exc.Message;

            }
        }

I will be glad to get your comments. Kind regards, Evgeny

8 Answers

+1 vote
by (147k points)
edited
 
Best answer

The current version of TerminalControl is not really suitable to be used with multiple threads.

However, we are currently working on a new scripting API that is much more powerful and versatile than the current one, and more suitable for multithreading. It's nearly finished at this point and you might want to give it a try - it's available at http://www.rebex.net/getfile/d148ba650d77466da45701012f7a5ed8/RebexTerminalEmulation-BetaBuild5229-Trial-Binaries.zip

The major difference is that instead of SendToServer/Expect methods, there is a Scripting object with Send, WaitFor and other methods that can be safely called from a background thread - just make sure to call TerminalControl's SetDataProcessingMode(DataProcessingMode.None) method first to disable automated response processing before calling WaitFor or similar method.

A sample telnet code with TerminalControl-based scripting looks like this with the new version:

// connect to the server and bind TerminalControl
Telnet client = new Telnet(server);    
terminal.Bind(client);

// the rest of this code can be safely called from a background thread

// disable automated response processing
terminal.SetDataProcessingMode(DataProcessingMode.None);

// get the scripting object
Scripting scripting = terminal.Scripting;

// wait for "login" prompt
scripting.WaitFor(ScriptEvent.FromString("ogin:"));

// send user name
scripting.SendCommand(userName);

// wait for "password" prompt
scripting.WaitFor(ScriptEvent.FromString("assword:"));

// send password
scripting.SendCommand(password);

// if wait is needed, uncomment next line and specify a delay
//scripting.WaitFor(ScriptEvent.Delay(200));

// detect prompt
scripting.DetectPrompt();

// start actual work

// send an 'exit' command to close the connection
scripting.SendCommand("exit");
0 votes
by (470 points)
edited

Hi Lukas,

Thanks lot for your response. I'd like to say that "BetaBuild5229-Trial-Binaries.zip" is working much better. There is still a problem with multitasking in terms of synchronization between thread of UI and another threads (as you have mentioned in your example about the rest of the code from another thread). I have verified that if binding to the Telnet channel has been done from the thread control it was created on, it started work much better (with no exception of cross threading). Another questions I'd like to ask. If the issue with multitasking will be completed soon and If I am planning to purchase, which one of versions I supposed to get (I meant the stable one) ? I assume that there is no available documentation for "betta" version up to now.

Thank a lot for assistance.

Kind regards. Evgeny.

by (147k points)
edited

If you purchased now, you would get the current stable version, but we can of course provide a non-trial beta version as well upon request.

At the moment, the beta is mostly finished and we are working on the documentation (and testing). Once we are done, it's going to become the next release. Estimated release date is next month.

Regarding the remaining synchronization issue - could you send some code that reproduces them? It should actually be possible to call Bind from the background thread as well.

0 votes
by (470 points)
edited

Hi Lukas,

Thanks for coming back.

Concerning the code, unfortunately I have no this one right now because it was changed, but I can provide you with short description of the issue:

================================

Task1: • Control Creating • Binding to Telnet • Calling to Task-2 in order to add the TerminalControl component on UI thread

Task-2 (UI) There is no problem when trying to add "Terminal Control" component to the form, but when you try to modify some parameters of the control such as: BackColor ,Cursor,CursorMouse,CursorText,Size and e.t.c

The error of cross-threading is popping up (the problem could be solved by setting CheckForIllegalCrossThreadCalls to false, but as you know it is not recommended)

====================================================

Now, please let me suggest some options regarding the API implementation of the component. It will be nice to have the commands like:

  1. string ReadString(StringToWaitFor [, TimeOutSeconds])
  2. string ReadString(StringsArray [, TimeOutSeconds])
  3. bool WaitFor(StringToWaitFor [, TimeOutSeconds]);
  4. bool WaitFor(StringsArray [, TimeOutSeconds]);
  5. bool TerminalConnectedStatus;

Thanks again for your response,

Kind Regards,

Evgeny

+1 vote
by (15.2k points)
edited

Hi Evgeny,

Regarding the cross-threading exceptions, this is common behaviour in .NET Control classes. You cannot set control's properties from the background thread without using Control.Invoke method (and Control.InvokeRequired property).

Thanks for the API suggestions but these are already possible with the current code you have.

  1. string ReadString(StringToWaitFor [, TimeOutSeconds]) use Scripting.ReadUntil method. As for timeout, you can use ScriptEvent.Delay to expect idle state. It has parameter in milliseconds. The code can look like this:

    // read until 'text' is received or 2 seconds idle state is noticed scripting.ReadUntil("text" | ScriptEvent.Delay(2000));

  2. string ReadString(StringsArray [, TimeOutSeconds]) use the same as in point 1. String array can be done by And and Or operators. You can use either ScriptEvent.And and ScriptEvent.Or methods or & and | operators. Please note that you cannot use operators with string arguments only due to the compilator preferences. At least one of the operands must be of type ScriptEvent. The sample for point 1. will work with many string operands because it contains ScriptEvent.Delay operand. For many strings only you can use this construct:

    scripting.ReadUntil(ScriptEvent.FromString("text") | "other text" | "and some more");

  3. ScriptEvent.Delay can be used with WaitFor as well

  4. is the same as above

  5. If you expecting that the connection will be closed, use ScriptEvent.Close to expect this. If you don't, there is terminal.Disconnected event which will be raised when the connection is closed and this state is noticed. Please note that if you send ie. 'exit' command and the server response contains some data (something like 'logout') and then closes the connection, the first call to any reading method reads the data and does not notice the closed connection. The second one should notice the closed connection, depending on closing response length and message partitioning. Easy way to raise terminal.Disconnected event can be done like this:

    // ... after the work exit the connection       
    scripting.SendCommand("exit");
    scripting.ReadUntil(ScriptEvent.Closed);
    

If you are not expecting idle state but timeout, you can use ScriptEvent.Timeout instead of ScriptEvent.Delay. To set the timeout time use scripting.Timeout property.

Kind regards,

Pavel

0 votes
by (470 points)
edited

Hi Pavel,

Thanks for the explanation you have provided with. Regarding the Control.invoke method, I meant that with using cross-threading operation (for modifying parameters) the object has been handled (by checking if invoke is required and using callback). In any case the new version is working much better than previous one. According to your description you have implemented already the functions I described before and it's working fine, great work! In case of using "Readstring" and "Waitfor" methods, it seems more friendly to use argument as stirng instead of "ScriptEvent.FromString("text")" and argument as double for ms. instead of ScriptEvent.Delay(2000)... it's only suggestion.

Thanks a lot for your assistance.

Kind regards, Evgeny

by (15.2k points)
edited

Hi Evgeny,

in fact you can use argument as string in WaitFor, CheckFor and ReadUntil methods. But you cannot use it when you need operators. Let's see the differences in following examples:

  1. scripting.WaitFor("text");

  2. scripting.WaitFor("text" | "other text");

  3. scritping.WaitFor(ScriptEvent.FromString("text") | "other text");

The first example will work fine, because ScriptEvent class have implicit conversion from string. But in the second example, even though the implicit conversion exists, the compiler apply the operator first, and there is no '|' operator with two strings. The third example will work, because the '|' operator have one SciptEvent operand and the second is convertible to ScriptEvent as well, so the compiler know that the '|' is used for two ScriptEvent objects.

Regarding ScriptEvent.Delay(double) vs. double only we discussed it internally and decided not to implement the conversion from double to ScriptEvent because it lacks the clarity what it will mean. Simply, this allow us some enhancement in future.

Kind regards, Pavel

0 votes
by (470 points)
edited

Thanks!

Kind regards, Evgeny

+1 vote
by (15.2k points)
edited

Hi Evgeny,

This Scripting API is released in 2014-R2 release. If you purchased release 2014-R1, you should be able to download latest release in your download page.

Kind regards, Pavel

0 votes
by (470 points)
edited

Hi Pavel,

Thanks for the update, I have another question, what is the maximum number of terminal controls (binded to telnet connection) can be added to WinForms (with a trial version is 5), and for the licensed version?

Thanks

Kind regards, Evgeny

by (15.2k points)
edited

Hi Evgeny,

there is no limitation in trial version nor full version either. It may depends on target server how many connections allows. Or do you mean something different than maximum connections to one server? Because you can have many WinForm TerinalControls in one application without any limitation.

...