The .NET Framework provides a significant number of specific exception classes, all inheriting from the base Exception class. In the .NET Framework documentation, you'll find tables listing all the possible exceptions that might occur when you call any method. For example, Table 12.2, taken from the .NET Framework documentation, makes it easy to determine what might go wrong when calling the File.Open method.
Table 12.2. Exception Types That Can Be Generated When You're Attempting to Open a File
|Exception Type ||Condition |
|SecurityException ||The caller to this routine does not have sufficient permissions. |
|ArgumentException ||The path is empty, contains invalid characters, or contains only whitespace. |
|FileNotFoundException ||The file cannot be found in the location specified. |
|ArgumentNullException ||The path or mode was not passed. |
|UnauthorizedAccessException ||The path requested is marked as read-only or is only a folder name with no filename. |
|DirectoryNotFoundException ||The requested directory cannot be found. |
Your procedures can include as many Catch blocks as necessary in order for you to handle individual exceptions differently. The procedure in Listing 12.5, from the sample project, tests for several different exceptions and handles each individually.
Listing 12.5 Use Hierarchical Exceptions, Arranged in a Logical Order, to Determine Exactly Which Error Occurred
Private Sub MultipleExceptions() Dim lngSize As Long Dim s As FileStream ' Use the most specific exception that you can. ' To test this, change the file name to be: ' 1.) In a good location, but the file doesn't exist. ' 2.) On a drive that doesn't exist. ' 3.) In a path that doesn't exist. ' 4.) On a drive that isn't ready. Try s = File.Open(txtFileName.Text, FileMode.Open) lngSize = s.Length s.Close() ' The code will match against the most specific ' error it can. Both FileNotFoundException and ' DirectoryNotFoundException inherit from ' IOException, and the code will use those first. ' If they weren't here, the code would always fall ' into(IOException) for those errors. Catch exp As ArgumentException lblError.Text = _ "You specified an invalid filename. " & _ "Make sure you enter something besides spaces." Catch exp As FileNotFoundException lblError.Text = _ "The file you specified can't be found. " & _ "Please try again." Catch exp As ArgumentNullException lblError.Text = "You passed in a Null argument." Catch exp As UnauthorizedAccessException lblError.Text = _ "You specified a folder name, not a file name." Catch exp As DirectoryNotFoundException lblError.Text = _ "You specified a folder that doesn't exist " & _ "or can't be found." Catch exp As SecurityException lblError.Text = "You don't have sufficient " & _ "rights to open the selected file." Catch exp As IOException ' A generic exception handler, for any IO error ' that hasn't been caught yet. Here, it ought ' to just be that the drive isn't ready. lblError.Text = "Drive selected is not ready" & _ " Make sure the drive contains valid media." Catch exp As Exception lblError.Text = "An unknown error occurred." End Try End Sub
To test this procedure, try a number of specific exceptions. For example, change the filename to be in one of the following locations:
In a good path (but select a file that doesn't exist)
On a drive that doesn't exist
In a path that doesn't exist
On a drive that isn't ready
The Exception Hierarchy
Following any of the links in the Exceptions table shown in Table 12.2 takes you to documentation on the individual Exception object. This documentation includes the following inheritance hierarchy (you'll need to understand this hierarchy of objects when you add multiple Catch blocks):
Object Exception SystemException ArgumentException ArgumentNullException ArgumentOutOfRangeException InvalidEnumArgumentException DuplicateWaitObjectException
Because each level inherits from the class defined above it, each lower level is an instance of the type specified above it. ArgumentNullException "is a" ArgumentException, which "is a" SystemException, which "is an" Exception. Here, "is a" appears in quotes because it's a meaningful operator when you have multiple Catch blocks, those blocks match against the current exception using an "is a" rule. In other words, the order of the Catch blocks is significant, based on this "is a" relationship. When you're processing multiple Catch blocks and the runtime first finds a match where the current exception meets the "is a" rule for the exception trapped by the Catch block, the runtime uses that Catch block to process the exception and doesn't look any further. All exceptions inherit from the base Exception class, so you'll always want to include a Catch block to handle the base Exception class last, if you include it at all.