| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 9108 人关注过本帖
标题:本人毕业设计 急求vb方面 中英文对照翻译文章
只看楼主 加入收藏
ouzhiguang
Rank: 1
来 自:湖南长沙
等 级:新手上路
威 望:1
帖 子:240
专家分:0
注 册:2007-5-18
收藏
 问题点数:0 回复次数:29 
本人毕业设计 急求vb方面 中英文对照翻译文章
本人毕业设计 急求vb方面 中英文对照翻译文章,翻译成汉字大概是6000字左右;要是没有中文对照,发篇英文的给 我也行 本人邮箱:1989-09@ 谢谢!
搜索更多相关主题的帖子: 中英文 毕业设计 翻译 
2007-05-21 22:27
ioriliao
Rank: 7Rank: 7Rank: 7
来 自:广东
等 级:贵宾
威 望:32
帖 子:2829
专家分:647
注 册:2006-11-30
收藏
得分:0 

Chapter 1
On Error GoTo Hell
A Methodical Approach to Error Handling
PETER J. MORRIS

Peet is the Technical Director and a cofounder of The Mandelbrot Set (International) Limited (TMS). Peet, a former Microsoft employee, is acknowledged industry-wide as a Microsoft Windows and Visual Basic expert and is a frequent speaker at events such as VBITS and TechEd. As a developer and lecturer, he has taught Windows (SDK) API, Advanced Windows API, Visual Basic (all levels), OS/2 Presentation Manager, C, C++, Advanced C and C++, Pascal, compiler theory, OWL, Smalltalk, and CommonView.

Since the first edition of this book was released, this chapter has been tidied up a little. I've added some new rules and sidebars about handling errors in components and controls, as well as some examples of handling errors on a server.

What is an error? The short answer is, "Something that's expensive to fix." Dealing with errors is costly in terms of both money and time. As you probably know already, your test cycle will be longer, more complex, and less effective if you don't build appropriate error handling into your code right from the start. You should do all you can to reduce and handle errors in order to reduce costs, deliver quality code, and keep to schedules.

One way to eradicate errors—a way that I'll dismiss immediately—is to write error-free code. I don't think it's possible to write such pristine code. A more realistic way to deal with errors effectively is to plan for them properly so that when they do occur:


The application doesn't crash.

The error's root cause (and thus cure) is relatively easy to determine.

The error is as acceptable and as invisible to the user as is humanly possible.
So what must we do to put a good error handling scheme in place? It's a deceptively simple question with a big (subjective) set of answers. I think that acquiring and then using some fundamental knowledge is where we should start:


Ensure that all your developers truly understand how Visual Basic raises and then dispatches and handles errors.

Make sure that those same developers understand the consequences of writing code that is hard to debug and the true costs of any unhandled error.

Develop a suitable error handling strategy that's based on your understanding of the preceding two points and that takes into account your budget and line of business.

Apply your strategy; demand self-discipline and team discipline.
Handling errors properly in Visual Basic is also a good idea because of the alternative: Visual Basic's default error handling rules are rather severe. Unhandled errors are reported, and then an End statement is executed. Keep in mind that an End statement stops your application dead—no form QueryUnload or Unload events, no class Terminate events, not much of anything in fact.

To help you develop an effective strategy for dealing with errors, I'll go over some ideas that I consider vital to the process. These are presented (in no particular order) as a series of tips. "Pick 'n mix" those you think will suit you, your company, and of course your development project. Each tip is empirical, and we have employed them in the code we write at The Mandelbrot Set (International) Limited (TMS). I hope they serve you as well as they have served us!


/images/2011/147787/2011051411021524.jpg" border="0" />
2007-05-22 10:24
ioriliao
Rank: 7Rank: 7Rank: 7
来 自:广东
等 级:贵宾
威 望:32
帖 子:2829
专家分:647
注 册:2006-11-30
收藏
得分:0 

Tip 1: Inconsistent as it is, try to mimic Visual Basic's own error handling scheme as much as possible.
When you call a Visual Basic routine that can fail, what is the standard way that the routine signals the failure to you? It probably won't be via a return value. If it were, procedures, for one, would have trouble signaling failure. Most (but not all) routines raise an error (an exception) when they want to signal failure. (This applies to procedures, functions, and methods.) For example, CreateObject raises an exception if it cannot create an object—for whatever reason; Open does the same if it cannot open a file for you. (Not all routines raise such exceptions. For example, the Choose function returns Null [thus, it requires a Variant to hold its return value just in case it ever fails] if you index into it incorrectly.) In other words, if a routine works correctly, this fact is signaled to the caller by the absence of any error condition.

Routines such as Open work like this primarily so that they can be used more flexibly. For example, by not handling the error internally, perhaps by prompting the user in some way, the caller is free to handle errors in the most suitable way. The caller can also use routines in ways perhaps not thought of by their writers. Listing 1-1 is an example using GetAttr to determine whether the machine has a particular drive. Because this routine raises exceptions, we can use it to determine whether or not a disk drive exists.

Listing 1-1 Using error handling to test for a disk drive
Public Function bDriveExists(ByVal sDriveAndFile As String) _
As Boolean
' ===============================================================
'
' Module: modFileUtilities. Function: bDriveExists.
'
' Object: General
'
' Author - Peter J. Morris. TMS Ltd.
' Template fitted : Date - 01/01/97 Time - 00:00
'
' Function's Purpose/Description in Brief
'
' Determines whether the drive given in sDriveAndFile
' exists. Raises an exception if no drive string is given.
'
' Revision History:
'
' BY WHY AND WHEN AFFECTED
' Peter J. Morris. TMS Ltd. - Original Code 01/01/97, 00:00
'
' INPUTS - sDriveAndFile - holds the drive name, e.g., "C".
' Later holds the name of the drive and the filename
' on the drive to be created.
'
'
' OUTPUTS - Via return. Boolean. True if drive exists;
' else False.
'
' MAY RAISE EXCEPTIONS
'
' NOTES: Uses formals as variables.
' Uses delayed error handling.
'
' ===============================================================

' Set up general error handler.
On Error GoTo Error_General_bDriveExists:

Const sProcSig = MODULE_NAME & "General_bDriveExists"

' ========== Body Code Starts ==========
' These are usually a little more public - shown local
' for readability only.
Dim lErr As Long
Dim lErl As Long
Dim sErr As String

' Constants placed here instead of in typelib for
' readability only.
Const nPATH_NOT_FOUND As Integer = 76
Const nINTERNAL_ERROR_START As Integer = 1000
Const nERROR_NO_DRIVE_CODE As Integer = 1001

' Always default to failure.
bDriveExists = False

If sDriveAndFile <> "" Then

' "Trim" the drive name.
sDriveAndFile = Left$(sDriveAndFile, 1)

' Root directory.
sDriveAndFile = sDriveAndFile & ":\"

' Enter error-critical section - delay the handling
' of any possible resultant exception.
On Error Resume Next

Call VBA.FileSystem.GetAttr(sDriveAndFile)

' Preserve the error context. See notes later on
' subclassing VBA's error object and adding your own
' "push" and "pop" methods to do this.
GoSub PreserveContext

' Exit error-critical section.
On Error GoTo Error_General_bDriveExists:

Select Case nErr

Case nPATH_NOT_FOUND:
bDriveExists = False

' Covers no error (error 0) and all other errors.
' As far as we're concerned, these aren't
' errors; e.g., "drive not ready" is OK.
Case Else
bDriveExists = True

End Select

Else

' No drive given, so flag error.
Err.Raise nLoadErrorDescription(nERROR_NO_DRIVE_CODE)

End If

' ========== Body Code Ends ==========

Exit Function

' Error handler
Error_General_bDriveExists:

' Preserve the error context. See notes later on
' subclassing VBA's error object and adding your own "push"
' and "pop" methods to do this.
GoSub PreserveContext

' **
' In error; roll back stuff in here.
' **

' **
' Log error.
' **

' Reraise as appropriate - handle internal errors
' further only.
If (lErr < nINTERNAL_ERROR_START) Or _
(lErr = nERROR_NO_DRIVE_CODE) Then

VBA.Err.Raise lErr

Else

' Ask the user what he or she wants to do with this
' error.
Select Case MsgBox("Error in " & sProcSig & " " _
& CStr(lErr) & " " & _
CStr(lErl) & " " & sErr, _
vbAbortRetryIgnore + vbExclamation, _
sMsgBoxTitle)
Case vbAbort
Resume Exit_General_bDriveExists:

Case vbRetry
Resume

Case vbIgnore
Resume Next

Case Else
VBA.Interaction.MsgBox _
"Unexpected error" _
, vbOKOnly + vbCritical _
, "Error"
End

End Select

End If

Exit_General_bDriveExists:

Exit Function

PreserveContext:

lErr = VBA.Err.Number
lErl = VBA.Erl
sErr = VBA.Err.Description

Return

End Function


Here are a few comments on this routine:


Although it's a fabricated example, I've tried to make sure that it works and is complete.

It handles errors.

It uses delayed error handling internally.

It's not right for you! You'll need to rework the code and the structure to suit your particular needs and philosophy.

The error handler might raise errors.

It doesn't handle errors occurring in the error handler.

It uses a local subroutine, PreserveContext. This subroutine is called only from within this routine, so we use a GoSub to create it. The result is that PreserveContext is truly private and fast—and it doesn't pollute the global name space. (This routine preserves the key values found in the error object. Tip 11 explains a way to do this using a replacement Err object.)
Within bDriveExists, I've chosen to flag parameter errors and send the information back to the caller by using exceptions. The actual exception is raised using the Raise method of the Visual Basic error object (Err.Raise) and the return value of a function (nLoadErrorDescription). This return value is used to load the correct error string (typically held in string resources and not a database since you want to always be able to get hold of the string quickly). This string is placed into Err.Description just before the Raise method is applied to the error object. Reraising, without reporting, errors like this allows you to build a transaction model of error handling into your code. (See Tip 14 for more on this topic.)

The nLoadErrorDescription function is typically passed the error number (a constant telling it what string to load), and it returns this same number to the caller upon completion. In other words, the function could look something like this (omitting any boilerplate code):


Public Function nLoadErrorDescription(ByVal nCode As Integer)

' Return the same error code we're passed.
nLoadErrorDescription = nCode

' Load the error text for nCode from some source and assign it
' to Err.Description.
Err.Description = LoadResString(nCode)

Exit Function

End Function

In this example, we're using a string resource to hold the error text. In reality, the routine we normally use to retrieve an error string (and, indeed, to resolve the constant) is contained in what we call a ROOS—that's a Resource Only OLE Server, which we'll come back to in Tip 10.

A good error handler is often complex, question: What will happen if we get an error in the error handler? Well, if we're in the same local scope as the original error, the error is passed back up the call chain to the next available error handler. (See Tip 5 for more information on the call chain and this mechanism.) In other words, if you're in the routine proper when this second error occurs, it will be handled "above" your routine; if that's Visual Basic, you're dead! "OK," you say, "to handle it more locally, I must have an error handler within my error handler." Sounds good—trouble is, it doesn't work as you might expect. Sure, you can have an On Error Goto xyz (or On Error Resume Next or On Error Resume 0) in your error handler, but the trap will not be set; your code will not jump to xyz if an error occurs in your error handler. The way to handle an error in your error handler is to do it in another procedure. If you call another procedure from your error handler, that routine can have an error trap set. The net effect is that you can have error handling in your error handler just as long as another routine handles the error. The ability to handle errors in error handlers is fundamental to applying a transaction processing model of error handling to your application, a subject I'll explain further in Tip 14.

To recap, the reason GetAttr doesn't handle many (if any) internal errors is that to do so would take away its very flexibility. If the routine "told" you that the drive didn't exist, by using, say, a message box, you couldn't use it the way we did in bDriveExists.

If you're still not convinced, I'll be saying a little more on why raising errors is better than returning True or False later. But for now, let's think BASICA!


/images/2011/147787/2011051411021524.jpg" border="0" />
2007-05-22 10:24
ioriliao
Rank: 7Rank: 7Rank: 7
来 自:广东
等 级:贵宾
威 望:32
帖 子:2829
专家分:647
注 册:2006-11-30
收藏
得分:0 

--------------------------------------------------------------------------------

Tip 2: Use line numbers in your source code.
Line numbers!? Yup, just like those used in "real" Basic. Bear with me here—I'll convince you!

In older versions of Basic, line numbers were mandatory and often used as "jump targets." A jump target is a line number used with a GoTo, such as GoTo 2000. The 2000 identifies the start of a block of code to execute next. After GoTo came GoSub (and Return). Now you had a "Basic subroutine," albeit one with a strange name: GoSub 2000. You can think of the (line) number almost as an address (just as in C). These days, of course, Basic is Visual Basic and we use symbolic names for labeling such jump targets (real subroutines, just like those in C and other programming languages). Line numbers have become a peculiarity designed to allow nothing more than some level of backward compatibility with some other version of Basic.

Or then again, maybe not. In Visual Basic, Erl, a Visual Basic (undocumented in Visual Basic 4, 5, and 6 but present in all versions of Visual Basic thus far) "global variable," gives you access to the line number of any erroring line of code. So by using line numbers and by using Erl in your error handlers, you can determine which line of code erred—wow! What happens to Erl if you don't use line numbers? Easy—it will always be 0.

Of course, you won't want to start typing line numbers in by hand. You need some automation. At TMS, we add line numbers to our code using an internal tool we originally developed for working with Visual Basic 2. It now works as an add-in under Visual Basic 6. There are tools on the market that can do the same for your code.

After the first edition of our book came out, we received lots of mail asking us where such a line numbering tool could be obtained. The demand was so great, and the available tools were so few, that we put an FOC line number wizard on our web site (www.themandelbrotset.com/html/downloads.html). That tool is still there, so please feel free to download a copy of it.

At TMS, we don't work with line numbers in our source code, however. We add them only when we're doing a ship build—that is, when we want to ship a binary to, say, beta testers or to manufacturing for an impending release. We use our internal tool to build a new version of the code, complete with line numbers, and then we make an executable from that. We store the line numbered source code in our source control system and ship the executable. We cross-reference the EXE version number (the Auto Increment option is just great here) to the source code stored in the source control system. Every time we do a new build for shipping, we create a new subproject whose name is the version number of the build and then store the line numbered source code in it along with a copy of the binary image. If an error report comes in, we can easily refer back to the source code to find the erroring line (very easy if you're using Microsoft Visual SourceSafe). Typically, the error report will contain details of the module, routine, and line number of the error.

Listing 1-2 is a typical Click event, line numbers and all.

Listing 1-2 Generic Click event with line numbers

Private Sub Command1_Click()
' =============================================================
' Module Type : Form
' Module Name : Form1
' Object : Command1
' Proc Type : Sub
' Proc Name : Click
' Scope : Private
' Author :
' Date : 01/01/97 00:00
'
' History : 01/01/97 00:00: Peter J. Morris : Original Code.
' =============================================================

' Set up general error handler.
On Error GoTo Error_In_Command1_Click:

1 Dim sErrorDescription As String

2 Const sProcSig = MODULE_NAME & "Command1_Click"

' ========== Body Code Starts ==========

3 Debug.Print bDriveExists("")

' ========== Body Code Ends ==========

4 Exit Sub

' Error handler
Error_In_Command1_Click:

5 With Err
6 sErrorDescription = "Error '" & .Number & " " & _
.Description & "' occurred in " & sProcSig & _
IIf(Erl <> 0, " at line " & CStr(Erl) & ".", ".")
7 End With

8 Select Case MsgBox(sErrorDescription, _
vbAbortRetryIgnore, _
App.Title & " Error")

Case vbAbort
9 Resume Exit_Command1_Click:
10 Case vbRetry
11 Resume
12 Case vbIgnore
13 Resume Next
14 Case Else
15 End

16 End Select

Exit_Command1_Click:

End Sub


Notice in Listing 1-2 that sProcSig is made up of the module name (Form1) and the routine name (Command1_Click). Notice also that the error handler examines Erl to "see" whether line numbers have been used. Figure 1-1 shows what's typically displayed when an error occurs using this kind of scheme.

图片附件: 游客没有浏览图片的权限,请 登录注册

Figure 1-1 Error and line number information

Of course, the actual code that makes up the error handler is entirely up to you. If you use this scheme, I recommend you have a module-level constant to hold your module name and use a routine-level constant to hold the routine name plus the module name:

Module Declaration Section


Private Const MODULE_NAME As String = "Form1."

Command1_Click Event


Const sProcSig As String = MODULE_NAME & "Command1_Click"


/images/2011/147787/2011051411021524.jpg" border="0" />
2007-05-22 10:27
ioriliao
Rank: 7Rank: 7Rank: 7
来 自:广东
等 级:贵宾
威 望:32
帖 子:2829
专家分:647
注 册:2006-11-30
收藏
得分:0 

Tip 3: Raise exceptions when possible because return values will be ignored.
This tip supplements Tip 1: "Inconsistent as it is, try to mimic Visual Basic's own error handling scheme as much as possible." Since Visual Basic 4, a function can be called like a subroutine. (In Visual Basic 3 and earlier, it couldn't.) To demonstrate this, consider the following code fragments:


Sub Command1_Click ()

Debug.Print SomeFunc()
Call SomeFunc

End Sub


Function SomeFunc () As Integer

SomeFunc = 42

End Function

The line Call SomeFunc is illegal in Visual Basic 3 but legal in Visual Basic 4 and later. (It's VBA!) In case you're wondering why this is so, the facility was added to VBA (Visual Basic for Applications) to allow you to write routines that were more consistent with some of Visual Basic's own routines, such as MsgBox, which acts sometimes like a function and sometimes like a statement (or a C type procedure if you're used to that language). (In Tip 4, you'll find out how to write your own MsgBox routine.)

A side effect of all this is that routines that return some indication of success or failure might now have that result ignored. As C and SDK programmers know only too well, this will cause problems! In Visual Basic 3, the programmer always had to use the return value. Typically, he or she would use it correctly. If a programmer can ignore a routine's returned value (say it's not a database handle but a True/False value—that is, either it worked or it failed), however, he or she usually will ignore it.

Exceptions, on the other hand, cannot easily be ignored (except by using On Error Resume Next or On Error Resume 0—both easy to test for and legislate against). Also, keep in mind that "newer" Visual Basic developers sometimes lack the necessary self-discipline to use and test return values correctly. By raising exceptions, you force them to test and then to take some appropriate action in one place: the error handler.

Another reason to use exceptions is that not using them can cause your code to become more difficult to follow—all those (un)necessary conditional tests to see that things have worked correctly. This kind of scheme, in which you try some code and determine that it didn't work by catching a thrown exception, is pretty close to "structured exception handling" as used in C++ and Microsoft Windows NT. For more on structured exception handling, see the MSDN Library Help. (Select the Contents tab, and follow this path: Visual C++ Documentation; Reference; C/C++ Language and C++ Libraries; C++ Language Reference; Statements; Exception Handling; Structured Exception Handling.)

Here's an example of a structured exception handling type of scheme:


Private Sub SomeWhere()

If a() Then
.
.
.
If b() Then
.
.
.
If c() Then
.
.
.
End If
End If
End If

End Sub

This example is not too hard to figure out. But I'm sure you've seen far more complex examples of nesting conditionals, and you get the idea! Here's the same code using exceptions to signal errors in a, b, or c:


Private Sub SomeWhere()

' TRY
On Error Goto ????

a()
.
.
.
b()
.
.
.

c()
.
.
.

' CATCH
????

' Handle exception here.

End Sub

Can you see the flow any easier here? What's implied by the presence of the error handler is that to get to the call to b, a must function correctly. By losing the If, you're losing some plain readability, but you're also gaining some readability—the code is certainly less cluttered. Of course, sometimes code is clear just because you're used to it. Consider replacing b, for instance, with a call to Open. If you were to use the If…Then scheme to check for errors, you couldn't check for any errors in Open because you can't put conditional statements around a procedure. So it's easy for you to accept the fact that after Open is called, if an error occurs, the statement following Open will not run. It works the same with the b function. If an error occurs in the b function, the error routine rather than the statement that follows b will execute.

If you adopt this kind of error handling scheme, just make sure that you have projectwide collaboration on error codes and meanings. And by the way, if the functions a, b, and c already exist (as used previously with the If statements), we'll be using this "new" ability to ignore returned values to our advantage.


NOTE


--------------------------------------------------------------------------------

Once again, if a routine's returned value can be ignored, a programmer will probably ignore it!


/images/2011/147787/2011051411021524.jpg" border="0" />
2007-05-22 10:27
ioriliao
Rank: 7Rank: 7Rank: 7
来 自:广东
等 级:贵宾
威 望:32
帖 子:2829
专家分:647
注 册:2006-11-30
收藏
得分:0 

Tip 4: Automatically log critical MsgBox errors.
One way to log critical MsgBox errors is by not using the standard message box provided by VBA's Interaction.MsgBox routine. When you refer to an object or a property in code, Visual Basic searches each object library you reference to resolve it. Object library references are set up in Visual Basic's References dialog box. (Open the References dialog box by selecting References from the Project menu.) The up arrow and down arrow buttons in the dialog box move references up and down in a list so that they can be arranged by priority. If two items in the list use the same name for an object, Visual Basic uses the definition provided by the item listed higher in the Available References list box. The three topmost references (Visual Basic For Applications, Visual Basic Runtime Objects And Procedures, and Visual Basic Objects And Procedures) cannot be demoted (or shuffled about). The caveat to all this prioritizing works in our favor—internal modules are always searched first.

Visual Basic 6 allows you to subclass its internal routines such as MsgBox and replace them with your own (through aggregation). Recall that in the code shown earlier (in Listing 1-1) some of the calls to MsgBox were prefixed with VBA. This explicitly scopes the call to VBA's MsgBox method via the Visual Basic For Applications type library reference. However, calls to plain old MsgBox go straight to our own internal message box routine.

A typical call to our new message box might look like this:


MsgBox "Error text in here", _
vbYesNo + vbHelpButton + vbCritical, sMsgBoxTitle

The vbHelpButton flag is not a standard Visual Basic constant but rather an internal constant. It's used to indicate to MsgBox that it should add a Help button. Also, by adding vbCritical, we're saying that this message (error) is extremely serious. MsgBox will now log this error to a log file.

To replace MsgBox, all you have to do is write a function (an application method really) named MsgBox and give it the following signature. (The real MsgBox method has more arguments that you might also want to add to your replacement; use the Object Browser to explore the real method further.)


Public Function MsgBox _
( _
ByVal isText As String _
, Optional ByVal inButtons As Integer _
, Optional ByVal isTitle As String _
)

Here's an example of a trivial implementation:


Public Function MsgBox _
( _
ByVal isText As String _
, Optional ByVal inButtons As Integer _
, Optional ByVal isTitle As String _
)

Dim nResult As Integer

nResult = VBA.Interaction.MsgBox(isText, inButtons, isTitle)

MsgBox = nResult

End Function

Here we're logging (implied by the call to LogError) the main message text of a message box that contains the vbCritical button style. Notice that we're using the VBA implementation of MsgBox to produce the real message box on screen. (You could use just VBA.MsgBox here, but we prefer VBA.Interaction.MsgBox for clarity.) Within your code, you use MsgBox just as you always have. Notice also that in our call to LogError we're logging away the user's response (nResult) too—"I'm sure I said 'Cancel'!"

Another good idea with any message box is always to display the application's version number in its title; that is, modify the code above to look like this:


sTitle = App.EXEName & "(" & App.Major & "." & _
App.Minor & "." & _
App.Revision & ")"

nResult = VBA.Interaction.MsgBox(isText, inButtons, _
sTitle & isTitle)

Figure 1-2 shows the message box that results from this code.

Figure 1-2 Using your version number in message boxes

Of course, you don't have to use VBA's MsgBox method to produce the message box. You could create your own message box, using, say, a form. We create our own custom message boxes because we often want more control over the appearance and functionality of the message box. For example, we often use extra buttons (such as a Help button, which is what the vbHelpButton constant was all about) in our message boxes.

One nifty way to log error events (or any other event you might consider useful) is to use the App object's LogEvent method, which logs an event to the application's log target. On Windows NT platforms, the log target is the NT Event Log; on Windows 9x machines, the log target writes to a file specified in the App.LogPath property. By default, if no file is specified, events are written to a file named VBEVENTS.LOG.

This code


Call App.LogEvent("PeetM", vbLogEventTypeInformation)
Call App.LogEvent(Time$, vbLogEventTypeError)

produces this output in the log:


Information Application C:\WINDOWS\vbevents.log: Thread ID:
-1902549 ,Logged: PeetM
Error Application C:\WINDOWS\vbevents.log: Thread ID:
-1902449 ,Logged: 15:11:32

Interestingly, App.LogPath and App.LogMode are not available at design time and are available as read-only at run time, so how do you set them? You set them with App.StartLogging. A disadvantage to these routines is that App.LogEvent is available only in a built executable—not very useful for debugging in the Integrated Development Environment (IDE)! Now the good news: you can improve on this behavior by using the Win32 API directly from your application to log events to the NT Event Log (under Windows NT) or to a file (under Windows 9x). If you're going to log events this way I would suggest that you do so by ignoring the advice given in the Support Online article, HOWTO: Write to the NT Event Log from Visual Basic. (You can find this article by searching for article ID Q154576 on Microsoft's web site, www.microsoft.com.) Instead, wrap the necessary API calls (steal the code required from the HOWTO article) within a replacement App object that is contained within an ActiveX DLL (to which you have a reference in your project). This means that you'll still use App.LogEvent and the other routines, but instead of calling into the "real" App object, you're calling into the one you've provided in the DLL (which is compiled, of course). You can write this DLL so that you can easily change App.LogFile or any other routine (if you're running Windows 9x).

图片附件: 游客没有浏览图片的权限,请 登录注册


/images/2011/147787/2011051411021524.jpg" border="0" />
2007-05-22 10:29
ioriliao
Rank: 7Rank: 7Rank: 7
来 自:广东
等 级:贵宾
威 望:32
帖 子:2829
专家分:647
注 册:2006-11-30
收藏
得分:0 

--------------------------------------------------------------------------------

Tip 5: Have an error handler in every routine.
Because Visual Basic nests routines into local address space, all errors happen locally. An unhandled error in some routine that might be handled above that routine, in another error handler, should be considered unhandled because it will probably destabilize the application.

Let's go over that again, but more slowly. Visual Basic handles local errors. By this, I mean that whenever an error handler is called it always thinks it's acting upon an error produced locally within the routine the error handler is in. (Indeed, a bit of obscure syntax, normally unused because it's implied, On Local Error GoTo, gives this little secret away.) So if we write some functions named SubA, SubB, and SubC and arrange for SubA to call SubB and SubB in turn to call SubC, we can spot the potential problem. (See Figure 1-3.) If SubC generates an error, who handles it? Well, it all depends. If we don't handle it, Visual Basic will. Visual Basic looks up Err.Number in its list of error strings, produces a message box with the string, and then executes an End for you. If, as in Figure 1-3, SubA handles errors, Visual Basic will search up through the call chain until it finds SubA (and its error handler) and use that error handler instead of its own default error handler. Our error handler in SubA, however, now thinks that the error happened locally to it; that is, any Resume clause we might ultimately execute in the error handler works entirely within the local SubA routine.

图片附件: 游客没有浏览图片的权限,请 登录注册

Figure 1-3 The call chain in action

Your code always runs in the context of some event handler; that is, any entry point into your code must ultimately be in the form of an event handler. So substituting SubA with, say, Form_Load, you could now write a catchall error handler by providing an error handler in Form_Load. Now, when SubC generates its error (I'm assuming here that these functions are only ever called from Form_Load), Visual Basic will find the local error handler in Form_Load and execute it. Ultimately, this error handler will execute a Resume statement. For argument's sake, let's say that it's Resume Next.

The Next here means after the call to SubB. OK, so what's the problem? If a problem exists, it's buried inside SubB and SubC—we don't know what they did! Imagine this scenario. Maybe SubC opened some files or perhaps a database or two, and somewhere within SubC, it was also going to close them. What happens if the erroring code happened somewhere in between these two operations—say, the files or databases got opened but were never closed? Again it depends, but loosely speaking, it means trouble.


NOTE


--------------------------------------------------------------------------------

The situation described above could be worse, however. Maybe instead of Resume Next we simply used Resume, that is, try again. This will result in an attempt to open the same files again; and as we all know, this attempt may fail for many reasons—perhaps instead of using FreeFile, you used hard-coded file handle IDs, or maybe you opened the files last time with exclusive access.

Unfortunately, when Visual Basic executes an error handler, there's no easy way of telling whether the error handler was really local to the error. So there's no way to guarantee that you handled it properly. And of course, there's no way to install a global error handler that's called automatically by Visual Basic whenever an error occurs. There's no way around it: to write professional and robust applications, we must have error handlers absolutely everywhere!


/images/2011/147787/2011051411021524.jpg" border="0" />
2007-05-22 10:31
ioriliao
Rank: 7Rank: 7Rank: 7
来 自:广东
等 级:贵宾
威 望:32
帖 子:2829
专家分:647
注 册:2006-11-30
收藏
得分:0 

--------------------------------------------------------------------------------

Tip 6: Write meaningful error logs (to a central location if possible).
By way of an example, Listing 1-3 is a typical log file entry produced by our internal application template code. No explanation is provided because most of the entry is pretty obvious.

Listing 1-3 Typical log file entry


******************************************************************
* Error Entry Start. TEST. Created 21 March 1998 19:01
******************************************************************
The Application:
----------------

C:\TMS\TEMPLATE\TEST.EXE Version 1.0.15
OS App Name C:\TMS\TEMPLATE\TEST.EXE

The Error:
----------
An error has occurred in C:\TMS\TEMPLATE\TEST.EXE - the TMS error code associated
with this error is 000053. If the problem persists, please report the error to TMS
support. The error occurred at line 100.

The error probably occurred in frmMainForm.cmd1_Click.
The standard VB error text for this error is 'File not found'.

Active Environment Variables:
-----------------------------
TMP=C:\WINDOWS\TEMP
winbootdir=C:\WINDOWS
COMSPEC=C:\COMMAND.COM
PATH=C:\WINDOWS;C:\WINDOWS\COMMAND;C:\;C:\DOS
TEMP=C:\TEMP
DIRCMD=/OGN/L
PROMPT=$e[0m[$e[1;33m$p$e[0m]$_$g
CMDLINE=WIN
windir=C:\WINDOWS

Relevant Directories: Attr:
---------------------
Windows DIR C:\WINDOWS - 16
System DIR C:\WINDOWS\SYSTEM - 16
Current DIR C:\TMS\TEMPLATE - 16
Versions:
----------
Windows - 3.95
DOS - 7.0
Mode - Enhanced
CPU - 486 or Better
COPRO - True
Windows 95 _ True (4.03.1214)B

Resources:
----------
Free Mem (Rough) 15,752 MB
Free GDI (%) 79
Free USER (%) 69
Free Handles 103

Other:
------
Threads - 64
VMs - 4
Registered Owner _ Peter J. Morris

******************************************************************************
* Error Entry End. TEST
******************************************************************************

******************************************************************************
* Stack Dump Start. TEST. Created 21 March 1998 19:01
******************************************************************************
Stack Frame: 001 of 003 AppObject - Run Called @ line 70 CheckStack
Stack Frame: 002 of 003 AppObject - CheckStack Called @ line 10 cmd1_Click
Stack Frame: 003 of 003 frmMainForm - cmd1_Click
******************************************************************************
* Stack Dump End. TEST
******************************************************************************

******************************************************************************
* DAO Errors Start. TEST. Created 21 March 1998 19:01
******************************************************************************
No Errors
******************************************************************************
* DAO Errors End. TEST
******************************************************************************


Also, see the note on stack frames later in this chapter for a fuller explanation of the Stack log entries.

The log files you write can be centralized; that is, all your applications can write to a single file or perhaps to many different files held in a central location. That "file" could be a Microsoft Jet database. Now if you log meaningful information of the same kind from different sources to a database, what have you got? Useful data, that's what! At TMS, we created a system like this once for a client. All the data gathered was analyzed in real time by another Visual Basic application and displayed on a machine in the company's support department. The application had some standard queries it could throw at the data (How's application xyz performing today?), as well as a query editor that the company could use to build its own queries on the data. (Show me all the Automation errors that occurred for user abc this year, and sort them by error code.) All the results could be graphed, too—an ability that, as is usual, allows the true nature of the data statistics to become apparent.

After a little while, it wasn't just Support that accessed this database. User education used it to spot users who were experiencing errors because of a lack of training, and developers used it to check on how their beta release was running. Remember, users are generally bad at reporting errors. Most prefer to Ctrl+Alt+Delete and try again before contacting support. By logging errors automatically, you don't need the user to report the error (sometimes incorrectly or with missing information: "Let's see, it said something about…"); it's always done by the application, and all the necessary information is logged automatically.

Figure 1-4 shows an example of the kind of output that's easy to produce from the log:

图片附件: 游客没有浏览图片的权限,请 登录注册

Figure 1-4 Graphing error log data

This chart shows how many users have produced trappable errors in various interesting applications.

Whenever we hit an error, we determine whether we're running in Visual Basic's IDE and then log or output different error text and do some other stuff differently depending on the result of that test. The reason we do this is that programmers are not end users, meaning that a programmer doesn't mind seeing "Input past end of file," but users almost always mind! If you know which context you're running in, you can easily switch messages.

The test we do internally at TMS to determine whether we're running in the IDE involves a routine called InDesign. Here's the code (the explanation follows):


Public Function InDesign() As Boolean

' ****************************************
' The only thing Debug.Assert is good for!
' ****************************************

Static nCallCount As Integer
Static bRet As Boolean ' By default this is set to False.

nCallCount = nCallCount + 1

Select Case nCallCount

Case 1: ' First time in
Debug.Assert InDesign()

Case 2: ' Second time in so we must have executed Debug.Assert...
bRet = True

End Select

' If Debug.Assert called, we need to return True to prevent the trap.
InDesign = bRet

' Reset for future calls.
nCallCount = 0

End Function
In the earlier versions of Visual Basic (previous to Visual Basic 6), InDesign used API calls to determine whether the Visual Basic IDE was kicking around. The version of InDesign (not shown here) in the first edition of our book evolved from some Visual Basic 4 code and therefore needed to cater to both the 16-bit and 32-bit worlds. We modified this code for the pure 32-bit world and replaced it with what amounts to a call to GetModuleHandle:


Private Function InDesign() As Boolean

InDesign = 0 < GetModuleHandle("VBA5.DLL")

End Function

The only problem with this code was that you needed to know the name of the DLL that implements the IDE, which in this case was VBA5.DLL. Who knew that this would be VBA6.DLL for version 6 and who knows what it will be for version 7 and so on? By the way, this code works because if the application is running under the IDE in Win32, the IDE (and its DLLs and so on) must be loaded into the same process space as the application. The DLLs of other processes cannot be seen (easily, anyway); ergo, if you can see it you must have it loaded, and if you have it loaded you must be running in the IDE.

Anyway, back to the InDesign code shown earlier. This "new" cut of the code should work for all future versions of Visual Basic (as well as for version 6). This code essentially uses the fact that Debug.Assert is coded in such a way as to make it suboptimal (an explanation of this statement follows shortly). Because the Debug object effectively goes away when you make an EXE, it follows that methods applied to it, like Print, also have no effect—in fact, they don't even run. Because the Assert method is such a method, we can make use of this disappearing act to determine whether the code is running in design mode.

The first time we call the function, which requires only a simple If InDesign Then statement, nCallCount is zero and bRet is False (initialized by default). Notice that both variables are declared as static, meaning they are accessed locally but stored globally. In other words, they are shared, persistent objects that can be accessed only within the scope of the subroutine in which they're declared. We increment nCallCount and then execute the Select Case statement. Obviously nCallCount is now 1, so the Case 1 code executes. At this point, if we're running in design mode, the Debug.Assert line causes us to reenter the routine. This time, nCallCount = nCallCount + 1 increments the static nCallCount to 2, and the Case 2 code sets bRet to True. Note that True is returned to the call made to Debug.Assert from the first entry into InDesign. Because we've asserted something that's True, we don't execute a Stop here. Instead, we return to the line of code to be executed after the call to Debug.Assert, which is the InDesign = bRet code (again). Once more we return True (because bRet is still set to True from the previous call to InDesign). This final value of True is now returned to the original caller to indicate, "Yes, we are running in design mode."

Now consider what happens if we're running as an EXE. Basically this means that the line Debug.Assert InDesign is missing from the routine. In this case, the only call made to our routine returns the state of bRet—False by default. If you're worried about the clock cycles taken here (that as an EXE we increment an integer and then set it to zero again), don't be—it's fast! If you insist, however, you can wrap the routine so that it's called just once, perhaps upon application start-up.

OK, why did I say that Debug.Assert was suboptimal? Normally assertions are used to implement what is known in the trade as a "soft if." Consider this code:


nFile = FreeFile

If FreeFile fails, what does nFile equal? Actually, like Open, FreeFile raises an exception to indicate failure (maybe it knows that return values can, and will, be ignored), but for the sake of argument let's say FreeFile returns 0. To detect this, as you should if you're building really critical applications that must cope with and recover from every possibility, expand the code to this:


nFile = FreeFile

If nFile <> 0 Then
.
.
.
End If

Adding the conditional test and the indentation complicates the code. The execution time increases due to the branch, the evaluation of both the expressions on either side of the angle brackets, and of course the comparison itself. For all we know, we may never need this code—after all, what is the probability that FreeFile will fail in real life? To test this, sanitize the code, and make it more efficient, we would use a "soft if" conditional instead of a "hard if" conditional:


nFile = FreeFile

Assert nFile <> 0

.
.
.

Here we're asserting our belief that FreeFile will never return 0. (Note that we've lost the indentation.) Now we build the application and send it out to test. If the assertion fails, the probability that we've run out of file handles surely approaches 1, whereas if it doesn't fail, the probability approaches 0. In this case, we can decide that the likelihood of failure is so remote that we can effectively ignore it. If the assertion never fails, we use conditional compilation to remove it and build the final EXE. In fact, we'd normally remove all assertions either by turning them into "hard ifs" or by removing them altogether. Never ship with assertions in your code. By the way, all of the previous was C-speak (for example, I'd do it this way in C or C++), and therein lies the rub. In Visual Basic you can't do otherwise because Debug.Assert is removed for you whenever you build an EXE. "Great," you say. "So now I must never ship with assertions in my code?" (I just said this practice was a good one, but only when you ship the final EXE.) "How do I determine if an assertion failed during a test if it's not even there?" Aha—the plot thickens. Assertions in Visual Basic seem to be there solely for the developer and not the tester, meaning they work only in the IDE environment. In other words, suboptimal. That is, of course, unless you ship the source and IDE when you go out to beta!

Back to the story. By using InDesign we can, as mentioned earlier, do things a little differently at run time depending upon whether we're running in the IDE. We at TMS usually store the result of a single call to InDesign in a property of the App object called InDesign. (We replace the real App object with our own—also called App—and set this property at application start-up.)

Another use of App.InDesign is to turn off your own error handling altogether. Now I know that Visual Basic allows you to Break On All Errors, but that's rarely useful, especially if you implement delayed error handling. Instead, use App.InDesign to conditionally turn error handling on or off:


If Not App.InDesign Then On Error GoTo ...

The reason for this is that one of the last things you want within the IDE is active error handlers. Imagine you're hitting F8 and tracing through your code. I'm sure you know what happens next—you suddenly find yourself in an error handler. What you really want is for Visual Basic to issue a Stop for you on the erroring line (which it will do by default if you're using the IDE and hit an error and don't have an error handler active). The code above causes that to happen even when your error handling code has been added. Only if you're running as an EXE will the error trap become enabled.




/images/2011/147787/2011051411021524.jpg" border="0" />
2007-05-22 10:34
ioriliao
Rank: 7Rank: 7Rank: 7
来 自:广东
等 级:贵宾
威 望:32
帖 子:2829
专家分:647
注 册:2006-11-30
收藏
得分:0 

Tip 7: Use assertions.
I've already briefed you on some stuff about assertions; here's the full scoop.

Assertions are routines (in Visual Basic) that use expressions to assert that something is or is not True. For example, you might have a line of code like this in your project:


nFile = FreeFile

So how do you know if it works? Maybe you think that it raises an exception if all your file handles are taken up. (The Help file doesn't tell you.) We wouldn't leave this to chance. What we'd do during both unit and system testing is use assertions to check our assumption that all is indeed well. We would have a line that looks like this following the line above:


Call Assert(nFile <> 0, "FreeFile")

This checks that nFile is not set to 0. Assertions are easy to use and extremely handy. They would be even better if Visual Basic had a "stringizing" preprocessor like the one that comes with most C compilers. Then it could fill in the second parameter for you with the asserted expression, like this:


Call Assert(nFile <> 0, "nFile <> 0")

Assertions should be removed at run time. They serve only for testing during development, a kind of soft error handler, if you will. (This removal could be done using the App.InDesign property described earlier.) If an assertion regularly fails during development, we usually place a real test around it; that is, we test for it specifically in code. For the preceding example, we would replace


Call Assert(nFile <> 0, "FreeFile")

with


If nFile = 0 Then
Err.Raise ????
End If

If an assertion doesn't fail regularly (or at all) during development, we remove the assertion.

If you're asking yourself, "Why isn't he using Debug.Assert?" you need to go back and read all of Tip 6.


/images/2011/147787/2011051411021524.jpg" border="0" />
2007-05-22 10:35
ioriliao
Rank: 7Rank: 7Rank: 7
来 自:广东
等 级:贵宾
威 望:32
帖 子:2829
专家分:647
注 册:2006-11-30
收藏
得分:0 

Tip 8: Don't retrofit blind error handlers.
The best error handlers are written when the routine they protect is being written. Tools that insert error handlers for you help but are not the answer. These tools can be used to retrofit semi-intelligent error handlers into your code once you're through writing—but is this a good idea? Your application will be error handler-enabled, that's for sure; but how dynamic will it be in its handling of any errors? Not very!

We rarely use any kind of tool for this purpose because in fitting a blind error handler there is little chance of adding any code that could recover from a given error situation. In other words, by fitting an error handler after the fact, you might just as well put this line of pseudocode in each routine:


On Error Condition Report Error

You're handling errors but in a blind, automated fashion. No recovery is possible here. In a nutshell, a blind error handler is potentially of little real use, although it is of course better than having no error handling at all. Think "exception" as you write the code and use automation tools only to provide a template from which to work.


/images/2011/147787/2011051411021524.jpg" border="0" />
2007-05-22 10:35
快速回复:本人毕业设计 急求vb方面 中英文对照翻译文章
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.040990 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved