Learn VB .NET Through Game Programming [Electronic resources]

Matthew Tagliaferri

نسخه متنی -صفحه : 106/ 66
نمايش فراداده

Chapter 9: Learning Other Object-Oriented Programming Topics

This Chapter Covers a Few important Visual Basic .NET topics that don’t merit an entire chapter on their own but that represent some new features of the language and the underlying framework.

Understanding Structured Exception Handling

The sample projects that accompany this book contain numerous examples of structured exception handlers. This programming construct takes the form shown in Listing 9-1.

Listing 9.1: Exception Handling Syntax

Try 
<code block> 
[Catch [e as Exception]] 
<exception handling block> 
[Catch [e as Exception]] 
<exception handling block> 
[Finally] 
<always-execute block> 
End Try 

An exception handler begins with a Try statement, followed by any number of statements. There may or may not be a Catch block. The Catch block, which may define a class of exception that it handles, runs if a runtime exception matching that class occurs in the previous Try block. The Finally block (also optional) is guaranteed to always run, regardless of any exceptions thrown or handled previously.

One caveat on the optional portions of an exception handler: Both the Catch clause and the Finally clause are optional, but the Try block must contain one or the other. For example, the following code block isn’t legal:

Try 
<code block> 
End Try 

Not only is the previous code block illegal, it’s also useless because it doesn’t handle any exceptions that might occur within the code.

Exception handlers require a new way of error handling. The old-school Visual Basic programmer used to conceive of every possible error while developing the program and build tests into the code to handle every type of problem. Consider a program that writes a complicated data structure to disk (say a CAD program or a Doom level editor). Suppose that the code that writes the complicated data structure is several thousand lines of code. The process would be as follows: Open the file; write the header; write structure A; write substructure A1, A2, and A3; write structure B; write substructure A, B, and C; and so on. Now, consider all of the errors that might occur as this code runs:

Permission errors: It’s possible that the user would try to write a file to aread-only location, such as to a CD-ROM or an area of the network to which the user doesn’t have write access. Or, the user might attempt to overwrite a file that’s marked as read-only.

Media errors: Perhaps there’s a bad cluster on the destination media, and the write operation fails at some point.

Out of disk space errors: The media may run out of disk space as the file is being written.

These are only a few of the problems that might occur while writing. So, how might the Visual Basic programmer handle these problems? The first problem, checking for write access on the destination drive, isn’t too big of a problem. The program can write the header or a short portion of the file, check for any return codes or problems that might have occurred, and then stop the write operation at that point and exit the program cleanly.

The second and third problems are much more difficult to handle cleanly, however, because they could happen at any time during the data save operation. The save could get 5 percent through—or 50 percent or 95 percent—and then afailure could occur. Should the program be written in such a way that it checks after every write to disk that the write was successful? For example:

Write to disk 
If WriteFailed then PrintErrorMessage, exit. 
Write to disk 
If WriteFailed then PrintErrorMessage, exit. 
Write to disk 
If WriteFailed then PrintErrorMessage, exit. 

Ugh. Surely there’s a better way. The most common way Visual Basic 6 developers handled errors such as running out of disk space or write errors was to not handle them at all and hope they never occurred.

One of the primary powers of structured exception handling is that the program doesn’t have to check for errors after every line. Instead, the program designates that entire block of code should run, and if an exception happens on any of those lines, then execution jumps to the exception handler. Without focusing on exact .NET class names or file Input/Output (I/O) syntax, Listing 9-2 shows how you might write the complex data saver.

Listing 9.2: Pseudocode for the Complex Data Writing Routine

bError = false 
Try 
Open File for Write 
Catch PermissionException 
Print "Permission Error" 
bError = true 
Catch AnyOtherException 
Print "Some other Error" 
bError = true 
End Try 
If bError then exit sub 
Try 
Write Some Stuff 
Write Some More Stuff 
Write Still More Stuff 
Write An Incredible Amount Of Stuff 
Catch MediaFailException 
Print "Bad Media" 
Catch OutOfSpaceException 
Print "Media Out Of Space" 
Catch AnyOtherException 
Print "Some other Error" 
Finally 
Close File 
End Try 

This code is about a zillion times cleaner because it handles the three potential errors identified earlier, and it does so regardless of how much more code you might add to the writing block. You can identify two exception handlers in this code. The first makes sure the file can be opened for writing. If a permission exception occurs, the user is told as such through some mechanism (identified only as Print in the pseudocode), and the routine exits. For even more robust error handling, this code includes a second exception handler that will catch all exceptions except the permission exceptions already caught by the previous block.

The second exception handler looks for the specific errors discussed earlier, plus it includes the catchall for anything you haven’t thought of to this point. It also contains a Finally block that makes 100-percent sure the file closes, no matter what errors may have occurred earlier.

I became grateful for one particular exception handler during the development of this book: the exception handler in the DirectDraw code for the dxSprite class discussed in Chapter 8, “Using DirectX.” By default, a DirectDraw program with an unhandled exception locks up the machine to a degree because the DirectDraw window doesn’t disappear to make way for Visual Studio to display any error that has occurred. Because of this, the best way to debug the drawing code is to place the DirectDraw code inside an exception handler and write any error that occurred to the Debug window. Listing 9-3 shows this exception handler.

Listing 9.3: Exception Handling in a DirectDraw World

Try 
FWBB = Me.BoundingBox        'start w/ normal bbox 
'start at the location 
oPt = New Point(System.Math.Floor(Location.X), _ 
System.Math.Floor(Location.Y)) 
If oPt.X < 0 Then 
'draw partial on left side 
oRect = New Rectangle(oRect.Left - oPt.X, oRect.Top, _ 
oRect.Width + oPt.X, oRect.Height) 
If oPt.X + FWBB.Left < 0 Then 
FWBB = New Rectangle(0, FWBB.Top, _ 
FWBB.Width + (oPt.X + FWBB.Left), FWBB.Height) 
Else 
FWBB = New Rectangle(FWBB.Left + oPt.X, FWBB.Top, _ 
FWBB.Width, FWBB.Height) 
End If 
oPt.X = 0 
End If 
<lots of range checking code removed> 
'should never happen, just in case 
If oRect.Width <= 0 Or oRect.Height <= 0 Then Return 
'offset the bounding box by the world coordinates 
FWBB.Offset(oPt.X, oPt.Y) 
'draw the sprite 
oSurf.DrawFast(oPt.X, oPt.Y, oSource, oRect, _ 
DrawFastFlags.DoNotWait Or DrawFastFlags.SourceColorKey) 
'draw the bounding box 
If Me.pShowBoundingBox Then 
oSurf.ForeColor = Color.Red 
oSurf.DrawBox(FWBB.Left, FWBB.Top, FWBB.Right, FWBB.Bottom) 
End If 
Catch oEx As Exception 
Debug.WriteLine("--------------------------------------") 
Debug.WriteLine(oEx.Message) 
End Try