I'm facing a bit of problem, it's giving error "Cross-thread operation not valid" even though I'm using Invoke method.
Here's the code snipit.
Method to update log box
private void updateStatus(String msg)
{
if (logBox.InvokeRequired)
logBox.Invoke((MethodInvoker)delegate()
{
logBox.SelectionStart = logBox.Text.Length;
logBox.Text += "\n";
logBox.Text += msg;
});
else
logBox.SelectionStart = logBox.Text.Length;
logBox.Text += "\n";
logBox.Text += msg;
}
And this Run method is being run by a thread.
private void Run()
{
int port;
try
{
port = Int32.Parse(broadcastPortTextBox.Text);
}
catch (Exception ex)
{
MetroFramework.MetroMessageBox.Show(this, ex.Message);
return;
}
updateStatus("Starting server at port: " + port.ToString());
server = new HTTPServer.HTTPServer(port);
server.Start();
} //function
It runs fine for the first time but when I click stop, it gives an exception.
private void stopButton_Click(object sender, EventArgs e)
{
updateStatus("Stoping server");
th.Abort();
updateStatus("Server stoped!");
}
I would try using the direct cast for the invoke. There's no need to check whether the invoke is required or not. If you invoke something it should always happen (in your context). Just remove the updateStatus(String msg) method so and try to cast your update like this:
void Run() {
// stuff
broadcastPortTextBox.Invoke(() => {
port = Int32.Parse(broadcastPortTextBox.Text);
});
// stuff..
logBox.Invoke(() => {
logBox.SelectionStart = logBox.Text.Length;
logBox.Text += string.Format("{0}{1}", Environment.NewLine, "Your message text..");
});
// stuff..
}
Note: If you manipulate any non thread owned element use the invoke method. Otherwise you'll end up with exceptions (see 'broadcastPortTextBox');
Edit: Accidently saved before I was done.
Related
I have an async function which still freezes / lags the UI thread for me when I execute it. This is my function calling it.
private void TcpListenerLogic(object sender, string e)
{
Application.Current.Dispatcher.BeginInvoke((Action)async delegate {
try
{
dynamic results = JsonConvert.DeserializeObject<dynamic>(e);
if (results.test_id != null)
{
// Get properties for new anchor
string testInformation = await CommunicationCommands.getJsonFromURL(
"http://" + ServerIP + ":" + ServerPort + "/api/" + results.test_id);
}
}
catch (Exception exception)
{
// Writing some Trace.WriteLine()'s
}
});
}
And this is the async function that freezes my UI Thread
public static async Task<string> getJsonFromURL(string url)
{
try
{
string returnString = null;
using (System.Net.WebClient client = new System.Net.WebClient())
{
returnString = await client.DownloadStringTaskAsync(url);
}
return returnString;
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
return null;
}
}
I already tried to make everything in TcpListenerLogic run in a new Thread:
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
}).Start();
Which resulted in the whole UI completely freezing. And I tried to make TcpListenerLogic async and await the dispatcher, which also made everything freeze permanently. I also tried to make TcpListenerLogic async and leave the dispatcher. The dispatcher is only there because I normally have some UI code in there, which I left out for my tests.
I have ventured far through the internet, but no BackgroundWorker, ThreadPool or other methods helped me in my endeavour.
If anyone has help for this particular problem, or a resource that would improve my understanding of async functions in C#, I would much appreciate it.
Edit
As requested a deeper insight in how this event handler is called.
I have System.Net.Websocket, which is connected to the Backend API I am working with and triggers an event, everytime he receives new Data. To guarantee the socket listens as longs as it is open, there is a while loop which checks for the client state:
public event EventHandler<string> TcpReceived;
public async void StartListener(string ip, int port, string path)
{
try
{
using (client = new ClientWebSocket())
{
try
{ // Connect to backend
Uri serverUri = new Uri("ws://" + ip + ":" + port.ToString() + path );
await client.ConnectAsync(serverUri, CancellationToken.None);
}
catch (Exception ex)
{
BackendSettings.IsConnected = false;
Debug.WriteLine("Error connecting TCP Socket: " + ex.ToString());
}
state = client.State;
// Grab packages send in backend
while (client.State == WebSocketState.Open || client.State == WebSocketState.CloseSent)
{
try
{
// **Just formatting the received data until here and writing it into the "message" variable**//
TcpReceived(this, message);
// Close connection on command
if (result.MessageType == WebSocketMessageType.Close)
{
Debug.WriteLine("Closing TCP Socket.");
shouldstayclosed = true;
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
break;
}
state = client.State;
}
catch
{
BackendSettings.IsConnected = false;
state = client.State;
}
}
state = client.State;
}
}
catch (Exception ex)
{
// Some error messages and settings handling
}
}
The Event has a handler attached:
TcpReceived += TcpListener_TcpReceived;
And this is the Handler, which calls the previously seen "TcpListenereLogic".
private void TcpListener_TcpReceived(object sender, string e)
{
TcpListenerLogic(sender, e);
//App.Current.Dispatcher.BeginInvoke(new Action(() => {
// TcpListenerLogic(sender, e);
//}));
//new Thread(() =>
//{
// Thread.CurrentThread.IsBackground = true;
// TcpListenerLogic(sender, e);
//}).Start();
}
I previously had the "TcpListenereLogic" as the handler, but I wanted to try different methods to call it. I also left in the commented out part, to show how the call of "TcpListenereLogic" looked already. All my attempts were with all mentioned setups and sadly lead to nothing.
Thank you very much #TheodorZoulias for helping me to find the solution to my problem.
It turns out it wasn't the async function itself, but rather how often it gets called. It got called roughly ~120 times every second.
My solution starts by calling the Listener method over a new Thread:
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
MainWindow.tcpListener.StartListener(ip, portNumber, "/api/");
}).Start();
To limit the amount of calls that happen every second I added a dispatcher timer, that resets a bool after it has been used for a call, by my Event.
readonly System.Windows.Threading.DispatcherTimer packageIntervallTimer =
new System.Windows.Threading.DispatcherTimer();
bool readyForNewPackage = true;
private void ReadyForPackage(object sender, EventArgs e)
{
readyForNewPackage = true;
}
public async void StartListener(string ip, int port, string path)
{
packageIntervallTimer.Interval = TimeSpan.FromMilliseconds(50);
packageIntervallTimer.Tick += (s, e) => { Task.Run(() => ReadyForPackage(s, e)); };
packageIntervallTimer.Start();
Then I wrapped everything inside the while loop into an if condition based on the bool, the most important part was to have my "event EventHandler TcpReceived" in there:
// Grab packages sent in backend
while (client.State == WebSocketState.Open || client.State == WebSocketState.CloseSent)
{
if (readyForNewPackage == true)
{
readyForNewPackage = false;
try
{
....
TcpReceived(this, message);
....
}
catch
{
...
}
}
}
I added my TcpListenerLogic to the Eventhandler:
TcpReceived += TcpListenerLogic;
And my TcpListenerLogic now looked like this (names have been changed):
private async void TcpListenerLogic(object sender, string e)
{
try
{
dynamic results = JsonConvert.DeserializeObject<dynamic>(e);
if (results.test_id != null)
{
string testID = "";
if (results.test_id is JValue jValueTestId)
{
testID = jValueTestId.Value.ToString();
}
else if (results.test_id is string)
{
testID = results.test_id;
}
// Get properties for new object
string information = await CommunicationCommands.getJsonFromURL(
"http://" + ServerIP + ":" + ServerPort + "/api/" + testID );
if (information != null)
{
await App.Current.Dispatcher.BeginInvoke(new Action(() =>
{
// Create object out of the json string
TestStatus testStatus = new TestStatus();
testStatus.Deserialize(information);
if (CommunicationCommands.isNameAlreadyInCollection(testStatus.name) == false)
{
// Add new object to the list
CommunicationCommands.allFoundTests.Add(testStatus);
}
}));
{
}
catch (Exception exception)
{
....
}
}
Adding a new Thread to execute any step results in problems, so keep in mind that all this uses the thread created at the beginning for "StartListener"
I read/write data to serial port and I want to see reading on listbox right away. I created a new thread to send command to serial port. I keep the main thread empty, so, it can update the UI and also serial port event handler wont be interrupted with something else.(I am not sure is it right approach?)
The following code works with while (!dataRecieved) { Thread.Sleep(4000); } but does not works with while (!dataRecieved) { Thread.Sleep(100); }.
The problem is if I use 100ms sleep, serial port event handler fire only once and then program stops!(If I debug with breakpoint 100ms works because I create additional time when stepping into the code.) If I wait 4000ms the program works. Also, I check the time between sending data and receiving data from serial port is 200ms. So, 100ms is reasonable.
Here is the code:
public bool dataRecieved = false;
public Form1()
{
InitializeComponent();
}
public void AppendTextBox(string value)
{
this.Invoke((MethodInvoker)delegate { richTextBox1.Text += value + "\n";});
}
private void button1_Click(object sender, EventArgs e)
{
serialPort1.Open();
Thread testThread = new Thread(() => sendThread());
testThread.Start();
}
public void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
data = serialPort1.ReadLine();
dataRecieved = true;
}
public void sendThread()
{
for(int i = 0; i<10; i++)
{
serialPort1.WriteLine("AT" + i);
// Following line creates odd situation:
// if Thread.Sleep(100), I receive only first data, then program stops(serial port handler doesnt fire!).
// if Thread.Sleep(4000), I receive all data, successfuly works.
// But I do not want to wait 4000ms, because I receive answer from device in 200ms.
while (!dataRecieved) { Thread.Sleep(100); }
AppendTextBox("Received" + "AT" + i);
dataRecieved = false;
}
}
Where I am wrong? Can you please provide a solution?
I even didn't use a new Thead for write and read on SerialPort. You just need use update control in Invoke() is ok. Below is my update on richTextBox. You can change form richTextBox to your listbox.
public void update_RichTextBox(string message)
{
Invoke(new System.Action(() =>
{
txtReceivedData.Text += message;
txtReceivedData.Refresh();
txtReceivedData.SelectionStart = txtReceivedData.Text.Length;
txtReceivedData.ScrollToCaret();
}));
}
and the way to use above void:
if (ComPort.IsOpen)
{
ComPort.Write(_inputdata + "\r");
Form1._Form1.update_RichTextBox(_inputdata + "\r");
string _receviedData = ComPort.ReadExisting();
Form1._Form1.update_RichTextBox(respond);
ComPort.DiscardInBuffer();//delete all data in device's received buffer
ComPort.DiscardOutBuffer();// delete all data in transmit buffer
}
else
{
MessageBox.Show("haven't yet open COM port");
return "FLASE";
}
I use something I call "Cross Thread Linker"
#region Cross Thread Linker
public bool ControlInvokeRequired(Control c, Action a)
{
if (c.InvokeRequired) c.Invoke(new MethodInvoker(delegate { a(); }));
else return false;
return true;
}
void Update_RichTextBox(RichTextBox rtb, string Text)
{
if (ControlInvokeRequired(rtb, () => Update_RichTextBox(rtb, Text))) return;
rtb.AppendText(Text + Environment.NewLine);
}
#endregion
Then:
Update_RichTextBox(richTextBox1, "Text to append");
I am unable to get the reason for this Exception:
private void bwWorker_DoWork(object sender, DoWorkEventArgs e)
{
if (Main.bolDebugMode)
MessageBox.Show("Function DoWork is called");
if (Main.ftpsync(Main.strUsername407, Main.strPassword407, sender as BackgroundWorker) == 0)
e.Result = e.Result + "No error in " + Main.strUsername407;
else
{
if (Main.bolDebugMode)
MessageBox.Show("Errors in " + Main.strUsername407);
e.Cancel = true;
e.Result = e.Result + "Errors in " + Main.strUsername407;
if (Main.bolDebugMode)
MessageBox.Show("Errors marked");
try
{
MessageBox.Show("Next step throws exception");
return;
}
catch (Exception error)
{
if (error.ToString() != null)
MessageBox.Show(error.InnerException.Message);
}
}
}
It throws this exception:An unhandled exception of type 'System.Reflection.TargetInvocationException' occurred in mscorlib.dll
Additional information: Exception has been thrown by the target of an invocation.
The target (to my limited understanding) is the backgroundworker's RunWorkerCompleted function:
private void bwWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (Main.bolDebugMode)
MessageBox.Show("DoWork Completed. Break: " + e.Cancelled + " Error: " + e.Error + " Result: " + e.Result);
// First, handle the case where an exception was thrown.
if (e.Error != null)
{
lStatus.Text = e.Error.Message;
}
else if (e.Cancelled)
lStatus.Text = "Cancelled: " + e.Result.ToString();
}
else
{
lStatus.Text = "Done! " + e.Result;
Thread.Sleep(Convert.ToInt16(Main.strGlobalWaitTime));
pbProgress.Value = 0;
lStatus.Text = "";
}
if (Main.bolDebugMode)
MessageBox.Show("Analysis completed");
// Enable the Start button.
btnStart.Enabled = true;
// Disable the Cancel button.
btnCancel.Enabled = false;
}
public class Main
{
#region Variables
// Variables - FTP Settings
// Reading tons of variables from a appconfig file like so:
private static string StrGlobalWaitTime = ConfigurationManager.AppSettings["program_globalWaitTime"];
private static bool BolDeleteRemoteFiles = Convert.ToBoolean(ConfigurationManager.AppSettings["program_deleteremotefiles"]);
// Configuring the variables to receive and write
public static string strGlobalWaitTime
{
get { return StrGlobalWaitTime; }
set { StrGlobalWaitTime = value; }
}
#endregion
#region Main function
public static int ftpsync(string strUsername, string strPassword, BackgroundWorker bwWorker)
{
if (Directory.EnumerateFiles(strWorkDirectory, "*.pdf").Any())
{
bwWorker.ReportProgress(0, "Files left! Upload not complete");
Thread.Sleep(Convert.ToInt16(Main.strGlobalWaitTime));
return 1;
}
However, it doesn't even reach the first debugging message box. Thus it must be happening between the return and the beginning of the function. Is he background worker not handing over directly to the RunWorkerCompleted function? Can anyone tell me what I am missing or doing wrong?
This is my first question. I will try to provide as much information as possible. Google searches for the most obvious queries have been done.
The thing to look for whenever you encounter a TargetInvocationException is the InnerException property, which will have the "real" exception.
From the look of it, it will most likely have something to do with trying to access the GUI from inside the worker's thread. Remember that when using the DoWork handler, the code executes in a different thread from the main UI's. Therefore, calls to GUI components must be either done via Invoke calls or avoided all together.
I am kind of new to c# and I am required to create a client server chat. Our professor gave us the following as a small hint to get us going. But I do not understand what the backgroundworker does.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) // Receive data
{
while (client.Connected)
{
try
{
receive = streamreader.ReadLine();
this.textBox2.Invoke(new MethodInvoker(delegate() { textBox2.AppendText("You : " + receive + "\n"); }));
receive = "";
}
catch (Exception x)
{
MessageBox.Show(x.Message.ToString());
}
}
}
private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e) // Send data
{
if (client.Connected)
{
streamwriter.WriteLine(text_to_send);
this.textBox2.Invoke(new MethodInvoker(delegate() { textBox2.AppendText("Me : " + text_to_send + "\n"); }));
}
else
{
MessageBox.Show("Send failed!");
}
backgroundWorker2.CancelAsync();
}
The BackgroundWorker class is designed to execute operations on a seperate thread, whilst reporting to the main thread through the ProgressChanged and RunWorkerCompleted events.
The example your professor provided is far from a typical implementation of the class, and a backgroundworker should probably not be used for something like that.
I have just started to learn about threads and methodinvoking in c#, but I have come across a problem which I couldn't find the solution of.
I made a basic C# form program which keeps updating and displaying a number, by starting a thread and invoke delegate.
Starting new thread on Form1_load:
private void Form1_Load(object sender, EventArgs e)
{
t = new System.Threading.Thread(DoThisAllTheTime);
t.Start();
}
Public void DoThisAllTheTime (which keeps updating the number) :
public void DoThisAllTheTime()
{
while(true)
{
if (!this.IsDisposed)
{
number += 1;
MethodInvoker yolo = delegate() { label1.Text = number.ToString(); };
this.Invoke(yolo);
}
}
}
Now when I click the X button of the form, I get the following exception:
'An unhandled exception of type 'System.ObjectDisposedException' occurred in System.Windows.Forms.dll
Can't update a deleted object'
While I actually did check if the form was disposed or not.
EDIT: I added catch (ObjectDisposedException ex) to the code which fixed the problem.
Working code:
public void DoThisAllTheTime()
{
while(true)
{
number += 1;
try {
MethodInvoker yolo = delegate() { label1.Text = number.ToString(); };
this.Invoke(yolo);
}
catch (ObjectDisposedException ex)
{
t.Abort();
}
}
}
Your call to this.IsDisposed is always out of date. You need to intercept your form closing event and stop the thread explicitly. Then you won't have to do that IsDisposed test at all.
There are many ways you can do this. Personally, I would use the System.Threading.Tasks namespace, but if you want to keep your use of System.Threading, you should define a member variable _updateThread, and launch it in your load event:
_updateThread = new System.Threading.Thread(DoThisAllTheTime);
_updateThread.Start();
Then in your closing event:
private void Form1_Closing(object sender, CancelEventArgs e)
{
_stopCounting = true;
_updateThread.Join();
}
Finally, replace the IsDisposed test with a check on the value of your new _stopCounting member variable:
public void DoThisAllTheTime()
{
MethodInvoker yolo = delegate() { label1.Text = number.ToString(); };
while(!_stopCounting)
{
number += 1;
this.Invoke(yolo);
}
}
Just put this override in your form class:
protected override void OnClosing(CancelEventArgs e) {
t.Abort();
base.OnClosing(e);
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
Thread.CurrentThread.Abort();
}