I have a scenario where a worker thread performs a task that looks like
try
{
for()
{
functionA();
functionB();
}
}
catch(Exception ex)
{
throw new Exception("Message");
}
I'm basically re-throwing the exception so that when the worker completes, the UI thread will receive the exception.
Now, if an exception occurs in functionA I'd like to throw the exception but continue the loop and not stop. How can I achieve this?
edit: If it isn't clear, this is so that the UI can be notified that an exception has occurred but not stop the worker
Something like this:
List<Exception> errorsLog = new List<Exception>();
for()
{
try
{
functionA();
}
catch(Exception e) {
errorsLog.Add(e);
}
try
{
functionB();
}
catch(Exception e) {
errorsLog.Add(e);
}
}
if (errorsLog.Count > 0) {
throw new AggregateException(errorsLog);
}
Where AggregateException is your custom exception that contains a list of other exceptions.
Related
We're having a winforms application that uses an async initialization process. Simplified you can say that the application will run the following steps:
Init - this runs async
Show MainForm
Application.Run()
The currently existing and working code looks like this:
[STAThread]
private static void Main()
{
SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
var task = StartUp();
HandleException(task);
Application.Run();
}
private static async Task StartUp()
{
await InitAsync();
var frm = new Form();
frm.Closed += (_, __) => Application.ExitThread();
frm.Show();
}
private static async Task InitAsync()
{
// the real content doesn't matter
await Task.Delay(1000);
}
private static async void HandleException(Task task)
{
try
{
await Task.Yield();
await task;
}
catch (Exception e)
{
Console.WriteLine(e);
Application.ExitThread();
}
}
The background how this is working is described very detailed by Mark Sowul here.
Since C# 7.1 we're able to use async Task in main method. We tried it in a straight forward way:
[STAThread]
private static async Task Main()
{
SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext());
try
{
await StartUp();
Application.Run();
}
catch (Exception e)
{
Console.WriteLine(e);
Application.ExitThread();
}
}
private static async Task StartUp()
{
await InitAsync();
var frm = new Form();
frm.Closed += (_, __) => Application.ExitThread();
frm.Show();
}
private static async Task InitAsync()
{
// the real content doesn't matter
await Task.Delay(1000);
}
But that doesn't work. The reason is clear. All the code after the first await will be forwarded to the message loop. But the message loop hasn't startet yet because the code that starts it (Application.Run()) is located after the first await.
Removing the synchronization context will fix the problem but causes to run the code after await in a different thread.
Reordering the code to call Application.Run() before the first await will not work because it is a blocking call.
We try to use the new feature of having an async Task Main() that allows us to remove the HandleException-solution that is hard to understand. But we don't know how.
Do you have any suggestions?
You don't need async Main. Here is how it can possibly be done:
[STAThread]
static void Main()
{
void threadExceptionHandler(object s, System.Threading.ThreadExceptionEventArgs e)
{
Console.WriteLine(e);
Application.ExitThread();
}
async void startupHandler(object s, EventArgs e)
{
// WindowsFormsSynchronizationContext is already set here
Application.Idle -= startupHandler;
try
{
await StartUp();
}
catch (Exception)
{
// handle if desired, otherwise threadExceptionHandler will handle it
throw;
}
};
Application.ThreadException += threadExceptionHandler;
Application.Idle += startupHandler;
try
{
Application.Run();
}
catch (Exception e)
{
Console.WriteLine(e);
}
finally
{
Application.Idle -= startupHandler;
Application.ThreadException -= threadExceptionHandler;
}
}
Note, if you don't register threadExceptionHandler event handler and StartUp throws (or anything else on the message loop throws, for the matter), it will still work. The exception will be caught inside the try/catch which wraps Application.Run. It will just be a TargetInvocationException exception with the original exception available via its InnerException property.
Updated to address the comments:
But for me it looks very strange to register an EventHandler to the
idle event so startup the whole application. It's totally clear how
that works but still strange. In that case I prefer the
HandleException solution that I already have.
I guess it's a matter of taste. I don't know why WinForms API designers didn't provide something like WPF's Application.Startup. However, in the lack of a dedicated event for this on WinForm's Application class, deferring specific initialization code upon the first Idle event is IMO an elegant solution, and it's widely used here on SO.
I particularly don't like the explicit manual provisioning of WindowsFormsSynchronizationContext before Application.Run has started, but if you want an alternative solution, here you go:
[STAThread]
static void Main()
{
async void startupHandler(object s)
{
try
{
await StartUp();
}
catch (Exception ex)
{
// handle here if desired,
// otherwise it be asynchronously propogated to
// the try/catch wrapping Application.Run
throw;
}
};
// don't dispatch exceptions to Application.ThreadException
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
using (var ctx = new WindowsFormsSynchronizationContext())
{
System.Threading.SynchronizationContext.SetSynchronizationContext(ctx);
try
{
ctx.Post(startupHandler, null);
Application.Run();
}
catch (Exception e)
{
Console.WriteLine(e);
}
finally
{
System.Threading.SynchronizationContext.SetSynchronizationContext(null);
}
}
}
IMO, either approach is more clean than the one used in your question. On a side note, you should be using ApplicationContext to handle the form closure. You can pass an instance of ApplicationContext to Application.Run.
The only point that I'm
missing is your hint that the synchronization context is already set.
Yes it is - but why?
It is indeed set as a part of Application.Run, if not already present on the current thread. If you like to learn more details, you could investigate it in .NET Reference Source.
I always encounter this exception during my batch run.
I've already catch the InterruptedException then call the Thread.currentThread.interrupt().
How can I get rid of the sleep interrupted?
I encountered randomly in WebDriverWait. I don't know why it happens.
public boolean isElementVisible(WebElement webElement) {
boolean isVisible = false;
try {
log.info(CHECK_IF_ELEMENT_IS_VISIBLE);
WebDriverWait wait = new WebDriverWait(driver,
CommonConstants.DEFAULT_TIMEOUT_IMPLICITWAIT);
Wait.until(ExpectedConditions.visibilityOf(webElement));
isVisible = true;
} catch (Exception e) {
log.error(ELEMENT_NOT_FOUND, e);
}
return isVisible;
}
I have a wpf c# app.
I normally use a global error handler to catch all errors:
private void Application_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
try
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => Xceed.Wpf.Toolkit.MessageBox.Show(e.Exception.ToString(), "Error",
MessageBoxButton.OK, MessageBoxImage.Error)));
e.Handled = true;
InformedWorkerDataService.Common.Shared.RecordMessage(e.Exception.ToString(), true);
}
finally { }
}
However, if start off a task.run 'bit of code' and it throws an error then i have observed that error is not caught:
Task.Run(() =>
{
throw and error here
});
so I have to put a 'Try-Catch' thing in to capture it:
Task.Run(() =>
{
try
{
throw an error here
}
catch (Exception ex)
{
do something with error
}
});
~ which defeats the object of having a Global Error handler
But, if i use this approach:
TaskScheduler.UnobservedTaskException += (s, e) => {
e.Exception //The Exception that went unobserved.
e.SetObserved(); //Marks the Exception as "observed," thus preventing it from triggering exception escalation policy which, by default, terminates the process.
};
... it will do my global exception handling but if I want to notify the user of the error real-time it does not do so very well because IT IS on a separate thread.
What would be a good compromise?
Unfortunately the TaskScheduler.UnobservedTaskException is not guarenteed to fire in real time with the exception being thrown. This means that using this handler for user notification can be quite confusing as the user action and error notification will not happen synchronously. For user driven handling of 'unexpected' task exceptions, you could create helper methods as below and use TaskEx.Run instead of Task.Run:
public static class TaskEx
{
public static Task Run(Action function)
{
return Task.Run(() =>
{
try
{
function();
}
catch (Exception ex)
{
TraceEx.TraceException(ex);
//Dispatch your MessageBox etc.
}
});
}
}
Obviously this is not as simple as adding a global handler (which should still be done for tracing purposes) but is simple enough to implement in UI driven code.
I am using Threadpool.QueueUserWorkItem like following
public void TestMain()
{
try
{
ThreadPool.QueueUserWorkItem(x =>
{
this.Dispatcher.BeginInvoke(new Action(() => this.BackGroundMethod()));
}
);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void BackGroundMethod()
{
try
{
int a = 0;
int b = 100;
var error = b / a;
}
catch(Exception)
{
throw;
}
}
By this way, TestMain() can not catch exception.
Program will shut down...
How can i catch this error?
Thx.
The reason is this.Dispatcher.BeginInvoke(new Action(() => this.BackGroundMethod)); executes asynchonously, so it will finished execute all code inside TestMain before BackGroundMethod is executed.
Use Dispatcher.UnhandledException event to catch the exception in the TestMain() method like this:
Dispatcher.UnhandledException += (sender, args) => MessageBox.Show(args.Exception.Message);
ThreadPool.QueueUserWorkItem(ignore => Dispatcher.Invoke(new Action(BackGroundMethod)));
Edit: Remember to set the Handled property to prevent the internal exception handler from being called:
Dispatcher.UnhandledException += (sender, args) =>
{
MessageBox.Show(args.Exception.Message);
args.Handled = true;
}
To prevent the crash, add a try/catch around the operation which might throw:
ThreadPool.QueueUserWorkItem(x =>
{
try {
this.Dispatcher.BeginInvoke(new Action(() => this.BackGroundMethod()));
}
catch (DivideByZeroException ex) {
// handle the error somehow
}
});
However, think about what you are doing here: you are pushing some action to the thread pool, and in turn it pushes the same action to the dispatcher thread. Why not do this yourself directly without the QueueUserWorkItem call?
You can catch all exceptions in all threads within the application by handling AppDomain.UnhandledException
When you use the Dispatcher to create a new thread, that thread has it's own stack, and so the exceptions wont bubble to the try...catch in TestMain, but instead they will originate in BackgroundMethod method. As you are throwing the exception in BackgroundMethod your try catch is useless, and so if you were to not throw the exception in BackgroundMethod your program wouldn't shut down.
private void BackGroundMethod()
{
try
{
int a = 0;
int b = 100;
var error = b / a;
}
catch(Exception)
{
MessageBox.Show(ex.Message);
}
}
You can do this by catching the exception in the BeginInvoke function and saving it in a temporary variable. After the await you can then rethrow it on the correct thread.
Exception thrown = null;
await Application.Current.Dispatcher.BeginInvoke(new Action<Action>(x =>
{
try
{
BackGroundMethod();
}
catch (Exception ex)
{
//exceptions on this thread MUST be caught or the program will exit
//we will save it, and then when we are back on the main thread we will rethrow it
thrown = ex;
}
}));
if (thrown != null)
throw thrown; //<--- I'm rethrowing it right here on the correct thread
I have searched for the solution but couldn't make it work. Here is the summary. I am trying to implement a webservice which runs on Glassfish 2.1 that implements a synchronous JMS Request-Response using Temporary queue. It sends a message to another Glassfish application running on remote server. I am able to send the message and process it but when the final message is sent back to temporary queue, the webservice gets the response as null. Here is the code:
private Message requestReply(String msg, Queue jmsRequestResponse, ConnectionFactory jmsRequestRespConnFactory) {
javax.jms.Connection conn = null;
javax.jms.MessageConsumer consumer = null;
javax.jms.Message replyMsg = null;
javax.jms.Session sess = null;
try {
logger.debug("[requestreply input message[" + msg);
conn = jmsRequestRespConnFactory.createConnection();
conn.start();
sess = conn.createSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);
javax.jms.Message reqMessage = sess.createTextMessage(msg);
javax.jms.Destination replyDestination = (jmsRequestResponse instanceof javax.jms.Queue) ? sess.createTemporaryQueue() : sess.createTemporaryTopic();
reqMessage.setJMSReplyTo(replyDestination);
sess.createProducer(jmsRequestResponse).send(reqMessage);
replyMsg = consumer.receive(60000);
consumer.close();
sess.close();
conn.close();
} catch (JMSException ex) {
logger.debug("exception in requestreply");
} finally {
if (consumer != null) {
try {
consumer.close();
} catch (Exception e) {
}
}
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
}
}
}
return replyMsg;
}
what am I missing here?? When I print the replyMsg, it is always null.