Directives

[Previous] [Next]

11.1 Use If Then Else when the decision is based on one condition being True or False.

If Then is well suited for making a decision based on the evaluation of a single condition, and it's the most commonly used decision construct. If Then has the following syntax:

  If   condition   Then   statement  

or

  If   condition   Then  [  statements  ]  EndIf  

If condition evaluates to True, the statement or statements are executed. If condition evaluates to False, the statement or statements are not executed. Expressions can be simple or complex. Regardless of complexity, the expression used for condition must evaluate to True or False. For instance, all the following expressions are valid for condition .

 sngCoordinate>=8 strFirstName="Adam" blnInHere 

Although at first glance the last item doesn't appear to be a condition, remember that a condition always evaluates to either True or False. Since blnInHere refers to a Boolean variable (as denoted by its prefix), blnInHere inherently evaluates to True or False and is therefore a valid condition.

NOTE
If condition evaluates to a numerical value, Microsoft Visual Basic interprets the result as True or False; a zero value is considered False, and all nonzero numeric values are considered True.

To execute a statement or a set of statements when condition evaluates to False, use an Else statement. All statements between Else and End If are executed when condition evaluates to False. All statements between If Then and Else execute only if condition evaluates to True; never do both sets of statements execute in a single pass through an If End If construct.

Practical Applications

11.1.1 Consider using End If , even if only one statement is executed. If only one statement executes when condition evaluates to True, the statement can be put on the same line as If and End If can be omitted. However, to make the code more legible, you might consider placing the statement on its own line and closing the construct with End If .

Incorrect:

  '*Iflockingconflictswereencountered,buildthisinformation '*intothemessagestring.   If  lngLockingConflicts>0  Then  strMessage=strMessage&","&_   lngLockingConflicts&_ "contact(s)couldnotbeadded"&_ "duetolockingconflicts!" 

Correct:

  '*Iflockingconflictswereencountered,buildthisinformation '*intothemessagestring.   If  lngLockingConflicts>0  Then  strMessage=strMessage&","&lngLockingConflicts&_ "contact(s)couldnotbeaddeddueto"&_ "lockingconflicts!"  EndIf  

11.1.2 Don't assume that Visual Basic will short-circuit a compound condition. When creating an If Then decision structure, it's possible to create a compound condition composed of multiple smaller conditions. Consider the following code:

  IfNot  ((rstAlarms  IsNothing  )  Or  (rstAlarms.EOF))  Then  rstAlarms.Delete  EndIf  

This decision structure attempts to delete a record if the Recordset's EOF (end-of-file) property is False. First, the If statement makes sure that the variable rstAlarms actually contains a Recordset, and then it makes sure that the EOF property is False. This code, however, has the potential to generate a run-time error.

If rstAlarms doesn't contain a Recordset ( rstAlarms Is Nothing evaluates to True), you might think that Visual Basic wouldn't evaluate the second part of the condition, which is rstAlarms.EOF . With an Or compound condition, if either of the individual conditions evaluates to True, the entire condition evaluates to True. Therefore, with the first part of the compound condition being True ( rstAlarms Is Nothing ), there's really no need to evaluate the second part. Unfortunately, this is not how Visual Basic behaves: Visual Basic evaluates each component of a compound condition regardless of whether it's necessary. In this example, if rstAlarms indeed contains no Recordset, attempting to determine whether the Recordset's EOF property is True causes a run-time error. Some programming languages evaluate the individual conditions (from left to right) in a compound condition only until the value of the compound condition is clear. The remaining individual conditions are not evaluated and therefore cannot cause run-time errors. This behavior is called short-circuiting , and although it works nicely in some languages, relying on it in Visual Basic creates potential land mines in your code.

Incorrect:

  PublicFunction  FirstValidItemID()  AsString   '*Purpose:ReturntheItemIDofthefirstitemin '*tblPhysicalInventory. '*Returns:TheItemIDofthefirstitem,ifithasone. '*Otherwisereturns"<notvalid>".   Dim  strSQL  AsString   Dim  rstInventory  As  Recordset  Const  c_NotValid="<notvalid>"  '*OpentblPhysicalInventory.   Set  rstInventory=dbInventory.OpenRecordset_ ("tblPhysicalInventory",dbOpenForwardOnly)  '*SincetheRecordsetisForwardOnly,itwillbepositioned '*onthefirstrecordifthereareanyrecords. '*ChecktoseewhetherthefirstrecordhasanItemID.   IfNot  (rstInventory.EOF)  And  rstInventory![ItemID]<>""  Then  FirstValidItemID=rstInventory![ItemID]  Else  FirstValidItemID=c_NotValid  EndIf  PROC_EXIT:  ExitFunction   EndFunction  

Correct:

  PublicFunction  FirstValidItemID()  AsString   '*Purpose:ReturntheItemIDofthefirstitemin '*tblPhysicalInventory. '*Returns:TheItemIDofthefirstitem,ifithasone. '*Otherwisereturns"<notvalid>".   Dim  strSQL  AsString   Dim  rstInventory  As  Recordset  Const  c_NotValid="<notvalid>"  '*OpentblPhysicalInventory.   Set  rstInventory=dbInventory.OpenRecordset_ ("tblPhysicalInventory",dbOpenForwardOnly)  '*Defaultthereturnvalueas"<notvalid>".  FirstValidItemID=c_NotValid  '*SincetheRecordsetisForwardOnly,itwillbepositioned '*onthefirstrecordifthereareanyrecords. '*Checktoseewhetherthereareanyrecords.   IfNot  (rstInventory.EOF)  Then   '*Thereisarecord;returntheItemIDifithasone.   If  rstInventory![ItemID]<>""  Then  FirstValidItemID=rstInventory![ItemID]  EndIf   EndIf  PROC_EXIT:  ExitFunction   EndFunction  

11.2 Use Select Case when comparing a non-Boolean expression to a variety of possible values.

When evaluating an expression that has only two possible values (True or False), If Then is the best decision construct to use. If you must take some action when the expression evaluates to False as well as when it evaluates to True, add an Else clause. Because there's no easy way to use If Then to compare an expression to more than two possible values, however, Select Case becomes the best choice in that situation. A typical Select Case construct has the following syntax:

  SelectCase   testexpression  [  Case   expressionlist1  [  statementblock-1  ]] [  Case   expressionlist2  [  statementblock-2  ]]  [  CaseElse  [  statementblock-n  ]]  EndSelect  
NOTE
Select Case can be used in many advanced ways, including putting multiple result values on a single Case line. My intent in this chapter is simply to show you when to use it and how to use it properly.

At times, you might find that you want to perform an action only when a given condition is True. If the condition evaluates to False, you want to evaluate a second condition and execute code based on the results of this new condition. You can create quite complex decision structures by using this method. The following shows a skeleton of just such a decision construct:

  If   condition1   Then    ElseIf   condition2   Then    ElseIf   condition3   Then    Else    EndIf  

Notice that these conditions can be entirely unrelated. This is in contrast to the Select Case structure where the conditions are formed by successively comparing one (typically non-Boolean) test expression to each of the expressions in the Case statements. If you find that you are evaluating the same expression and comparing it to a variety of possible values, you should use Select Case rather than If Then ElseIf End If .

Incorrect:

  '*Setthepushedstateoftheproperoptionbuttoninthecontrol '*array.   If  rstTask![Type]="PhoneCall"  Then  optType(c_PhoneCall).Value=  True ElseIf  rstTask![Type]="Appointment"  Then  optType(c_Appointment).Value=  True ElseIf  rstTask![Type]="To-do"  Then  optType(c_Todo).Value=  True EndIf  

Correct:

  '*Setthepushedstateoftheproperoptionbuttoninthecontrol '*array.   SelectCase  rstTask![Type]  CaseIs  ="PhoneCall" optType(c_PhoneCall).Value=  True   CaseIs  ="Appointment" optType(c_Appointment).Value=  True   CaseIs  ="To-do" optType(c_Todo).Value=  True CaseElse   '*Noothervaluesareexpected.   EndSelect  
NOTE
You can use Select Case in ways that might not be immediately apparent. For instance, when you have a number of option buttons on a form, you often need to determine which of the option buttons is selected ( Value = True ). If you consider True as your test expression, you can create a fairly nifty construct to determine which option button is selected:
  '*Determinewhichoptionbuttonisselected.   SelectCaseTrue   CaseIs  =   optDragBehavior(c_CopyFile).Value   CaseIs  =optDragBehavior(c_MoveFile).Value   CaseIs  =optDragBehavior(c_CreateShortcut).Value   CaseElse   '*Therearenofurtheroptionbuttonsinthisarray.   EndSelect  

Practical Applications

11.2.1 Always include a Case Else with every Select Case construct, even when it's not needed. Generally, you design a Select Case construct such that it handles every possible value of testexpression . Case Else is useful for creating code to execute when none of your specifically expected results are encountered. However, many developers often leave out Case Else if they've included Case statements for all expected results. In general, it's a good idea to always include Case Else . If you want to ignore the results not specifically accounted for in the Case statements, simply note this in a comment in the Case Else clause. If you firmly believe that no value would cause Case Else to execute ”that is, if all possible results are accounted for in the Case statements ”consider raising an error in the Case Else clause. That way, if a value slips through ”which could happen if additional possible results are created during future development and the Select Case structure is not properly updated ”you'll know about it. Always including a Case Else clause makes your code more self-documenting and you don't force other developers to guess your intentions for handling results not specifically accounted for in Case statements.

Incorrect:

  SelectCase  m_intActiveSearchType  CaseIs  =c_ListSearch txtSearch.Text=grdListFind.Columns(c_ListFindName)  CaseIs  =c_PhoneSearch txtSearch.Text=grdPhones.Columns(c_PhoneFindName)  CaseIs  =c_PriceBookSearch txtSearch.Text=grdPriceBook.Columns(c_PriceBookFindName)  EndSelect  

Correct:

  SelectCase  m_intActiveSearchType  CaseIs  =c_ListSearch txtSearch.Text=grdListFind.Columns(c_ListFindName)  CaseIs  =c_PhoneSearch txtSearch.Text=grdPhones.Columns(c_PhoneFindName)  CaseIs  =c_PriceBookSearch txtSearch.Text=grdPriceBook.Columns(c_PriceBookFindName)  CaseElse   '*Allpossiblevaluesshouldbecovered,butjustincase  MsgBox"Unexpectedvalueencounteredfor"&_ "m_intActiveSearchTypein"&Me.Name&_ "txtSearch_KeyDown.",vbCritical  EndSelect  

11.2.2 Use an intelligible ordering sequence for all Case statements. The order of the various Case statements in a Select Case construct might seem superficial, but it's often quite important. When ordering the statements, consider speed and readability. When a Select Case statement is encountered, the Case statements are evaluated in their listed order until a condition is found to be True. In a large list of items, and when speed is the primary concern, you might consider putting the most frequently expected values at the top of the Case list. More often than not, however, speed is second in importance to readability and ease of maintenance. In these cases, put the list of items in alphabetical or numerical order, which makes it easier to debug the code and to add new values to the Case list.

Incorrect:

  SelectCase  rstBilling![Basis]  CaseIs  ="Metered" CallComputeMeteredContract  CaseIs  ="Hourly"  Call  ComputeHourlyContract  CaseIs  ="Units"  Call  ComputeUnitsContract  CaseIs  ="Incidents"  Call  ComputeIncidentsContract  EndSelect  

Correct:

  SelectCase  rstBilling![Basis]  CaseIs  ="Hourly"  Call  ComputeHourlyContract  CaseIs  ="Incidents"  Call  ComputeIncidentsContract  CaseIs  ="Metered" CallComputeMeteredContract  CaseIs  ="Units"  Call  ComputeUnitsContract  CaseElse   '*Noothervaluesareexpected.   EndSelect  

11.2.3 Don't create a Case statement that will never produce a True result. When creating Select Case structures that evaluate a numeric value, you can create Case statements that never produce a True result. This usually occurs as a result of incorrectly ordering the Case statements, causing an earlier statement to evaluate to True before a later statement is encountered. Notice how the Case Is <= 0 statement in the incorrect code below never evaluates to True. A value of less than 0 causes the preceding Case statement ( Case Is <=  5 ) to evaluate to True, stopping all further evaluations.

Incorrect:

  SelectCase  sngTaxRate  CaseIs  <=5  CaseIs  <=0  CaseIs  <=10  CaseElse   EndSelect  

Correct:

  SelectCase  sngTaxRate  CaseIs  <=0  CaseIs  <=5  CaseIs  <=10  CaseElse   EndSelect  

11.3 Use end-of-line comments to add clarity to nested decision structures.

Chapter 9, "Commenting Code," describes the proper way to comment a program in great detail. Although Chapter 9 argues that in-line comments are superior to end-of-line comments, end-of-line comments are appropriate at times ”for example, when nested decision structures significantly complicate code. In long procedures, it can be difficult to determine which end-of-construct statement ( End Select or End If ) corresponds to which beginning-of-construct statement. In these situations, use an end-of-line comment after the last statement of each decision structure to state which decision construct the closing statement belongs to.

NOTE
If you prefer, you can use end-of-line comments after the terminating statements of all your decision structures, regardless of whether they're part of a nested group .

Incorrect:

  If  otnumHours.Value<=0  Then  MsgBox"Thisisanhourlycontract.Youmustenterapositive"&_ "numberofhours",vbExclamation otnumHours.SetFocus  Else   '*Determinewhethertheuserhasenteredarate.   If  otnumRate.Value>0  Then  otnumPrice.Value=otnumHours.Value*otnumRate.Value  Else   If  otnumPrice.Value>0  Then   '*Norateentered;checkwhetheracontractpriceisset. '*Ifacontractpriceisset,figuretheratebasedonthe '*hoursentered.  otnumRate.Value=otnumPrice.Value/otnumHours.Value  EndIf   EndIf EndIf  

Correct:

  '*Makesuretheuserhasenteredapositivenumberofhours.   If  otnumHours.Value<=0  Then  MsgBox"Thisisanhourlycontract.Youmustenterapositive"&_ "numberofhours",vbExclamation otnumHours.SetFocus  Else   '*Determinewhethertheuserhasenteredarate.   If  otnumRate.Value>0  Then  otnumPrice.Value=otnumHours.Value*otnumRate.Value  Else   '*Thereisnorateentered,seeifthereisaprice.   If  otnumPrice.Value>0  Then   '*Acontractpriceisset.Figuretheratebasedonthe '*hoursentered.  otnumRate.Value=otnumPrice.Value/otnumHours.Value  EndIf   '*otnumPrice.Value>0   EndIf   '*otnumRate.Value>0   EndIf   '*otnumHours.Value<=0  

11.4 Format expressions for accurate evaluation and ease of understanding.

The evaluation of expressions is at the heart of creating decision structures. Most expressions can be written in multiple ways. Properly formatting an expression reduces the possibility of errors in your code and increases its readability. Since most expressions used in decision structures evaluate to a Boolean value (True or False), correctly working with Boolean expressions is crucial.

Practical Applications

11.4.1 Never compare a Boolean expression to True or False. This seems like a basic principle, but it's one that is often violated. Boolean values are True or False, so there's no need to compare them to True or False. The incorrect code below came from a well-respected Visual Basic magazine. Lack of a naming convention aside, this procedure suffers from a case of overcomplication. BOF and EOF are properties of the Recordset object that indicate that the Recordset is at beginning-of-file or end-of-file, respectively. Each can be either True or False. Since they are inherently Boolean values, comparing them directly to True or False makes the code cluttered and can decrease performance.

Incorrect:

  PublicFunction  IsEmptyRecordset(rs  As  Recordset)  AsBoolean  IsEmptyRecordset=((rs.BOF=  True  )  And  (rs.EOF=  True  ))  EndFunction  

Correct:

  PublicFunction  IsEmptyRecordset(rs  As  Recordset)  AsBoolean  IsEmptyRecordset=rs.BOF  And  rs.EOF  EndFunction  

11.4.2 Create Boolean variable names that reflect the positive rather than the negative. A classic case of overcomplicating a procedure is creating a Boolean variable name that reflects the negative of some condition. Basing decisions on such variables adds an unnecessary layer of complexity ”for instance, why call a variable blnNotLoaded when blnLoaded works just as well and is easier for the mind to deal with? When you work with the negative, you increase the chances for errors in your code because you might not catch problems as you write them. This isn't not like using double negatives in a sentence ”get it? If you must deal with the negative, use Not on the positive form of the variable rather than using the negative form of the variable.

Incorrect:

  Dim  blnInvalidTemplate  AsBoolean   '*Attempttoopenthetemplate.ThefunctionOpenTemplatereturns '*successorfailure.  blnInvalidTemplate=  Not  (OpenTemplate(strTemplateName))  '*Ifthetemplateisinvalid,getout.   If  blnInvalidTemplate  Then   GoTo  PROC_EXIT  EndIf  

Correct:

  Dim  blnValidTemplate  AsBoolean   '*Attempttoopenthetemplate.ThefunctionOpenTemplatereturns '*successorfailure.  blnValidTemplate=OpenTemplate(strTemplateName)  '*Ifthetemplateisinvalid,getout.   If   Not  (blnValidTemplate)  Then   GoTo  PROC_EXIT  EndIf  

11.4.3 Use parentheses in expressions for clarity, even when they're not required. Parentheses are used in algebraic expressions to override the traditional order of operations. For instance, standard order of operations dictates that multiplication takes place before addition. So, the statement Debug.Print 1 + 5 * 6 prints the value 31. To override this behavior, you use parentheses. Items in parentheses are evaluated first. For instance, Debug.Print (1 + 5) * 6 prints 36. Although you don't have to provide parentheses if you want to use the traditional order of operations, you should use them anyway to add clarity to complicated expressions.

Incorrect:

  '*Computetheheightandwidthoftheeditablearea.  m_sngEditWidth=m_sngImageWidth*m_intMagnification+1 m_sngEditHeight=m_sngImageHeight*m_intMagnification+1 

Correct:

  '*Computetheheightandwidthoftheeditablearea.  m_sngEditWidth=(m_sngImageWidth*m_intMagnification)+1 m_sngEditHeight=(m_sngImageHeight*m_intMagnification)+1 

11.4.4 Make code flow obvious. When writing decision structures, make the flow of the code as obvious as possible. Pen and paper shouldn't be required for someone to figure out your intentions. The incorrect code below was also culled from a well-known Visual Basic publication. Can you easily tell exactly what's happening here? The code is setting the Cancel parameter equal to the result of the MsgBox function. No the return value of the function is compared to vbCancel , and the result (True or False) is what's stored in the Cancel parameter. Why make the reader work so hard? The revised code performs the same function without any real loss of performance, and it's much easier to understand.

Incorrect:

  PrivateSub  Form_Unload(Cancel  AsInteger  ) Cancel=(MsgBox("QuitNow?",vbOKCancel  Or  _ vbQuestion,"ConfirmationDemo")=vbCancel)  EndSub  

Correct:

  PrivateSub  Form_Unload(Cancel  AsInteger  )  '*ChecktoseewhethertheuserclickedCancel.   If  MsgBox("QuitNow?",vbOKCancel  Or  _ vbQuestion,"ConfirmationDemo")=vbCancel  Then  Cancel=  True EndIf EndSub  

11.5 Refrain from using GoSub whenever possible.

The ability to use GoSub in code dates back to the early days of Basic, when code was linear rather than procedural. GoSub allows you to create a "pseudosubroutine." A GoSub <label> statement causes execution to jump to the specified label, which must be in the same procedure as the GoSub statement. A Return statement causes execution to return to the line following the GoSub statement. GoSubs make code difficult to read and debug. With the advent of procedure-based, event-driven code, a GoSub is rarely needed. If you find yourself writing a GoSub , ask yourself whether the code that you branch to could be handled in-line within the procedure. If not, determine whether it could be turned into a separate procedure. If it can, chances are good that creating a separate procedure is a better approach.

The one time GoSub really comes in handy is when the code in the pseudosubroutine works on a large number of variables local to the procedure. Under certain rare circumstances, the overhead and hassle of passing many local variables to another routine make GoSub a better proposition.

11.6 Use GoTo only when there are no other alternatives or when jumping to an error handler or single exit point.

Although the venerable GoTo statement has been used to force execution to a specific line in a procedure for quite some time, GoTo statements make code difficult to follow because they often redirect the execution path in unintuitive ways. GoTo is perfect, however, for jumping to a single exit point and for jumping to an error handler; otherwise, there's usually a better way to write a process than by using GoTo . Rarely, if ever, should a GoTo statement send code execution back in a procedure (using GoTo PROC_EXIT in an error handler comes to mind). Often, when a GoTo statement sends execution back, some sort of standard looping construct would be a better solution. The following code illustrates how code with a GoTo statement can be better written as a Do loop.

Incorrect:

  PrivateFunction  StripDoubleQuotes(strString  AsString  )  AsString   '*Purpose:DoublequotescauseerrorsinSQLstatements. '*Thisprocedurestripsthemfromtheenteredtext '*andreplacesthemwithsinglequotes. '*Accepts:strString-thestringinwhichtosearchfor '*doublequotes. '*Returns:Theoriginalstringwithalldoublequotesreplaced '*withsinglequotes.   OnErrorGoTo  PROC_ERR  Dim  intLocation  AsInteger Const  c_DoubleQuote="""" START_CHECK: intLocation=InStr(strString,c_DoubleQuote)  '*Determinewhetherornotadoublequotewasfound.   If  intLocation>0  Then   '*Thereisatleastonedoublequote.  Mid$(strString,intLocation,1)="'"  GoTo  START_CHECK  Else  StripDoubleQuotes=strString  EndIf  PROC_EXIT:  ExitFunction  PROC_ERR:  Call  ShowError(Me.Name,"StripDoubleQuotes",Err.Number,_  Err.Description)  GoTo  PROC_EXIT  EndFunction  

Correct:

  PublicFunction  StripDoubleQuotes(  ByVal  strString  AsString  )  AsString   '*Purpose:DoublequotescauseerrorsinSQLstatements. '*Thisprocedurestripsthemfromtheenteredtext '*andreplacesthemwithsinglequotes. '*Accepts:strString-thestringinwhichtosearchfor '*doublequotes. '*Returns:Theoriginalstringwithalldoublequotesreplaced '*withsinglequotes.   OnErrorGoTo  PROC_EXIT  Dim  intLocation  AsInteger   Dim  strText  AsString   Dim  blnQuotesFound  AsBoolean Const  c_DoubleQuote="""" blnQuotesFound=  False   '*Determinewhetherornotadoublequotewasfound.   Do  intLocation=InStr(1,strString,c_DoubleQuote)  '*Seewhetheradoublequotewasfound.   If  intLocation>0  Then  Mid$(strString,intLocation,1)="'" blnQuotesFound=  True Else  blnQuotesFound=  False   EndIf   '*Continuelookingfordoublequotesuntilnonearefound.   LoopWhile  blnQuotesFound StripDoubleQuotes=strString PROC_EXIT:  ExitFunction  PROC_ERR:  Call  ShowError(Me.Name,"StripDoubleQuotes",Err.Number,_  Err.Description)  GoTo  PROC_EXIT  EndFunction  

Practical Application

11.6.1 Use all uppercase letters for GoTo labels. GoTo statements make code hard to read. Reduce the amount of effort required to scan a procedure for GoTo labels by using all uppercase letters for those labels.

Incorrect:

  PrivateSub  lvwPhones_MouseDown(Button  AsInteger  ,Shift  AsInteger  ,_  x  AsSingle  ,y  AsSingle  )  '*Purpose:Ensurethattheitemclickedisalwaysselected, '*evenifclickedwiththerightmousebutton.   OnErrorGoTo  proc_err  '*Determinewhetherthereisalistitemwheretheuserclicked.   If  lvwPhones.HitTest(x,y)  IsNothingThen   Set  lvwPhones.SelectedItem=  Nothing Else   '*Thereisalistitemwheretheuserclicked.Selectitnow.  lvwPhones.SelectedItem=lvwPhones.HitTest(x,y)  EndIf  proc_exit:  ExitSub  proc_err:  Call  ShowError(Me.Name,"lvwPhones_MouseDown",Err.Number,_   Err.Description)  GoTo  proc_exit  EndSub  

Correct:

  PrivateSub  lvwPhones_MouseDown(Button  AsInteger  ,Shift  AsInteger  ,_ x  AsSingle  ,y  AsSingle  )  '*Purpose:Ensurethattheitemclickedisalwaysselected, '*evenifclickedwiththerightmousebutton.   OnErrorGoTo  PROC_ERR  '*Determinewhetherthereisalistitemwheretheuserclicked.   If  lvwPhones.HitTest(x,y)  IsNothingThen   Set  lvwPhones.SelectedItem=  Nothing Else   '*Thereisalistitemwheretheuserclicked.Selectitnow.  lvwPhones.SelectedItem=lvwPhones.HitTest(x,y)  EndIf  PROC_EXIT:  ExitSub  PROC_ERR:  Call  ShowError(Me.Name,"lvwPhones_MouseDown",Err.Number,_  Err.Description)  GoTo  PROC_EXIT  EndSub  


Practical Standards for Microsoft Visual Basic
Practical Standards for Microsoft Visual Basic .NET (Pro-Developer)
ISBN: 0735613567
EAN: 2147483647
Year: 2000
Pages: 57

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net