In a windows form application, on main form load, i have set a serial port and started reading it. The purpose is, as and when I receive some data on the serial port, I want to open another form related to the data.
So i use the DataReceived Event Handler of the serial port.
void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string str = this.serialPort1.ReadLine();
if (!string.IsNullOrEmpty(str))
{
Main.Instance.customerData = new CustomerData(str);
Main.Instance.customerData.MdiParent = Main.Instance; //Exeption received at this point
Main.Instance.customerData.Disposed += new EventHandler(customerData_Disposed);
Main.Instance.customerData.Show();
}
}
But when I try to open a form within the event handler it gives me an InvalidOperationExeption saying:
"Cross-thread operation not valid: Control 'Main' accessed from a thread other than the thread it was created on."
I tried removing the code line :
Main.Instance.customerData.MdiParent = Main.Instance;
then it works fine. But its necessary also to assign the mdiparent in order to open it as a child form.
Any suggestions to resolve this problem ?
Use the Invoke method on the Main form. You have to pass control over to the Main form to interact with it. The event handler is triggered on a background thread.
Here's some sample code that may work:
void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string str = this.serialPort1.ReadLine();
if (!string.IsNullOrEmpty(str))
{
ShowCustomerData(str);
}
}
private delegate void ShowCustomerDataDelegate(string s);
private void ShowCustomerData(string s)
{
if (Main.Instance.InvokeRequired)
{
Main.Instance.Invoke(new ShowCustomerDataDelegate(ShowCustomerData), s);
}
else
{
Main.Instance.customerData = new CustomerData(str);
Main.Instance.customerData.MdiParent = Main.Instance; //Exeption received at this point
Main.Instance.customerData.Disposed += new EventHandler(customerData_Disposed);
Main.Instance.customerData.Show();
}
}
Your event handler is not running on the UI thread. To get on the UI thread use the Invoke method of the main form.
That is simply because a threading commandment in windows is "Thou shalt not access UI from any other thread but the UI thread"
So you need to use Control.Invoke to run code that Accesses UI on the UI thread.
//assuming your within a control and using C# 3 onward..
this.Invoke( () =>
{
//anything that UI goes here.
}
);
A little Stackoverflow Ninja Google Search Would have helped you out. This happens to be a pretty infamous problem.
This answer seems to be almost exactly your problem:
Cross-thread operation not valid while listening to a COM port
Related
I am intermittently getting a System.ObjectDisposedException after closing a Modal dialog that displays received BLE adverts. There is a higher chance of the error with increasing advert frequency.
The modal dialog is created by the app main form. In fact I have made it persist for the life of the app, in order to cross off one possible Dispose of an object. Code has been reduced to its most basic form to reproduce the problem and there are now more lines of debug asserts and output than actual code.
Here's the main form. Just one button to show the dialog modally...
using System.Diagnostics;
using TestBle;
namespace AdWatcherCrashTest
{
public partial class FormMain : Form
{
WatcherCrashTest watcherCrashTest;
public FormMain()
{
InitializeComponent();
Debug.WriteLine("Main Form Constructor: Create Dialog and assign to Main Form field...");
watcherCrashTest = new();
}
private void button1_Click(object sender, EventArgs e)
{
Debug.WriteLine("ShowDialog...");
DialogResult result = watcherCrashTest.ShowDialog(this);
Debug.Assert(watcherCrashTest != null);
Debug.WriteLine("ShowDialog returned " + result);
}
}
}
And here's the dialog that is displayed when the button is clicked...
using System.Diagnostics;
using Windows.Devices.Bluetooth.Advertisement;
namespace TestBle
{
public partial class WatcherCrashTest : Form
{
private BluetoothLEAdvertisementWatcher deviceWatcher;
public WatcherCrashTest()
{
Debug.WriteLine("Dialog constructor");
InitializeComponent();
deviceWatcher = new BluetoothLEAdvertisementWatcher();
}
private void WatcherCrashTest_Shown(object sender, EventArgs e)
{
Debug.WriteLine("Dialog Shown");
deviceWatcher.Received += deviceWatcher_Received;
deviceWatcher.Start();
}
private void WatcherCrashTest_FormClosing(object sender, FormClosingEventArgs e)
{
Debug.WriteLine("Dialog FormClosing");
if (deviceWatcher != null)
{
deviceWatcher.Stop();
deviceWatcher.Received -= deviceWatcher_Received;
// Adding the line below appears to avoid the exception but smells bad to me
// Application.DoEvents();
}
}
private void deviceWatcher_Received(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
{
String BtDeviceDetails = "0x" + args.BluetoothAddress.ToString("X8") + ": " + args.Advertisement.LocalName;
Debug.WriteLine(BtDeviceDetails);
Debug.Assert(deviceWatcher != null);
Debug.Assert(this != null);
Debug.Assert(!this.IsDisposed);
Debug.Assert(!this.Disposing);
// Debug.Assert(false); // Uncomment to prove out Assert, once losing your mind.
// Getting intermittent 'Exception User-Unhandled' with whole Invoke statement highlighted.
// System.ObjectDisposedException: 'Cannot access a disposed object. ObjectDisposed_ObjectName_Name'
//
// Invoking with the Owner's method does not cause the exception
//this.Owner.Invoke(new Action(() =>
//
// So 'this' form is supposedly Disposed when the Invoke method is called.
// But the form is shown with ShowDialog(), so should not be disposed.
// What the hell???
//
this.Invoke(new Action(() =>
{
// The same exception is thrown, even if the line below is commented out.
listBox1.Items.Add(BtDeviceDetails);
}));
}
}
}
The exception is thrown when the dialog is closed using the standard 'cross' close button in the top right corner. Here is the debug output..
Main Form Constructor: Create Dialog and assign to Main Form field...
Dialog constructor
ShowDialog...
Dialog Shown
0x4EA52B5CA482:
0x78BDBC740D11:
0x6FE1A3309EF8:
0x69B9D65D8361:
0xE135AEE6F3FC:
0xCD322894FB49:
0xD0034B541726:
0x4A47B7552655:
0x78BDBC740D11:
0x1A56AD6F4DEF:
0x63F46825075B:
0xF71E0F339CFF:
Dialog FormClosing
ShowDialog returned Cancel
ShowDialog...
Dialog Shown
0xF71E0F339CFF:
0x6FE1A3309EF8:
0x4EA52B5CA482:
0xD0034B541726:
0x4A47B7552655:
0xC3B56E06B2D9:
0x6FE1A3309EF8:
Dialog FormClosing
Exception thrown: 'System.ObjectDisposedException' in System.Private.CoreLib.dll
An exception of type 'System.ObjectDisposedException' occurred in System.Private.CoreLib.dll but was not handled in user code
Cannot access a disposed object.
In the instance above, the exception was thrown the second time that the form was shown. This varies. Sometimes it is the first. Sometimes it takes several goes.
I have found two ways to prevent the exception, but neither smell right to me...
Add an Application.DoEvents(); in FormClose.
Use the Invoke method of the 'Owner' main form instead of the modal form to populate the listbox in the correct thread.
I don't understand why this (the dialog form) .Invoke causes an exception, since the form is not disposed of by a call to ShowDialog. Clearly the form isn't disposed of, since I can re-open the form by clicking the button on the main form once more. Furthermore, the previous entries in the listbox are still there.
Running a debug build, I get this error. I have yet to get the error on a release build.
Can anyone see why this is happening?
Is my coding sense of smell appropriate, or is one of my fixes a fair solution?
I have about 30 years of Delphi experience, but unfortunately more like 30 minutes of c# .net experience (still learning). So it is quite possible that this error is a basic one, though I've spent hours trying to fix it, with plenty of web searches to no avail. Any help or suggestions appreciated.
Steve
#MikePetrichenko was kind enough to produce the following code, with which I have been unable to recreate the exception. This is very similar to the solution suggest by #Selvin in the comments. Thank you both for your help.
using System.Diagnostics;
using Windows.Devices.Bluetooth.Advertisement;
namespace TestBle
{
public partial class WatcherCrashTest : Form
{
private BluetoothLEAdvertisementWatcher deviceWatcher;
private Boolean CanClose;
public WatcherCrashTest()
{
InitializeComponent();
deviceWatcher = new BluetoothLEAdvertisementWatcher();
deviceWatcher.Stopped += DeviceWatcher_Stopped;
}
private void WatcherCrashTest_Shown(object sender, EventArgs e)
{
CanClose = false;
deviceWatcher.Received += deviceWatcher_Received;
deviceWatcher.Start();
}
private void DeviceWatcher_Stopped(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementWatcherStoppedEventArgs args)
{
CanClose = true;
this.Invoke(new Action(() =>
{
this.Close();
}));
}
private void WatcherCrashTest_FormClosing(object sender, FormClosingEventArgs e)
{
if (deviceWatcher.Status == BluetoothLEAdvertisementWatcherStatus.Started)
{
deviceWatcher.Received -= deviceWatcher_Received;
deviceWatcher.Stop();
}
e.Cancel = !CanClose;
}
private void deviceWatcher_Received(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
{
String BtDeviceDetails = "0x" + args.BluetoothAddress.ToString("X8") + ": " + args.Advertisement.LocalName;
this.Invoke(new Action(() =>
{
listBox1.Items.Add(BtDeviceDetails);
}));
}
}
}
This SO post highlighted by Mike gave me some more ideas... Avoiding `ObjectDisposedException` while calling `Invoke`
Analysis of the problem:
I did some further research and debugging and found the source of the exception to be within the Invoke call itself. Invoke() posts a message from the non-UI thread back to the UI thread, where it executes the specified Action using the UI thread. So the invoke calls end up in the message queue for the UI thread. No amount of asserts or conditions prior to the Invoke() call can prevent the possibility of an exception. This is because at the time the checks are done, they are all passed and the Invoke() call posts a message to the UI thread. The exception occurs in the situation where the form closed occurs and the message queue is processed afterwards. The message posted by Invoke() is then processed and it is unable to run the code. As a result it raises the exception.
Possible solutions:
As I had noted in my original question, an Application.DoEvents() in the FormClose event fixes things. I now believe this is because it ensures all the posted Invoke() messages are processed before the form closes.
Mike's solution works because, after stopping the ad watcher, an Invoke() call is used to finally close the form. Because the message queue has to be processed to run the Form.Close() event within that Invoke(), the other Invoke() calls in the message queue will also get processed. So I believe this removes the possibility of further Invoke() calls being processed after the FormClose, which would cause these exceptions.
I think that these general conclusions would apply to most situations where there are a stream of Invoke calls and a closing form causing exceptions. I.e. There is nothing special about me using a BLE Ad Watcher - the same errors could just as well be caused by any code in another thread using Invoke to access the UI.
I have Window 1 in which on button click i am opening Window 2 in new thread.
Following is my code
private void Button_Click_2(object sender, RoutedEventArgs e)
{
Thread thread = new Thread(() =>
{
Scanner w = new Scanner();
w.Show();
w.Closed += (sender2, e2) =>
w.Dispatcher.InvokeShutdown();
System.Windows.Threading.Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
Window 2 has form I am getting form values on Button click
private void EnterProduct(object sender, RoutedEventArgs e)
{
var data = ProductDetailsData;
LoadCurrentBetween objMain = new LoadCurrentBetween(); //new MainWindow();
objMain.fillorderform(data);
}
on button click of window 2 i am passing values of form to another View
public void fillorderform(dynamic data)
{
this.Dispatcher.Invoke(() =>
{
LoadCurrentdetails.Part = data.Part;
LoadCurrentBetween loadCurrentbtw = new LoadCurrentBetween();
Switcher.Switch(loadCurrentbtw);
});
} public static class Switcher
{
public static MainWindow pageSwitcher;
public static void Switch(UserControl newPage)
{
pageSwitcher.Navigate(newPage);
}
}
Following code is giving error at "this.Content = nextPage;"
The calling thread cannot access this object because a different thread owns it.
public void Navigate(UserControl nextPage)
{
this.Dispatcher.Invoke(() =>
{
var aa = nextPage.Dispatcher.CheckAccess();
this.Content = nextPage;
});
}
I have seen similar Questions asked by other developers but i am not getting how to fix.
pls help
WPF is very strict (compared to Windows forms) about requiring methods which update UI elements to be done on the main/UI thread. So you definitely want both windows to be in the main/UI thread. The error that you are seeing is what happens if you try to do UI work in WPF from a different thread, so you absolutely have to stop doing that. It's OK to have multiple windows open, all on the same UI thread.
If one of your windows is doing heavyweight processing that makes the UI lock up, then the easiest thing is probably to add the async keyword to your button click event, and put the work you are doing in another method which has an async keyword. Then, when you call the helper method, you use the await keyword.
I agree with others that BackgroundWorker and Task are two other ways to accomplish heavyweight processing in a background thread while still having a responsive UI. Tasks are easier to use than BackgroundWorker.
If you are using a BackgroundWorker, it may be good enough to use the RunWorkerCompleted event. If so, look at this post: How to use WPF Background Worker. If you are using a BackgroundWorker and you need to call a custom method in your UI class from the background thread, then pass the Dispatcher object for your window/dialog to the background thread (or get access to it some other way), and when it needs to call back into the UI, use Invoke with the Dispatcher object. By using Invoke, the method you are calling from the background thread will be executed on the UI thread.
I have seen a lot of questions about how to edit controls on c# form from a different thread but none make much sense to me. I understand that you can not change any UI from another thread than it's main. To make this work you have to use invoke and from there safely edit the control?
I have a button that starts writing in a file and the moment you press the button the button itself gets disabled so you can not start multiple threads that do exactly the same. When the writing is done I want the button to be available again but I can not get it working on this other thread.
I have this as the Generate_Click event from the form.
private void Generate_Click(object sender, EventArgs e)
{
Generate.Enabled = false;
int x = 512;
int y = 512;
MBrot mbrot = new MBrot(x, y);
PB_Update lb = new PB_Update(0, y, Generator_PB, Generate, mbrot, this);
lb.Start();
}
And this is in PB_Update.cs the ThreadWork() function, when the while loop is done the writing to the file is done and so is the thread so its ended and given a messagebox with "finished" now as last the button needs to be enabled again.
public void ThreadWork()
{
while (true)
{
if (currValue_ >= maxValue_)
break;
ThreadTick();
}
mb_.StopBrot();
t_.Interrupt();
MessageBox.Show("Finished!");
Generate_.Enabled = true;
}
For WinForms you can execute directly on the thread which the control was created on through the Control.BeginInvoke method, you can use Control.Invoke as well but, Control.BeginInvoke is preferred for UI operations.
public void ThreadWork()
{
while (true)
{
if (currValue_ >= maxValue_)
break;
ThreadTick();
}
mb_.StopBrot();
t_.Interrupt();
MessageBox.Show("Finished!");
Generate_.BeginInvoke((Action)delegate()
{
Generate_.Enabled = true;
});
}
Somehow, get a reference to the form that hosts the generate_ button (let's call it myform). Then, at the bottom of your ThreadWork:
myform.Invoke(new Action(() => {
myform.SetGenerateEnabled();
}));
And then inside your form create that method that enables the button appropriately. (I used a method rather than just updating the button directly so that you don't publicly expose the button.)
This executes the commands inside the { ... } on myform's thread, which is a UI thread, because it is UI. At least, that's what I understand. This is how I do all of my UI updating from other threads.
Here's a simple example of a way to kick off an async task that disables a button for 5 seconds and then enables it again. Meanwhile, the rest of the UI is functional.
Note that this async method exists in the same class as your Generate_Click event, and runs on the UI thread. This means that it can enable and disable the button. But the long running task executes on a separate thread, so it doesn't lock the UI.
Hopefully this sample provides you a base to modify for your own code:
private void Generate_Click(object sender, EventArgs e)
{
DisableButton(sender as Button, 5);
}
private async void DisableButton(Button sender, int secondsToDisable)
{
sender.Enabled = false;
// In your code, you would kick off your long-running process here as a task
await Task.Run(()=>Thread.Sleep(TimeSpan.FromSeconds(secondsToDisable)));
sender.Enabled = true;
}
Hello guys I have a question regardless a old code a client needed a update.
This code add a thread.sleep(500) to keep the service alive, is reading from a com port some calls, and sending a alarm to other pcs now this time when I was sending some information to the machine in question this error pops out
Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on.
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e) {
Thread.Sleep(500);
string data = port.ReadExisting();
//try
//{
if (textBox1.TextLength == 30000)
{
textBox1.Text = "";
}
//}
//catch (Exception) {}
this.BeginInvoke(new setTextDeleg(si_DataRecived), new object[]{
data});
}
This is the function that writes on the com machine, does making a exception to eat the error is ok, or is there another better way to handle it?
PD: Sorry for my bad english, this is on C# 2008 vs
You should modify GUI components like textboxes and labels only from the thread that created them which is the main thread. You may take a look at BackgroundWorker which simplifies this task in WinForms application. And here's another useful article illustrating the usage of the InvokeRequired property and the Invoke method.
It's not a good idea to simply swallow this exception. The exception is occurring because you are not allowed to modify UI components from any thread other than the UI thread (the thread that created them). Instead, check out this MSDN article on how to pass information between worker threads (your thread that sleeps) and UI threads to update the text box in the correct manner.
The problem is because Windows Forms Controls are not thread-safe, and it would seem that the control is not being invoked properly for a thread-safe call. You can use the BackgroundWorker class or you can invoke it yourself. Here is a small code example.
// Delegate used by our worker thread to invoke our control
private delegate void ProgressDelegate(int value);
// Callback method used for our delegate
private void ProgressCallback(int value) {
progressBar1.Value = value;
}
protected override void OnShown(EventArgs e) {
Thread thread = new Thread(new ThreadStart(MyThreadWorker));
thread.IsBackground = true;
thread.Start();
}
// Thread method
private void MyThreadWorker() {
// Setup the delegate
ProgressDelegate mydelegate = new ProgressDelegate(ProgressCallback);
// Do some work
int pos = 0;
do {
pos++;
// Make a thread-safe call to our control and invoke our callback on the original thread
// Original thread: The thread the form and control were created on
progressBar1.Invoke(mydelegate, pos);
} while (pos < 100);
}
I'm guessing what some of your other code looks like, but you could probably move this
if (textBox1.TextLength == 30000)
{
textBox1.Text = "";
}
to the si_DataRecived method, so that it gets executed as part of the BeginInvoke call, the target of which will execute on the main (UI) thread.
To play a bit with threading, delegates and backgroundworkers, I'm putting together a few small applications, I'm having a bit of trouble with one of them.
I've a Windows form, with a textbox, a button and a richttext.
When I press the button, the text in the textbox is used as a paramter to instantiate a class, like this:
public partial class Form1 : Form
{
private BackgroundWorker backgroundWorker;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += new DoWorkEventHandler(worker_DoWork);
backgroundWorker.RunWorkerAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
new Thread((ThreadStart)delegate()
{
this.BeginInvoke((ThreadStart)delegate()
{
foreach (string line in textBox1.Lines)
{
Dig digger = new Dig(line, textBox1.Text);
digger.DomainChecked += new Dig.DomainCheckedHandler(OnUpdateTicker);
string response = digger.GetAllInfo();
richTextBox1.AppendText(response);
Application.DoEvents();
}
});
}).Start();
}
void OnUpdateTicker(string msg)
{
new Thread((ThreadStart)delegate()
{
this.BeginInvoke((ThreadStart)delegate()
{
label4.Text = msg;
Application.DoEvents();
});
}).Start();
}
}
When debugging I run into a 'textBox1.Lines' threw an exception of type 'Microsoft.VisualStudio.Debugger.Runtime.CrossThreadMessagingException'
Any tips on how to solve this problem?
First, there is no need to create new threads inside DoWork; the whole idea with the BackgroundWorker is that DoWork is executed on a separate thread. Second, since DoWork is executed on a separate thread and UI controls can be modified only on the UI thread, you need to invoke those updates correctly. So, a rewritten version of worker_DoWork could look like this:
void worker_DoWork(object sender, DoWorkEventArgs e)
{
foreach (string line in textBox1.Lines)
{
Dig digger = new Dig(line, textBox1.Text);
digger.DomainChecked += new Dig.DomainCheckedHandler(OnUpdateTicker);
string response = digger.GetAllInfo();
richTextBox1.Invoke((Action) delegate { richTextBox1.AppendText(response); });
}
}
Note how the code does not explicitly spawn any new threads, and also how the AppendText method call is done through a Control.Invoke call, forcing it to execute on the UI thread.
The main reason is that the textbox is not owned by the background thread.
Your UI thread owns all the UI objects, and you're spinning up a background thread when a button is pressed. That background thread should not have access to any UI objects.
If you want the value of the textbox to be used, you'll need to pass it to your background thread another way.
Have a look here for an explanation (and solution).
You can only update controls on the main thread from the main thread itself, unless you explicitly tell your program that it's ok to do, by using the .Invoke method of the control.
From: http://www.albahari.com/threading/part3.aspx
Control.Invoke
In a multi-threaded Windows Forms application, it's illegal to call a method or property on a control from any thread other than the one that created it. All cross-thread calls must be explicitly marshalled to the thread that created the control (usually the main thread), using the Control.Invoke or Control.BeginInvoke method. One cannot rely on automatic marshalling because it takes place too late – only when execution gets well into unmanaged code, by which time plenty of internal .NET code may already have run on the "wrong" thread – code which is not thread-safe.