I am currently asking myself some questions about exception handling and eventhandlers, and i hope some of you will give me some help.
I will start to explain what i would like to achieve in my c# application:
I have a top-level method (lets call it the main method). This method calls an asynchronous method (wich is called connect), which connect to a FTP server.
An EventHandler object is associated to this connection, and a "callback" method is called when the connection is successful.
I want to handle exceptions that can be launched during the whole process. So i would like to catch it in the top level method. It works fine for exceptions launched by the connect method (which is called inside the top level method).
However, it does not work for exceptions called inside the "callback" method: the top level method does not catch them and the execution fails.
What can I do to make these exceptions beeing caught by the top level method ? I don't want to handle these exceptions in the callback.
Take a look at how the Backgroundworker deals with this: the Exception is propagated to the Completed event handler.
I assume you have some form of State object that is passed to/from the delegate, that's where you can add such a property. And you will have to catch all exceptions in the thread, at the outermost scope. But 'handling' just means passing it along.
There is a standard pattern for the RunWorkerCompleted event, see this MSDN page.
Consider the below code fragment for wrapping all of your code in a global exception handler:
namespace MyClient
{
class Program
{
static void Main(string[] args)
{
try
{
bool isSuccess = SubMain(string[] args);
}
catch (Exception e)
{
HandleExceptionGracefully(e);
}
}
static bool SubMain(string[] agrs)
{
// Do something
}
static void HandleExceptionGracefully(Exception e)
{
// Display/Send the exception in a graceful manner to the user/admin.
}
}
}
Also, don't forget to make your error handling user-friendly.
There is an event handler in the Application class called ThreadException. This event will be fired whenever an exception is thrown an not caught anywhere in the current call stack.
Edited:
Sorry, I misread the question - I didn't realise that the "main" method in your example isn't the actual main method. In that case you may want to catch the exception inside the callback, but not handle it - instead simply pass it back up to the main method as part of the event args.
The BackgroundWorker in Winforms does something similar.
Thanks for your answers.
It seems that using the BackgroundWorker solve this problem.
I did not try it, because i chose to avoid this implementation burden. So I took away my asynchronous call and made my application behaving synchronously.
One tip for people using the Compact Framework instead of the full .NET Framework:
the BackgroundWorker is not available in CF, but a similar solution is provided by OpenNETCF (see the BackgroundWorker class in the Smart Device Framework).
A more convenient way to deal with this problem of top-level exception handling is to use delegates.
These c# delegates allow to call methods in a asynchronous way. And delegates allow also top-level exception handling. Indeed, exceptions thrown inside delegates are re-thrown on the original thread.
I don't know why i did not think about delegates before.
I hope it will help.
See these nice articles about delegates:
Article 1
Article 2
Related
I use the PubSub mechanism the send events in my application,
each event has a custom handler that use to handle it.
My problem is that in many places the handler suppled as the following:
SubscriberService.Subscribe(new SubscribeRequest<string>
{
Topic = TopicName,
Action = async pubSubEvent => await DoSomthingAsync()
}
the return type of Action is void so behind the scenes this lambda translated to async void.
So when an exception occur I have no chance to catch it.
I wondered to myself if there is a way to create a method at runtime that warp the method body of the action with a try-catch block that catches the exception when they occur and write them to the log.
I saw some articles about the Emit.IlGenerator class could help but I have no idea where to start.
I would love to hear any suggestions to deal with these issues.
Thanks!
I was working on a larger project which uses Rx extensively. In one particular instance I noticed one of the subscriptions threw an exception. At that point I assumed the subscription would just be completed (with an error) as I did not have any Retry() call. However, what I saw was the subscription repeatedly retried.
I tried to repro a similar case in a small example, seen below. I put a break point on
Console.WriteLine("!");
and expected it to be hit after the subscription failed. But it is never reached. GetImportantValues().Subscribe is just called over and over.
I don't understand why though. I would have expected the exception that is thrown to kill the subscribe attempt.
I would like to modify the below sample to retry 3 times if an exception is thrown, and then after that just stop altogether.
using System;
using System.Reactive.Linq;
namespace RxTest
{
class Program
{
static void Main(string[] args)
{
var ob1 = GetImportantValues().Subscribe(Console.WriteLine);
Console.WriteLine("!");
Console.ReadLine();
}
private static IObservable<int> GetImportantValues()
{
var obs = GetThem();
return obs;
}
private static IObservable<int> GetThem()
{
//Do some work. Would return a valid observable if everything is ok
return Observable.Throw<int>(new Exception("test"));
}
}
}
It's not being called over and over again. You probably just have Break on first chance exceptions enabled in Visual Studio and each time you try to continue it's just breaking on the same exception. If you were to actually continue the process the application would just crash.
Observable.Throw calls OnError. You haven't provided an OnError handler in your call to Subscribe, so the default OnError handler is used. The default behavior simply throws the error and brings down the process.
To retry simply apply Retry(3) before calling Subscribe. To swallow the error after the 3rd attempt, provide an OnError handler to Subscribe, though I don't necessarily recommend the latter. Think carefully whether the application can really recover from such a scenario.
The following code is pretty self-explanatory and my question is very simple :
Why is AsyncCallback method "HandleConnect" not propagating exception to the "Connect" method and how to propagate it ?
public void Connect(IPEndPoint endpoint, IVfxIpcSession session)
{
try
{
ipcState.IpcSocket.BeginConnect(ipcState.IpcEndpoint, HandleConnect, ipcState);
}
catch(Exception x)
{
ManageException(x.ToString()); //Never Caught, though "HandleConnect" blows a SocketException
}
}
private void HandleConnect(IAsyncResult ar)
{
// SocketException blows here, no propagation to method above is made.
// Initially there was a try/catch block that hided it and this is NOT GOOD AT ALL
// as I NEED TO KNOW when something goes wrong here.
ipcState.IpcSocket.EndConnect(ar);
}
1 - I guess this is pretty normal behavior. But I would appreciate a comprehensive explanation of why is this happening this way and what happens exactly behind the hoods.
2 - Is there any (quick and simple) way to propagate the exception through my app ?
forewarning I know many dudes in here are very critical and I anticipate the comment "Why don't you put the ManageException directly in the "HandleConnect" Method. Well, to make a long story short, let's just say "I got my reasons" lol. I just posted a code sample here and I want to propagate this exception way further than that and do much more stuff than showed in other places in the "N-upper" code.
EDIT
As an aswer to a comment, I also tried this previously indeed, with no luck :
private void HandleConnect(IAsyncResult ar)
{
try
{
ipcState.IpcSocket.EndConnect(ar);
}
catch(Exception x)
{
throw x; // Exception Blows here. It is NOT propagated.
}
}
My Solution :
I ended up putting an Event Handler to whom every concerned code logic subscribes.
This way the exception is not just swallowed down nor just blows, but a notification is broadcasted.
public event EventHandler<MyEventArgs> EventDispatch;
private void HandleConnect(IAsyncResult ar)
{
try
{
ipcState.IpcSocket.EndConnect(ar);
}
catch(Exception x)
{
if (EventDispatch!= null)
{
EventDispatch(this, args);
}
}
}
//Priorly, I push subscriptions like that :
tcpConnector.EventDispatch += tcpConnector_EventDispatch;
public void tcpConnector_EventDispatch(object sender, VfxTcpConnectorEventArgs args)
{
//Notify third parties, manage exception, etc.
}
This is a little bit crooked, but it works fine
When you use BeginConnect the connection is done asynchronously. You get the following chain of events:
Connect "posts" a request to connect through BeginConnect.
Connect method returns.
The connection is done in the background.
HandleConnect is called by the framework with the result of the connect.
When you reach step number 4, Connect has already returned so that try-catch block isn't active any more. This is the behavior you get when using asynchronous implementations.
The only reason you would have an exception caught in Connect is if BeginConnect fails to initiate the background connection task. This could e.g. be if BeginConnect validates the supplied arguments before initiating the background operation and throws an exception if they are not correct.
You can use the AppDomain.UnhandledException event to catch any unhandled exceptions in a central place. Once the exception reaches that level any form of recovery is probably hard to achieve, since the exception could be from anywhere. If you have a recovery method - catch the exception as close to the origin as possible. If you only log/inform the user - catching centrally in once place is often better.
One option is to use AsyncWaitHandle with your existing code.
For better exception handling, you would have to either use event based programming model or modify your code to use BackgroundWorker component which supports reporting error from the worker thread to the main thread.
There are some discussions and articles present on this topic at following links:
http://openmymind.net/2011/7/14/Error-Handling-In-Asynchronous-Code-With-Callbacks/
MSDN Sample: http://msdn.microsoft.com/en-us/library/ms228978.aspx
Further to what Anders has pointed out, it is probably a good idea to read this:
http://msdn.microsoft.com/en-us/library/2e08f6yc.aspx
and look into how you can pass a callback method into the asynchronous call to BeginConnect (if one does exist) using something like an AsyncCallback where you can retrieve the delegate and call EndInvoke within a try catch block.
E.g.:
public void
CallbackMethod
(IAsyncResult AR)
{
// Retrieve the delegate
MyDelegate ThisDelegate =
(MyDelegate)AR.AsyncState;
try
{
Int32 Ret = ThisDelegate.EndInvoke(AR);
} // End try
catch (Exception Ex)
{
ReportException(Ex);
} // End try/catch
} // End CallbackMethod
I know I can use something like MessageBox.Show("some error") but I'm talking about an error that occurs at some lower level in my code that has no business tossing up a MessageBox or any other GUI elements.
I'm building an RSS Client and I have a class which manages the various feeds (lets say FeedManager) which is just a wrapper for a list at this point. Now this class calls on another class for data access. So anytime someone in the GUI wants to save the feeds, the GUI simply calls FeedManager.SaveFeeds() which processes the feeds and saves them to a file, database, etc. So I attempt to save to a file and something bad happens (either something I coded for or an exception). So now I'm at least 3 levels deep, GUI -> FeedManager -> SomeDataAccessLayer, and I want to display a message to the user like "Hey, that file was not found" or "You don't have permission to write to that location", etc...
How should I go about this? Throwing up a MessageBox from the data access layer blatantly couples that component to the GUI. Having all methods return strings with any error messages seems silly as well.
Non-GUI code should indeed not show a MessageBox.
The standard approach is to throw an Exception.
Your GUI should surround the call to SaveFiles() with a try/catch block and take the appropriate action, like showing a Messagebox.
Maybe you overlooked the point is that this is exactly what Exceptions are for: to communicate errors over (multiple) method calls.
Perhaps you could create a new class that handles errors, and that class will (depending on your wishes) print it to the console, display it in e.g. a public static GUI component, etc. That way you could decouple it from the GUI in an easy way, but still show messages in it
You should throw exception instead of message box
My solution for this was to use an event with a string parameter and whenever an exception (or any other error) occurred I triggered the event and past to it the message.
In the GUI class that create the instance just need to register to that event and in case it was triggered pop up a message box (or any other info).
namespace XXX {
public delegate void Error(string a_sErrorMessage);
public class XXX {
public event Error OnError;
public void Test() {
try {
// Do something
} catch (Exception ex) {
// Trigger the event
OnError(ex.Message);
}
}
}
}
I read a lot about how bad catching base Exceptions is and I have to confess that I did it also:
try{
...
}
catch (Exception exception){
MessageBox.Show(exception.Message, "Error!");
MyLogger.Log(exception.Message);
}
Now I would like to do it right and have some questions about it:
Which exceptions should I catch (for example FileNotExists for file manipulation, but what for TableAdapter or ReportClass (CrystalReports))
Where can I see a list of exceptions, that an objects can throw (for example TableAdapter)
Where in Windows Forms Application can I set a static method, which will log any exception to a file for example
Any other suggestions?
Catch whichever exceptions you can reasonably handle. For example, if you're trying to open a file for writing, you should expect that maybe the file is marked read-only, so that would throw an exception. But in the same situation you wouldn't try to catch a null argument exception, because that would be due to programmer error.
They should be found in the function reference in MSDN (you'll have to look it up on each one). For user-defined functions, you'll have to go digging, unless there is additional documentation or summary commentary.
3, 4. Consider using a logging library for .NET
I have one thing to add. If you just want to log an exception without affecting program flow you can always do this:
try
{
...
}
catch (Exception exception)
{
MyLogger.Log(exception.Message);
throw;
}
That's up to you to decide which exceptions your application logic can reasonably expect to recover from.
Exceptions are thrown by method invocations, not objects. In Visual Studio, Intellisense explanations will tell you which exceptions are thrown by an object (provided that the XML documentation describes which exceptions a method throws.
Rather than use a static method, respond to the Application.ThreadException event. The link provided has examples.
MSDN
You can set an event for unhandled exceptions in application events file
(got a VB sample here but i hope you get the point)
Private Sub MyApplication_UnhandledException(ByVal sender As Object, ByVal e As Microsoft.VisualBasic.ApplicationServices.UnhandledExceptionEventArgs) Handles Me.UnhandledException
End Sub
You can find the application events in the options of you project.
You should only catch exceptions you can do something about, really.
That's the rule of thumb. I typically have a try/catch around my Program.Main just in case an exception bubbles right to the top and needs logging. You can also handle the CurrentDomain_UnhandledException event, in case exceptions are thrown in other threads than the UI thread (assuming you are multithreading).
In response to "4. Any other suggestions?":
In your example code, a message box is displayed before logging the exception. I would recommend logging the exception before displaying the message, just in case the user sees the error message, panics, and goes on vacation without clicking "OK". It's a minor thing, but message boxes block the program indefinitely and should be used with discretion!