问题
I've found several C# application crashes in response to error conditions such as obj = null
or obj.member = null
. A lot of time, the obj from the interface of 3rdPartyApp.
And caused both 3rdPartyApp and MyCsApp crashed together.
How could I add exception handling in all possible areas so that my application can survive in these disastrous situations? It is a challenge to add try-catch to ALL places, and recover from the situation.
How could I accomplish this in a way which is realistic, reliable and bullet-proof?
[Update: Industry Automation Control]
Structure:
GUI(asp.net, c++) - RuntimeApp (C++) - MyCsApp(cs) - 3rdPartyApp(Cs)
Normal procedure:
- HostApp --(Connect through ethernet Cabele)-- MyCsApp
- Operator -- GUI -- RuntimeApp -- MyCsApp
Abnormal conditions:
- Some non-standard operation procedure;
- some hardware issue occurred;
- etc.
I'd better handle all the abnormall conditions. And most importantly, I must think how to recover from the situations.
回答1:
It would make sense to cure the disease first, find out why it's causing to crash, sure the code is crashing because of obj = null
or similar - using exception handling and swallowing all exceptions is just masking the problem....That's what it is not used for! It sounds like there's a lot of code-smells that is triggering the crashes - Protecting your application from crashing is not the right way to deal with it and only making things worse...
Ok, you can follow John Saunders's and pm100's suggestion to do that...but handle it in a manner to see what's the root cause, do not treat it as a 'magic silver bullet', at the end of the day, the code that is interacting with the third party application needs to be debugged thoroughly...
for instance
object foo = null; bar baz; // .... // foo is now set by thirdparty app if (foo != null && foo is bar) baz = (bar)foo as bar; if (baz != null){ // Continue on, baz is a legitimate instance of type 'bar' }else{ // Handle it gracefully or throw a *user defined exception* }
Notice how the 'as' is used to check if 'foo' is of the right type for 'bar' instance - now compare with this, that is a typical code smell...
object foo = null; bar baz; // foo is now set by thirdparty app - ARE YOU REALLY SURE ITS NON-NULL? // IS IT REALLY OF TYPE 'BAR'? baz = foo; // CRASH! BANG! WALLOP! KERRUNCH!
回答2:
You do not want to catch every exception everywhere.
You want to prevent exceptions from "leaking out" of the lower layers of your application up to where they can kill the application or corrupt it.
But preventing corruption is going to require more than just catching exceptions. You're going to have to make sure that the application is always safe to interrupt at every point where an exception could be thrown. This may mean that you need to clean up complicated operations. For example:
ComplexBuilder cb = new ComplexBuilder();
try
{
cb.AddOperation(...); // Once building starts,
cb.AddOperation(...); // it's not safe to use cb
cb.AddOperation(...);
}
catch (SpecificException ex)
{
cb.Cleanup(); // until it's cleaned up
}
// Now safe to access cb, whether or not an exception was thrown
I recently ran into an application with a similar attitude. There was piece of this application that was considered to be "important". When that "important" thing happened, there were other things that were supposed to happen, but which were considered "not important". The idea was that if there was an exception in the "not important" part, then it was necessary for the "important" part to continue.
What happened is that an attempt to read a resource failed for some reason. This returned null instead of the string resource. This caused an ArgumentNullException
in a String.Format
call. This caused the exception to be caught by code that just continued.
But between the first exception and the last one, an object was to have been allocated, and the reference to the object was to have been set. But because of the exception, setting the reference never happened. The result was that I saw a NullReferenceException
, four stack levels up, and two .csproj files away from where the actual problem happened.
So when you talk about catching exceptions so that your program can continue, you need to keep in mind that the control flow of your program is changed drastically by catching all these exceptions. In fact, it could be changed so much that you can no longer determine whether it's safe for your program to continue executing.
回答3:
This is something that a lot of developers don't get. By the time your exception catch-all gets hit, your application has already crashed. Something unexpected happened, which means that your code didn't anticipate it, and things are very likely to be in an indeterminate state (i.e. you can't be certain exactly how much of the offending function completed at the point the exception was generated, you don't know how much data got written out, what bits got set in the hardware, etc.). Is it safe to continue on? Should you try to save out the user's data? Who knows!
When you reach this high-level catch-all you're going to provide, you haven't prevented your app from crashing. You're just deciding what to do about the crash at that point. You can put up a different message than the standard:
This application has performed an illegal operation
...but what's your custom message going to say that's any better?
We're shutting down without warning for unscheduled maintenance, but rest assured that it had nothing to do with a flaw in this excellent software
...?
回答4:
you should certainly not add try catch everywhere
you just need a top level catch of all exceptions. If this is a GUI app then just display a nice dialog with a button saying 'please report to support' (it can write out a stack trace snapshot to the screen or a file)
if you are lucky then the app can continue (lucky since you have no way of knowing if you are really stuck badly)
note that you can also do this
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Forms.Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
but that doesnt stop it crshing, it just lets you capture the failure
回答5:
One technique to avoid such exceptions is to use the null object pattern.
So instead of having Account account = null;
you would do Account account = Account.SpecialNullAccount;
and in your Account class you would define SpecialNullAccount
as a static Account
object. Now if you try to use account.Name
in some inconsequential code (like, say logging code) you don't get an Exception, instead you get a value like, say "NO NAME" defined on the static null object instance.
Of course it's important that such an object DOES throw Exceptions in any important case like .PayInvoice() or objectContext.SaveChanges() so take steps to ensure that happens.
回答6:
AppDomain.UnHandledException and/or AppDomain.ThreadException are events you can subscribe to to catch unhandled exceptions. I don't think you can use this to continue execution where it left off (and this probably woudn't be a good idea anyway). They can be used swallow the error message or log it, etc.
Whether this is a good idea to do is up to you however!
回答7:
If it is a Winforms application add event handlers for for the following events in Main method like this
Application.ThreadException += Application_ThreadException;
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
You can then show a messagebox or other notification, loog the exeception etc and continue working without crashing the app.
来源:https://stackoverflow.com/questions/2249280/how-can-i-swallow-all-exceptions-and-protect-my-application-from-crashing