I have a BackgroundWorker to call a function to do a long process at BackgroundWorker _DoWork, when error occur in the function I will prompt a customized messagebox:
WPFMessageBoxResult result = WPFMessageBox.Show("Activation Fail", "Error!!", WPFMessageBoxButtons.OK, WPFMessageBoxImage.Error);
The exception below happens at WPFMessageBoxResult class :
The calling thread must be STA, because many UI components require this.
Thank you.
You should not try to interact with any UI components from a background thread.
One way could be to catch the exception in your doWork method and assign it to the backgroundworker's result property and then check if that result is a type of exception or not null if you are not using the result for anything else. then check for it in the backgroundWorker_completed event.
BackgroundWorker_DoWork(sender, )
{
try
{
// do work
}
catch (Exception ex)
{
BackgroundWorker w = sender as BackgroundWorker;
if (w != null)
w.Result = ex;
}
}
then
BackgroundWorker_Completed()
{
if (s.Result != null && e.Result is Exception)
{
Exception ex = e.Result as Exception;
// do something with ex
}
}
Usually with Winforms/WPF you use Invoke() to jump onto the UI thread if you need to interact with the UI from a long-running task. You can call invoke from any object that belongs to the UI, but be sure when in the invoke scope to only do as little code as possible. Since this code is on the UI thread it will block/hang the UI if it takes too long.
public void BackgroundWorkerMethod()
{
try
{
// do work
}
catch (Exception e)
{
uiObject.Invoke(new Action(() => {
// now you are on the UI thread
Message.ShowMessage(e.Message);
});
}
}
Your background thread is just a worker thread and not a user interface thread. WPF and WinForms both require that the thread performing user interface actions be marked as STA (Single Threaded Apartment) because the user interface code is not thread safe. They also require that you add a message pump so that windows messages are dispatched.
I recommend that instead of showing the message box in your worker thread you send a message to your main user interface thread and have that thread show the message box. To do this you should pass a reference to the Dispatcher from the main UI thread into the worker thread. Then use the Dispatcher.BeginInvoke to request a delegate be called back on the main thread.
Alternatively you can wait for the background thread to complete and then check the result and show the appropriate answer to the user. Either way the worker thread should not be directly interacting with the user interface.
You must use this method
void BGW_DoWork(object sender, DoWorkEventArgs e)
{
try
{
Dispatcher.BeginInvoke(new Action(() =>
{
Button btn = new Button();
btn.Width = 100;
btn.Height = 50;
btn.Content = "Test";
myG.Children.Add(btn);
}
));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Related
I've made an app that runs around the clock with three Backgroundworkers running in different intervals.
In their DoWork i do some Dispatcher.BeginInvoke so it updateds some charts. The problem is that its crashing during the night and I'm unsure why. I've wrapped the Dispatcher.BeginInvoke in try/catch, but since I'm invoking the UI thread, I'm thinking I maybe do the try catch INSIDE the Dispatcher.BeginInvoke instead.
Does it matter?
test if the EventArgs Error property is not null in the RunWorkerCompleted method
backgroundWorker.RunWorkerCompleted += (s, e) =>
{
if (e.Error != null)
{
// handle error
}
}
The try/catch will only work with the stuff that's happening in its current thread. If an error happened in another thread, the worker will finish but you don't know why unless you inspect the Error property.
A callback queued with Dispatcher.BeginInvoke is asynchronous. You should observe all exceptions inside the delegate you pass into Dispatcher.BeginInvoke, because they are not getting propagated anywhere outside it (except as Application.Current.DispatcherUnhandledException, AppDomain.CurrentDomain.UnhandledException events and as DispatcherOperation.Task.Exception property, see below). If they go unhanded, they will crash the app inside the core Dispatcher event loop on the UI thread.
This includes RunWorkerCompletedEventArgs.Error too. An exception thrown inside Dispatcher.BeginInvoke delegate will not be available there as Error, upon RunWorkerCompletedEvent event.
Here is a simple example illustrating the problem. Note how e.Error is null inside RunWorkerCompleted:
// UI Thread
// prepare the message window
var window = new Window
{
Content = new TextBlock { Text = "Wait while I'm doing the work..." },
Width = 200,
Height = 100
};
// run the worker
var dispatcher = Dispatcher.CurrentDispatcher;
var worker = new BackgroundWorker();
worker.DoWork += (s, e) =>
{
// do the work
Thread.Sleep(1000);
// update the UI
dispatcher.BeginInvoke(new Action(() =>
{
throw new ApplicationException("Catch me if you can!");
}));
// do more work
Thread.Sleep(1000);
};
worker.RunWorkerCompleted += (s, e) =>
{
// e.Error will be null
if (e.Error != null)
MessageBox.Show("Error: " + e.Error.Message);
// close the message window
window.Close();
};
// start the worker
worker.RunWorkerAsync();
// show the modal message window
// while the worker is working
window.ShowDialog();
To solve the problem, observe the exceptions with something like this:
var step = 0; // progress
// do the work on a background thread
// ..
var lastStep = step++;
Dispatcher.BeginInvoke(new Action(() =>
{
try
{
// do the UI update
}
catch(Exception ex)
{
// log or report the error here
MessageBox.Show("Error during step #" +
lastStep + ": " + ex.ToString());
}
}));
Alternatively, you can keep track of all DispatcherOperation returned by Dispatcher.BeginInvoke:
var invokes = new List<DispatcherOperation>();
// do the work on a background thread
// ..
invokes.Add(Dispatcher.BeginInvoke(new Action(() =>
{ /* update the UI */ }))));
Then you can examine DispatcherOperation.Task.Exception of each invocation you've queued with Dispatcher.BeginInvoke. I don't think this is feasible though, unless you can prevent the invokes list from growing endlessly.
please help someone.
I want to create and start a new task in the button click handler and it always causes aggregate exception. I'm doing the following:
private void btn_Click(object sender, EventArgs e)
{
Task<Image> t = Task<Image>.Factory.StartNew(InvertImage,
TaskCreationOptions.LongRunning);
t.ContinueWith( task => {
some code here;
pictureBox1.Image = t.Result;
},
TaskContinuationOptions.OnlyOnRanToCompletition);
t.ContinueWith( task => { some code here },
TaskContinuationOptions.OnlyOnFaulted);
}
private Image InvertImage()
{ some code here }
The code if run in the main thread works perfectly, so here is definetely something wrong with my understanding of using Tasks. Thank you in advance.
By default continuation runs on default scheduler which is Threadpool Scheduler. Threadpool threads are always background threads so they can't update the UI components (as UI components always run on foreground thread). So your code won't work.
Fix: Get the scheduler from UI thread.This will ensure that the continuation runs on the same thread which created the UI component
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
and than pass it to ContinueWith function.
t.ContinueWith( task => {
some code here;
pictureBox1.Image = t.Result;
},
TaskContinuationOptions.OnlyOnRanToCompletition,scheduler);
In Winforms(or even in WPF) only the thread who create the component can update it you should make your code thread-safe.
For this reason the debugger raises an InvalidOperationException with the message, "Control control name accessed from a thread other than the thread it was created on." which is encapsulated as AggregateException because tasks encapsulate all exceptions in aggregate exception
you can use this code to iterate through all exceptions in aggregate exception raised by the task
try
{
t.Wait();
}
catch (AggregateException ae)
{
// Assume we know what's going on with this particular exception.
// Rethrow anything else. AggregateException.Handle provides
// another way to express this. See later example.
foreach (var e in ae.InnerExceptions)
{
if (e is MyCustomException)
{
Console.WriteLine(e.Message);
}
else
{
throw;
}
}
}
To make your thread safe just do something like this
// If the calling thread is different from the thread that
// created the pictureBox control, this method creates a
// SetImageCallback and calls itself asynchronously using the
// Invoke method.
// This delegate enables asynchronous calls for setting
// the text property on a TextBox control.
delegate void SetPictureBoxCallback(Image image);
// If the calling thread is the same as the thread that created
// the PictureBox control, the Image property is set directly.
private void SetPictureBox(Image image)
{
// 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 (this.picturebox1.InvokeRequired)
{
SetPictureBoxCallback d = new SetPictureBoxCallback(SetPictureBox);
this.Invoke(d, new object[] { image });
}
else
{
picturebox1.Image= image;
}
}
Another option to use a Task result within the calling thread is using async/await key word. This way compiler do the work of capture the right TaskScheduler for you. Look code below. You need to add try/catch statements for Exceptions handling.
This way, code is still asynchronous but looks like a synchronous one, remember that a code should be readable.
var _image = await Task<Image>.Factory.StartNew(InvertImage, TaskCreationOptions.LongRunning);
pictureBox1.Image = _image;
I have a BackgroundWorker that I call from my main UI thread as follows.
On the MainWindow I declare the BackgroundWorker
private BackgroundWorker backgroundWorkerRefreshFromWeb = new BackgroundWorker();
And in the constructor I set it up as follows.
backgroundWorkerRefreshFromWeb.WorkerReportsProgress = true;
backgroundWorkerRefreshFromWeb.WorkerSupportsCancellation = true;
backgroundWorkerRefreshFromWeb.DoWork +=
new DoWorkEventHandler(backgroundWorkerRefreshFromWeb_DoWork);
backgroundWorkerRefreshFromWeb.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(backgroundWorkerRefreshFromWeb_RunWorkerCompleted);
backgroundWorkerRefreshFromWeb.ProgressChanged +=
new ProgressChangedEventHandler(backgroundWorker_ProgressChanged);
At the end of the constructor I call the following method to start it on its way.
private void RefreshWebDataTimer(Object state)
{
if (!backgroundWorkerRefreshFromWeb.IsBusy && !backgroundWorkerLoadFromDB.IsBusy)
{
System.Diagnostics.Debug.Print("Refresh Timer Started at {0}", DateTime.Now);
backgroundWorkerRefreshFromWeb.RunWorkerAsync(nfl);
}
}
The DoWork event handler calls a method from another project (DLL). That method has a pattern which calls multiple threads to do process work. when one of those threads throws an error, the application crashed and the BackgroundWorker does not catch it in the RunWorkerCompleted event. The pattern is a complex (probably overly complicated) but is as follows.
In the method the DoWork event handler calls I create a set of "sub-worker" threads in a wrapper as follows...and then WAIT for all threads to finish processing before moving on.
private static void GetRoster(Nfl teams, ref ManualResetEvent[] mre, ref int index)
{
mre = new ManualResetEvent[Dictionary.NUMBER_OF_NFL_TEAMS];
ParseDataAsyncWrapper[] dw = new ParseDataAsyncWrapper[Dictionary.NUMBER_OF_NFL_TEAMS];
index = 0;
foreach (NflTeam team in teams)
{
//Get Roster and create players
mre[index] = new ManualResetEvent(false);
dw[index] = new ParseDataAsyncWrapper(team, mre[index]);
ThreadPool.QueueUserWorkItem(new WaitCallback(dw[index].RosterCallbackAsync), index++);//Don't fully understand this
Thread.Sleep(wait.Next(Dictionary.THREAD_WAIT_MS));
}
foreach (ManualResetEvent re in mre) { if (re != null) { re.WaitOne(); } } //Wait for all threads to finish
mre = null; //allow to be disposed
dw = null;
}
I use the callback for each thread to get a webpage and then process that page:
internal async void RosterCallbackAsync(object State)
{
if (Thread.CurrentThread.Name == null) { Thread.CurrentThread.Name = string.Format("Roster{0}", State); }
WebPage = await Html.WebClientRetryAsync(Dictionary.ROSTER_WEBPAGE.Replace(Dictionary.CITY_REPLACE_STR, this.Team.CityAbr));
Html.ParseRoster(WebPage, Team);
DoneEvent.Set();
}
I am then throwing the exception in Html.ParseRoster but it is not getting caught. This is on a different thread than the BackgroundWorker. I don't know why the BackgroundWorker is not catching it. Since I am waiting for all threads to finish before moving on I don't think the RunWorkerCompleted event would run before I am done here.
I've looked at the help page for Application.DispatcherUnhandledException event and it states:
you will need to write code to do the following: Handle exceptions on
the background thread. Dispatch those exceptions to the main UI
thread. Rethrow them on the main UI thread without handling them to
allow DispatcherUnhandledException to be raised.
My question is 1) Why is the excpetion not being caught? Should I use Application.DispatcherUnhandledException and if so how can I accomplish this? I would ultimately like to throw these exceptions to the BackgroundWorker. Any suggestions or comments would be greatly appreciated.
UPDATE
I have been working on using TPL with await/async and Tasks and updated my code. This has been somewhat successfull as I am now getting the exception back to the BackgroundWorker. Ignoring how I get the exception back to the DoWork event for now ... I check that I am getting an exception by adding a try/catch block and am catching and re-throwing the exception. Here is my DoWork event
private async void backgroundWorkerRefreshFromWeb_DoWork(object sender, DoWorkEventArgs e)
{
// Do not access the form's BackgroundWorker reference directly.
// Instead, use the reference provided by the sender parameter.
BackgroundWorker bw = sender as BackgroundWorker;
// Start the time-consuming operation.
NflStatsComplete = false;
bw.ReportProgress(0, "Starting Data Refresh from Web...");
try
{
e.Result = await Html.RetrieveWebData(bw, e);
}
catch (Exception ex)
{
throw;
}
// If the operation was canceled by the user,
// set the DoWorkEventArgs.Cancel property to true.
if (bw.CancellationPending)
{
e.Cancel = true;
}
}
In the debugger I get an exception and see it thrown. However, when it then goes to the RunWorkerCompleted event the RunWorkerCompletedEventArgs e shows e.Error == null. I don't understand how this can be since I am throwing an exception directly from the DoWork event. Can someone explain this behavior?
The Backgroundworker is setup as an async method. I believe this is causing the RunWorkerCompleted event to fire before the DoWork event has completed and the exception has been raised. By updating the DoWork method to remove the async compiler keyword and removing any awaits the exception propogates back to the method specified by the RunWorkerCompleted event and e.Error != null
I have a program that has stock quotes pushed to me via an API. The program also has a front end, made in XAML, that freezes while this program is running (i.e. processing the information that the API is sending me). I've tried using Dispatcher.Invoke and/or BackgroundWorker and have read up on threading plenty, but can't get it to unfreeze. Perhaps I'm just doing something wrong. I've attached the relevant code here. Was hoping someone could help.
private void QuoteUpdate(QuoteInfo info)
{
BackgroundWorker bwQuoteUpdate = new BackgroundWorker();
bwQuoteUpdate = new BackgroundWorker();
bwQuoteUpdate.WorkerSupportsCancellation = true;
bwQuoteUpdate.DoWork += bwQuoteUpdate_DoWork;
bwQuoteUpdate.RunWorkerAsync(info);
}
private void bwQuoteUpdate_DoWork(object sender, DoWorkEventArgs e)
{
try
{
Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadStart(() =>
{
QuoteInfo info = e.Argument as QuoteInfo;
//logical functions and work are here
}));
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show("Error in QuoteUpdate: " + ex.Message, "Exception Thrown");
}
}
Although you’re creating a BackgroundWorker with the intention of executing your long-running task on a background thread, you’re still dispatching all your processing back onto the UI thread.
private void bwQuoteUpdate_DoWork(object sender, DoWorkEventArgs e)
{
// Code here runs on background thread.
Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadStart(() =>
{
// Code here runs on UI thread.
}));
}
What you need to do is first perform your calculations on the background thread, but do not update any UI components; rather, store all your results in local variables. Then, once that’s done, use the Dispatcher.Invoke to dispatch control back to the UI thread, and use the results stored in your local variables to update your UI.
For example:
private void bwQuoteUpdate_DoWork(object sender, DoWorkEventArgs e)
{
// Code here runs on background thread.
QuoteInfo info = e.Argument as QuoteInfo;
string result = PerformLongRunningProcessing(info);
Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadStart(() =>
{
// Code here runs on UI thread.
this.resultTextBox.Text = result;
}));
}
Yes, you are doing something wrong. The computation should be done in thread alone add only UI changes should be done in Dispatcher.Invoke.
And if you use DataBinding through INotifyPropertyChange, then drop the Dispatcher.Invoke completly, because marshaling the changes to UI thread is done automaticaly.
Try
Dispatcher.BeginInvoke(...)
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.