Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Schongar P.VBScript unleashed.1997

.pdf
Скачиваний:
45
Добавлен:
23.08.2013
Размер:
1.59 Mб
Скачать

If Err.Number <> 0 Then Msgbox "An error is present prior"

' Return total seconds value

ConvertStringtoTotalSeconds = CInt(vHours) + _

CInt(vMinutes) + CInt(vSeconds)

If Err.Number <> 0 Then Msgbox "An error is present here"

An informative tracing message is generated when this script is run, as shown in Figure 12.6.

Figure 12.6 : Output from the message box trace.

Now to trace your program, you no longer have to click on trace message boxes when everything is going okay. If you see a message box come up, you know it's coming from an area of your code that detected an error.

NOTE

The modified Pace-Pal program with the change shown here is located on the accompanying CD-ROM in the file Ppalerr3.htm.

Saturating Your Code with the Message Box

Using a single pair of message box trace statements might be sufficient if you have a pretty good idea where your problem is. But if you have a really tough problem and want to make sure to cover all the bases, it might be just as easy to insert a trace after each and every statement. That way, you virtually guarantee that you will pinpoint the exact statement that causes the problem.

When you take this approach, remember to clearly and uniquely identify each trace statement through the message box text. It does little good to add 200 trace statements to a program if they all just say Error has been detected!. If you run the program and only Error has been detected! pops up on your screen, you have no idea which statement the message originated from!

The more descriptive the trace messages, the better. If you have messages spread across more than one procedure, it is helpful to identify the procedure name in the message. The idea is that when you see the message, it quickly leads you to the corresponding location in the program. Listing 12.6 shows the Pace-Pal program with extensive trace messages added. Each is uniquely identified within the message text.

NOTE

The modified Pace-Pal program with the change shown here is located on the accompanying CD-ROM in the file Ppalerr4.htm.

Listing 12.6. Tracing the flow with many message box statements.

' . . . SAME CODE UP TO THIS POINT AS SHOWN IN PREVIOUS LISTINGS

vMinutes = vHours

If Err.Number <> 0 Then Msgbox "Error occurred prior to Point

A!"

vSeconds = sDuration

If Err.Number <> 0 Then Msgbox_"Error occurred prior to Point

B!"

vHours = 0

If Err.Number <> 0 Then Msgbox "Error occurred prior to Point

C!"

Else ' Time info must be in hh:mm:ss format

vMinutes = Left(sDuration, iPosition - 1)

If Err.Number <> 0 Then Msgbox "Error occurred prior to Point

D!"

vSeconds = Right(sDuration, Len(sDuration) - iPosition)

If Err.Number <> 0 Then Msgbox "Error occurred prior to Point

E!"

End If

End If

'Represent all components in terms of seconds

vHours = vHours * 3600

If Err.Number <> 0 Then Msgbox "Error occurred prior to Point F!"

vMinutes = vMinutes * 60

If Err.Number <> 0 Then Msgbox "Error occurred prior to Point G!"

'Return total seconds value

ConvertStringtoTotalSeconds = CInt(vHours) + CInt(vMinutes) + CInt

(vSeconds)

If Err.Number <> 0 Then Msgbox "Error occurred prior to Point H!"

If Err.Number <> 0 Then

Msgbox "Error #:" & Err.Number & " Description:" & Err.

Description _

& " Source:" & Err.Source, 0, "Error in

ConvertStringtoTotalSeconds!"

End If

End Function ' ConvertStringtoTotalSeconds

The message resulting from this modified code runs with the same program input presented earlier is shown in Figure 12.7.

Figure 12.7 : Output from tracing with sequential message boxes.

From reading this trace message and looking at Listing 12.6, it should be clear exactly which statement caused the problem: the ConvertStringtoTotalSeconds = statement. If you had inserted just one trace statement at a time, it might have taken many debug iterations to come to this conclusion. Although you spend more time editing the code when you insert multiple trace statements, you can hone in on the specific problem statement after many fewer iterations.

Watching Your Code Using Variables and the Message Box

So you've located the statement that is causing the problem, but you don't know why the error is occurring or how to fix it. That often takes further debugging; it typically requires a look at the contents of the variables if those variables are involved in the rogue statement. One way to get this information is to print the contents of the variables and the variable subtypes right before the problem statement. This technique is applied to the Pace-Pal program in List-ing 12.7.

Listing 12.7. Tracing variable contents with a message box statement.

' Return total seconds value

MsgBox "vHours = " & vHours & " with type = " _

&vartype(vHours) & _

"vMinutes = " & vMinutes & " with type = " _

& vartype(vMinutes) & _

"vSeconds = " & vSeconds & " with type = " _

& vartype(vSeconds),0, "Var Dump"

ConvertStringtoTotalSeconds = CInt(vHours) + _

CInt(vMinutes) + CInt(vSeconds)

If Err <> 0 Then

Msgbox "Error #:" & Err.Number & " Description:" & Err.

Description _

& " Source:" & Err.Source, 0, "Error in

ConvertStringtoTotalSeconds!"

End If

End Function ' ConvertStringtoTotalSeconds

Figure 12.8 shows the results of this variable trace.

Figure 12.8 : Output from the message box variable trace.

A full description of the variables is now available. One variable is empty, another has an integer value, and another contains string data. Even after viewing this information, you might be confused over exactly what causes the error. You can take yet one more step to shed light on the problem.

Breaking Apart Complex Statements to Find Bugs

The problem now has been isolated to one statement, and the values and subtypes of the variables prior to that statement are known. In the problem statement, the CInt (convert to an integer) function is applied to both a variable that is empty and to a variable that contains a string. How can you see which of these is the problem conversion, or whether both are to blame? The problem statement consists of multiple pieces or expressions, any of which might be the cause of the problem. Your next goal should be to isolate the problem to just one of these pieces. Therefore, the next step is to break it down into smaller pieces, and then apply the same trace techniques to those subpieces. Listing 12.8 shows the modified Pace-Pal script with the problem statement broken down into smaller pieces that can be traced individually.

Listing 12.8. The complex statement broken down into multiple simpler statements.

Loop

' Temporary code to isolate problem to one statement

If Err.Number <> 0 Then Msgbox "Error prior to debugA: "

&Err.Description

debugA = CInt(vHours)

If Err.Number <> 0 Then Msgbox "Error after debugA: " _

&Err.Description

debugB = CInt(vMinutes)

If Err.Number <> 0 Then Msgbox "Error after debugB: " _

& Err.Description

debugC = CInt(vSeconds)

If Err.Number <> 0 Then Msgbox "Error after debugC: " & _

Err.Description

ConvertStringtoTotalSeconds = debugA + debugB + debugC

'Return total seconds value

'***Decomposed above for Debug ConvertStringtoTotalSeconds = _

'CInt(vHours) + CInt(vMinutes) + CInt(vSeconds)

If Err.Number <> 0 Then

Msgbox "Error #:" & Err.Number & " Description:" & Err.

Description & _

"Source:" & Err.Source, 0, "Error in

ConvertStringtoTotalSeconds!"

End If

End Function ' ConvertStringtoTotalSeconds

When the script is run after this change, the message box specifically highlights which piece causes the error. Figure 12.9 shows the error message generated when the CInt function, when applied to a string that contains non-numeric characters, causes VBScript to generate an error.

Figure 12.9 : Output from the first message box trace after the error, with a trace on decomposed statements.

This is the cause of the original runtime calamity first encountered at the start of the chapter.

Using Other Tools to Help You with the Debugging Task

The example in Listing 12.8 shows the use of many trace statements in just one area of code. In some cases, you might find that inserting one or more trace statements and moving them around is an effective strategy. If you have a 100-line script but suspect that the problem is somewhere in the first five lines, for example, you might put the error check after line 5. If the trace indicates an error at this location, it simply means that the error occurred somewhere on or prior to that line. To prove exactly where within the first five lines the error occurs, your next step might be to move the trace statement so that it comes right after line 4, and so on.

This type of trace statement movement actually is quite typical of debugging efforts. A statement is moved in the HTML page editor, the browser is activated, and the page is reloaded to test the effect of the change. The same cycle is repeated as often as necessary. As a result, you might find that much of your debugging time is spent in transition between your page editor and browser. It is worth noting that the mechanics of making such changes and testing them can consume a significant part of your debugging time. Take careful stock of the tools you have available, and find a process that works well for you.

One approach that works well in the Windows environment is to simply have the Notepad text editor loaded with your source file. You can specify View-Source from the Internet Explorer 3.0 menu to launch Notepad. Then you can use Notepad to modify your script and save it. After you save the script, however, don't close Notepad. It doesn't hurt to leave it up and running. Simply activate the browser, and then reload your page to pick up the new modifications. You can do this in Internet Explorer by pressing the f5 function key. After your modified page is loaded, you can test your script. When you find that more changes are needed, just shift back to the still-open Notepad and repeat the cycle. You avoid the time hit of reopening the editor by using this method. You can take a similar approach with the ActiveX Control Pad editor from Microsoft.

This Notepad scenario is outlined here not to emphasize how to best work with Notepad, but to stress that, whatever your tool, you need to put some thought into how you are applying it. The idiosyncrasies of interacting with it will be multiplied many times over, because debugging, and particularly tracing, often is a tedious, repetitive process. As more tools become available, choosing the right tool is likely to become more and more critical.

Because technology and tool sets are evolving almost on a daily basis, simply finding out about the right tools for debugging can be a challenge. The best way to get an up-to-date view of available tools for VBScript is to search the Web for current information. Resources such as www.doubleblaze.com present you with a summary of many of the important and useful tools. Microsoft's Web site, www.microsoft.com, provides additional sources of information.

Using VBScript Versus Traditional Debugging Environments

When you get a browser that supports VBScript, you do not get a VBScript development environment along with it. You are left to your own devices to decide how to construct the segments of VBScript code you insert in your Web pages. You might build VBScript programs in an HTML-generation tool, Microsoft's ActiveX Control Pad editor, or you could generate them directly in a text editor. No matter how you're doing it, odds are it's not in a dedicated VBScript development environment with the full-fledged debugging features of Visual Basic 4.0. Although such tools may materialize over time, they do not exist currently. By contrast, almost all high-end computer languages do have their own sophisticated development environment. Typically, a special editor is used for producing programs; the editor sometimes can help check syntax as you type the programs. Also, these environments often include powerful debugging environments that assist with the task of tracing a program and isolating problems. The Visual Basic 4.0 environment and VBA 5.0 offer a rich set of debugging facilities. Although you can't use these debugging facilities directly with VBScript, you can apply some of the same concepts with the "build-it-yourself" trace techniques you'll learn about later in this chapter.

Using Visual Basic to Debug VBScript Applications

Visual Basic 4.0 has a wealth of debugging tools and capabilities that VBScript does not have. If you are a Visual Basic programmer, however, you can take advantage of Visual Basic to debug your VBScript programs. How is this done? This section gives you the answers.

Using Visual Basic 4.0 Trace Capabilities

The Visual Basic 4.0 programmer has no excuse for not having keen insight into all areas of his source code. Visual Basic 4.0, the high-end member of the Visual Basic family, has powerful, easily controlled debugging facilities. Visual Basic 4.0, for example, enables you to stop the program at any location simply by selecting a line of source code in the program editor and pressing a function key to designate the line as a temporary breakpoint. Upon hitting this breakpoint, the running program screeches to a halt, enabling you to spring into debugging action. You can inspect the value of variables in a special debugging window provided by the Visual Basic 4.0 design environment. You can even type more complex expressions in the window. This enables you to evaluate any other statements that might help you understand the problem. You can use this window to inspect the current state of variables even as your program remains suspended. Figure 12.10 shows a view of the Visual Basic 4.0 development environment with a suspended program that has reached a breakpoint.

Figure 12.10 : The Visual Basic 4.0 debugging environment.

After you check out the state of your suspended program, you can send it on its merry way, right where it left off. You can have it proceed one statement at a time, providing you with the opportunity to query its state line by line. You also can just let it continue until program completion or some other predefined breakpoint. You can even tell it to pick up at an entirely different program location than the one where it stopped. And if you're even nosier about what is going on, you can make arrangements to automatically monitor the contents of your favorite variables as the program chugs along. As variable values change, the new values are displayed automatically in the debug window without stopping your program. You can even provide instructions for your program to stop and show you the last statement it carried out if a variable reaches a certain value.

By now, your head probably is spinning from this dizzying array of debugging weapons, and you're wondering how many of them apply to VBScript. Unfortunately, the answer is that virtually none of these whiz-bang Visual Basic 4.0 debugging features is available to you as you develop VBScript programs. No inherent means exists through the browser or standard text editor to place breakpoints on VBScript programs, to pop up a debug window, to make your program leap to a new location from a suspended state, or to automatically monitor the contents of variables.

On the other hand, several rays of encouragement offset this lack of tools. One comforting thought for the long term is that, as the language matures, tools will likely come. True, that doesn't help you for the short term, but even today's VBScript provides the foundation you need to build similar trace and monitoring capabilities right into your programs. It takes a little extra effort, but as you'll see in the sections that follow, the techniques are really quite easy to apply.

NOTE

You can find further information on some VBScript development tools as they become available at www.DoubleBlaze.com. Many utilities also are available for various aspects of Web page authoring at the Microsoft site at www. microsoft.com/intdev.

Debugging VBScript Code Within Visual Basic

If you happen to be one of the million plus Visual Basic 4.0 or Visual Basic for Applications programmers, you have a secret weapon you can use in developing VBScript code. Try writing the code in Visual Basic first, and then move it to VBScript! You can take full advantage of the rich debugging capabilities of Visual Basic as you get the bugs out of your program. Then, when it is stable, you can move it to VBScript.

A note of caution, however: There's no such thing as a free lunch. Likewise, there's also no such thing as a free debugger (or at least so it seems). Language differences exist between Visual Basic and VBScript. A program that works fine in Visual Basic 4.0 can be obstinate in VBScript. VBScript is a subset of VBA, so much of what works in your Visual Basic application will not work in your script. Depending on your knowledge of the two languages, it can take some work to weed out the syntax differences as you move the code over from Visual Basic 4.0 to VBScript. Some of the language differences are subtle and might not be immediately obvious, even if you know both languages fairly well. The bottom line is that if you debug your code in Visual Basic first and get it running in Visual Basic, don't assume that you're home free. Porting work still might lie ahead in moving the code to VBScript. Nevertheless, Visual Basic's debugging tools make the "debug, then port" process more effective for larger or more complex scripts.

Handling HTML Errors

So far, you have been looking at techniques to debug VBScript applications. You should be aware of the fact, however, that your programs typically are contained within HTML documents. In this case, VBScript works hand-in-hand with HTML, and you must write both VBScript and HTML code to present a Web page to the user. One thing HTML does have going for it is a rich set of tools that can aid in quickly developing well-structured Web pages. If you don't have one of those tools, however, or if you have a low-end tool, debugging HTML itself can be the cause of serious hair pulling and grimacing.

It is just as easy to create errors in VBScript as it is in HTML. So how does HTML resolve errors? Consider the HTML shown in Listing 12.9, and note the <A that marks the beginning of an anchor reference.

Listing 12.9. Normal HTML.

<H1><A HREF="http://www.mcp.com"><IMG ALIGN=BOTTOM

SRC="../shared/jpg/samsnet.jpg" BORDER=2></A>

<EM>Pace-Pal Sample 3</EM></H1>

Suppose that this markup language had been mistakenly entered with just one character different. Assume that the < was inadvertently omitted from in front of the A, as shown in Listing 12.10.

Listing 12.10. HTML missing a tag.

<H1>A HREF="http://www.mcp.com"><IMG ALIGN=BOTTOM

SRC="../shared/jpg/samsnet.jpg" BORDER=2></A>

<EM>Pace-Pal Sample 3</EM></H1>

The effects of such an omission are ugly indeed, as you can see in Figure 12.11.

Figure 12.11 : Pace-Pal with a missing tag.

Instead of creating the anchor, the internal details of creating the anchor are displayed on-screen. To make matters worse, not only are the results ugly, but they also result in a Web page that doesn't work as intended. The page now has a nonfunctioning link. HTML has no runtime sequence of logic to step through to help pinpoint the error. Instead, it is just a markup or pagegeneration instruction set. In the absence of sophisticated authoring tools for HTML, the only way you can debug the code is the old method of inspecting it visually. You must look at the page, visually scan it, and review each tag, one by one, for proper syntax. As a general rule, you first should ensure that your HTML code is clean by making a visual inspection before proceeding to debug the VBScript code itself.

Making Sure the Bugs Are Dead

Once you know the cause of the bug, fixing it is often easy. First, decide what type of fix you want to put in place. In most programs, like Pace-Pal, many solutions are available. Pace-Pal can immediately check that data entered by the user is in the correct format and demand that the user reenter the data if it is invalid, for example. Alternatively, Pace-Pal can check the data and drop illegal extra characters without telling the user. Then again, Pace-Pal simply can convert any invalid data to 0 and let the user know. The possibilities are many.

For the sake of simplicity, the last solution mentioned is used in this example. Although this solution isn't necessarily the best way to handle the fix when viewed in the context of the whole program, it does eliminate the error. A check for invalid data is made right before the problem statement. If invalid data is found, the user is informed and all times are forced to be 0. Because this is a legal numeric representation, the type-conversion problem is avoided and no error occurs. Listing 12.11 shows this solution.

Listing 12.11. The fix that our trace pointed us to!

'If there is invalid string data, warn the user and

'reset data to prevent more errors

If (Not IsNumeric(vHours)) Or (Not IsNumeric(vMinutes)) _

Or (Not IsNumeric(vSeconds)) Then

Msgbox "Time contains character when digits expected. " & _

" Please respecify!", 0,"Invalid time"

vHours = 0

vMinutes = 0

vSeconds = 0

End If

' Return total seconds value

ConvertStringtoTotalSeconds = CInt(vHours) + CInt(vMinutes) + CInt (vSeconds)

If Err.Number <> 0 Then

Msgbox "Error #:" & Err.Number & " Description:" & _

Err.Description & " Source:" & Err.Source,

0, "Error in ConvertStringtoTotalSeconds!"

End If

End Function ' ConvertStringtoTotalSeconds

With this fix, the program will be robust enough to continue even if a user enters invalid input. Figure 12.12 shows the resulting message box. The program informs the user of the error, substitutes a time of 0 in place of the input, and calculates a pace of 0 seconds per mile.

Figure 12.12 : User feedback from the bug-proofed Pace-Pal.

The next step after inserting the fix is to verify that it worked. In this case, that verification is relatively easy. This fix addresses a type-conversion error that prevented Pace-Pal from calculating a final pace. Because the code that checks the error status is still in place at the end of the procedure, simply rerun the procedure. If you don't get an error message, you know the typeconversion error has been eliminated. Likewise, the fact that a pace of 00:00 shows up in the pace text box indicates a complete calculation. So, in this case, verifying that the fix really solves the problem is relatively easy. For some bugs, you might have to add more trace statements or variable analyses after the fix is in place to verify that it had the intended results.

It is important to note that this fix keeps the program from crashing when the user enters an invalid time such as 37:12AA. A similar problem still exists in the code with distance rather than time, however. If the user enters 6.2AA rather than 6.2 miles, a type-conversion-induced runtime error results from a different procedure in Pace-Pal that calculates the final pace. Because the type of problem present in dealing with time also is present in dealing with distance, more than one fix is needed in Pace-Pal to address all the areas where this type of problem occurs. This, it turns out, is very common in debugging, especially in datahandling code. If you find a bug in one place, check for it in other areas of the program too. If wrong assumptions or coding techniques led to problems once, they very likely will lead to problems again.

If you think you have a problem area that you need to check throughout your application, you should be able to pinpoint it quite easily if it results in an error condition. You can use the techniques presented earlier in this lesson. Insert On Error

Resume Next in every procedure. At the end of every procedure, check the Err object and display a message if an error occurred within that procedure. This level of tracing gives you a clear indication of any procedures in which the error occurs.

Creating Your Own Debug Window for Tracing

By now, the value of good tracing should be clear. Good debugging usually comes down to good program tracing. Several approaches to tracing are available. The technique of tracing the flow of every statement by displaying message boxes was presented in the sections "Using Simple Debugging Techniques" and "Using Advanced Debugging Techniques." This method can be a bit cumbersome because it requires interaction with a series of message boxes each time you do a trace. The technique of simply displaying a message box only if an error occurs also was illustrated earlier in this chapter. This approach is quite effective, but you might want to monitor the flow of your program at times even if an error has not occurred.

Tracing all statements, even under non-error conditions, can provide a helpful overall picture of what the code is really doing. Understanding the flow of the code and gaining insight into the state of the variables as the code execution progresses helps you better understand the behavior of a program. The more you understand the overall behavior of a program, the better code you can write for it. Likewise, you'll be able to make better intuitive decisions when chasing problems. So what's the best approach when you want to trace normal program flow? As established earlier, the "always display message box" approach can be rather cumbersome. And the "display message box only after error" approach doesn't give the full level of feedback you might be looking for in every case.

You really need a separate debug window that lets you peek into the program as it progresses, much like Visual Basic 4.0's debug window. It turns out that you can build at least some of those capabilities right into your page with VBScript. You just add a rather large form text area input control at the bottom of your page. A debug text box typically is used as a temporary debug tool, and is removed before you release your final version of the code. But in the meantime, during the scriptdevelopment phase, it can be a considerable help during debugging. Listing 12.12 shows the Pace-Pal HTML source code with an <INPUT> tag added to define this type of debug text box. The sample program Ppalerr5.htm on the accompanying CDROM uses this same approach, but it additionally formats the input controls in a table for better visual presentation on-screen.