views:

96

answers:

2

For the love of all things holy, how do you distinguish between different "exception flavors" within the predefined .NET exception classes?

For example, a piece of code might throw an XmlException under the following conditions:

  • The root element of the document is NULL
  • Invalid chars are in the document
  • The document is too long

All of these are thrown as XmlException objects and all of the internal "tell me more about this exception" fields (such as Exception.HResult, Exception.Data, etc.) are usually empty or null.

That leaves Exception.Message as the only thing that allows you to distinguish among these exception types, and you can't really depend on it because, you guessed it, the Exception.Message string is glocabilized, and can change when the culture changes. At least that's my read on the documentation.

Exception.HResult and Exception.Data are widely ignored across the .NET libraries. They are the red-headed stepchildren of the world's .NET error-handling code. And even assuming they weren't, the HRESULT type is still the worst, downright nastiest error code in the history of error codes. Why we are still looking at HRESULTs in 2010 is beyond me. I mean if you're doing Interop or P/Invoke that's one thing but... HRESULTs have no place in System.Exception. HRESULTs are a wart on the proboscis of System.Exception.

But seriously, it means I have to set up a lot of detailed specific error-handling code in order to figure out the same information that should have been passed as part of the exception data. Exceptions are useless if they force you to work like this. What am I doing wrong?

EDIT. A DAY LATER.

Thanks for all the comments and responses. I'm learning a general lesson here ("error messages suck") even if I haven't quite solved the specific problem. Here's the specific scenario I'm deaing with.

Our application consumes XML files produced by a third party. These XML files sometimes contain illegal characters that really have no business being in an XML file. These illegal chars cause a (validating) XmlReader to blow up with an "illegal char on line X" exception". We have to process these files, however; we can't simply tell the user, "sorry, that file doesn't conform to the official XML spec." Our users don't even really know what XML is.

Microsoft has an official (and quite strange to me) recommendation in this case (the case where an XML document contains illegal characters): to load the file into a stream, iterate to the specific line containing the error (as provided, ironically, in the XmlException object), and to custom-replace the offending char with a legal one. Then try loading the doc into the validating XmlReader again, and seeing if it blows up. Here's the Knowledge Base article describing the technique.

So fine, we're doing that, and it works well enough. The problem is that sometimes these XML files we get are malformed in other ways: they might be empty, they might be missing a closing tag, etc. So if you follow the MS recommendation, you're actually hooking your "replace illegal chars" logic into the catch block where you catch the original exception thrown by the validating reader.

That's fine if the exception is in fact an "illegal char" exception. But if it's a "root element missing" or "missing closing tag" exception, you find that the MS technique to go in and replace the offending char itself blows up, because not only is there not an offending char, there are no chars at all, or there are, but they're invalidly-formed XML, or whatever. At this point you're in a doubly-nested catch-inside-a-catch, your hair's turning gray and falling out, your eyes are crimson red with caffeine fatigue, and you're questioning the sanity, let alone the utility, of using validating readers against real-world XML.

So what I need is a way of telling, in that initial catch(XmlException) block, whether this is a "root element missing" or a "invalid char" exception, so I can take the appropriate action. One thing we can't do is prevent our users to open a document that contains a few invalid chars. We have to process those documents regardless, and I guess it's looking like the only solution is to iterate through every char in the document ahead of time, viewing it as a text stream rather than as XML, find the illegal chars, replace them, then load the thing with the validating XmlReader and if it blows up, we know it's not an illegal char exception, because we stripped illegal chars beforehand.

Before doing that I thought I'd at least ask 1) can we get better information out of the XmlException object and I was hoping somebody would tell me 2) "it's okay to key off the XmlException.Message string. They say it's localized but it's not really. This string will be the same across all versions and cultures of Windows."

But nobody has told me that.

+6  A: 

I totally agree with you in principle, but the number of instances where I really care about the exact reason the exception was being thrown (in my code) is pretty small.

Using XmlException as an example, is there really some different behavior you would have in your code if the document was too long vs. if it had invalid characters?

The only example I can think of where I've ever really cared was for SQL type exceptions where some errors can be recovered from (e.g. a lost database connection).

ETA:

If you are worried about the culture when keying off the error message, you could just set the current thread culture to the invariant culture while you are processing the document, then set it back to the original culture when you are done. That should ensure that the message will always be the same.

Eric Petroelje
+1. when it fails, it fails. Just say to your user that is fails, and store the technicals details somewhere in a log.
Clement Herreman
@Clement: yeah ... but what is the next step? Unless your software is mass-produces for $49.99, the next step will be the client calling your support number and kicking and screaming. You do want to help out your support fellas whose job is to cover your ass, do you not?
Hamish Grubijan
@Hamish - That's why you log the details in an error log. You can give the details to the user, but they'll just click past it and ignore the message anyways.
Eric Petroelje
@Hamish : just like @Eric said. Do you know a user that actually **read** the error message ? Sadly, no. Just ask the customers to send you the log (or better, have your application sending it to you), then treat the bug, maybe with a little phone call to your customer to ask them how they encountered the bug (and to show them that you care about their happiness).
Clement Herreman
In general, I 100% agree. Who cares what the exception is, beyond a certain level of granularity. In this case we have specific illegal-chars processing logic that needs to be kicked off, and if the doc is empty, we need to display an empty document window. On the other hand if the XML is *malformed* (aside from the illegal chars), we can tell the user "sorry. bad document."
Swingline Rage
+3  A: 

The issue here seems to be the poor message you are getting from XmlException. This is an API design issue.

An option either use a different library than System.Xml such as System.Xml.Linq and using the XDocument to see if you get a better error code.

If none of those work then you can use Open source Xml Parser such as NDigester and modify the code to give a more detail error.

David Basarab
Hm I would rather use a well-tested crappy library, than community-tested good library. Well, depends on what you are crafting, I suppose. Ndigester - 0% of 2 users recommend this project ;)
Hamish Grubijan
I agree it's an API issue. I think the design is fine -- XmlException *has* these fields like HResult, Data, etc. It has the capacity to carry this information. But whoever wrote XmlReader, XmlDocument, etc., neglected to fill in those fields, so they're effectively useless. And this seems to happen across the entire .NET library. I've *never* used Exception.HResult because it almost never has a value I can use. And Exception.Data? A key-value collection just sitting there, waiting to disappoint me with its emptiness.
Swingline Rage