9
votes
\$\begingroup\$

I had to find the corresponding process for a windows service the other day, and I kept on getting this one exception. I've spent some time researching the exception only to find that this exception will occur if the process is terminating, Hence my question, is it really that bad to catch expected exceptions ?

Sample Code:

try
{
  if (proc.MainModule.FileName == controller.ImagePath)
    {
             //Logic goes here
    }
 }
 catch (System.ComponentModel.Win32Exception ex)
 {
      // Thrown if process is already terminating,
      // the process is a Win16 exe or the process
      // could not be accessed.
      MyLogger.Debug(
                   "The following Win32Exception occurred while trying to access the MainModule information: " + ex.ToString());
}
\$\endgroup\$
1
  • 1
    \$\begingroup\$ and who told you it was bad to catch expected exceptions? If you didn't expect the exception, how are you supposed to catch it? \$\endgroup\$ May 16, 2011 at 20:38

4 Answers 4

13
votes
\$\begingroup\$

There are some exceptions I classify as "exogenous" exceptions. That is, they are exceptions that state "the world is not how you assumed it would be, and we can't do the thing you asked". The problem with exogenous exceptions is that the world is constantly changing. You think you're going to be able to access that file, and then someone changes its permissions from another process, or the tape drive catches on fire, or someone unplugs the router, or whatever, and oops, you can't access the file after all.

Exogenous exceptions are precisely the ones you should be catching; they indicate situations that are rare enough and dangerous enough to reasonably be exceptions, but common enough that you can have some clue ahead of time as to what they are, and reasonably recover from them.

The other class of exceptions you need to catch are the "vexing" exceptions. That is, the exceptions where someone wrote you a library that communicates with you via exceptions in non-exceptional circumstances. Ideally you should never write a library that requires the developer to catch a vexing exception; rather, expose tools that allow them to prevent the exception.

For example, suppose you write a method that turns a string into an int. You might say

public static int Parse(string s) { ... }

And if the string is malformed, it throws an exception. That is vexing! The developer might have a string where they don't know whether it is well-formed or not, because a user typed it in. The work they have to do to avoid the exception is equivalent to simply not calling the method in the first place. A better way to do it is:

public static bool IsWellFormedIntString(string s) { ... }
public static int Parse(string s) { ... }

or

public static int? Parse(string s) { ... }

or

public static bool TryParse(string s, out int result) { ... }

Or whatever; there are lots of ways to avoid the vexing exception situation.

More thoughts on this topic here:

http://blogs.msdn.com/b/ericlippert/archive/2008/09/10/vexing-exceptions.aspx

\$\endgroup\$
6
  • 2
    \$\begingroup\$ I must disagree regarding "vexing" exceptions. Why is a bool return value better then an exception? \$\endgroup\$ May 16, 2011 at 20:25
  • 1
    \$\begingroup\$ @winston ...especially considering the exception may give us a clue what the problem is where the bool tells us nothing other than that something is wrong. \$\endgroup\$
    – Icode4food
    May 16, 2011 at 20:50
  • 5
    \$\begingroup\$ @Winston: Exceptions are slow. Exceptions are noisy when you're debugging. Many high-reliability products are configured to log all exceptions and treat them as bugs. (I once added a by-design exception on a common control path in Active Server Pages and boy, did I hear about that the day afterwards, when suddenly their error logs had several hundred thousand new entries.) Exceptions are intended to represent exceptional situations, not common situations. \$\endgroup\$ May 16, 2011 at 21:18
  • 6
    \$\begingroup\$ @Winston: Even if exceptions were cheap and so on, they'd still be a lousy control flow best left to exceptional circumstances. An exception is a goto without even a label. Their power comes at an extremely high cost; it becomes impossible to understand the flow of the program using only local analysis; the whole program must be understood. This is especially true when you mix exceptions with more exotic control flows like event-driven or asynchronous programming. Avoid, avoid, avoid; use exceptions only in the most exceptional circumstances, where the benefits outweigh the costs. \$\endgroup\$ May 17, 2011 at 4:53
  • 2
    \$\begingroup\$ @Eric, I agree that exceptions can be used in a way that makes it impossible to understand the flow of the program. However, this isn't true if the "vexing" exceptions are caught as soon as they are thrown. (I.e. the function that calls ParseInt catching the ParseIntError) If the exception isn't caught that quickly its really a boneheaded error because its a bug in your program, an error happened that you thought couldn't happpen. \$\endgroup\$ May 17, 2011 at 14:08
4
votes
\$\begingroup\$

In general, if we assume that the exception is going to be thrown (i.e. there's no simple / cheap / reliable way to avoid it), then there is nothing wrong with catching it. However, the question is whether you are going to achieve much by doing so.

  • If the exception is complaining about something that the end user is likely to be able remedy, then reporting it with a helpful and informative message is a good ides.

  • If the exception is complaining about something that the end user won't understand and won't be able to remedy, then the best idea would be to let it propagate, catch it at the top level (e.g. using a root exception class), log it, and then tell the user to contact "support".

The other thing that you have to watch for is that you don't "squash" the exception. For instance, your code looks like it might be catching an exception with serious consequences, logging it as a "minor" issue (level == debug) ... and then continuing as if nothing bad had happened. This approach is a bad idea. It makes bugs harder to find than if you'd just let the exception propagate and produce an ugly stack trace ... or whatever.

\$\endgroup\$
3
votes
\$\begingroup\$

Sometimes they are hard to avoid, as in your example. I would consider it bad practice only in cases where the exception is easy to avoid (e.g. by testing the sign before calculating the square root).

\$\endgroup\$
2
votes
\$\begingroup\$

In general, only use exceptions for exceptional cases. Everything which could be written using normal conditional checks should be written as such. (Hence the name exceptions.)

In your particular scenario, you should wonder why the process is being terminated. Is there no way you could prevent the exception from being thrown? Perhaps you could solve the issue better by attaching logic to the Exited event. It all depends on what your sample code is doing.

A simplified wrong example:

int[] ints = new[] { 0, 1, 2 };
try
{
    Console.WriteLine( ints[ 10 ] );
}
catch ( IndexOutOfRangeException ) {}

Should be:

int[] ints = new[] { 0, 1, 2 };
int toDisplay = 10;
if ( toDisplay < ints.Length )
{
    Console.WriteLine( ints[ toDisplay ] );
}
\$\endgroup\$
1
  • \$\begingroup\$ "Hence the name exceptions" - with that attitude we'd never have invented rugby. \$\endgroup\$
    – ICR
    May 21, 2011 at 8:11

Not the answer you're looking for? Browse other questions tagged or ask your own question.