VBA has a large number of built-in functions and statements. For possible reference, Table 12-1 shows the VBA functions, and Table 12-2 shows the statements. We will take a look at a few of the more commonly used functions and statements in this chapter and the next.
Abs | CreateObject | Error | InputB | Len | PPmt | StrComp |
Array | CSng | Exp | InputBox | LenB | PV | StrConv |
Asc | CStr | FileAttr | InStr | LoadPicture | QBColor | String |
AscB | CurDir | FileDateTime | InStrB | Loc | Rate | Switch |
AscW | Cvar | FileLen | Int | LOF | RGB | SYD |
Atn | CVDate | Fix | Ipmt | Log | Right | Tab |
CBool | CVErr | Format | IRR | Ltrim | RightB | Tan |
CByte | Date | FreeFile | IsArray | Mid | Rnd | Time |
CCur | DateAdd | FV | IsDate | MidB | RTrim | Timer |
CDate | DateDiff | GetAllSettings | IsEmpty | Minute | Second | TimeSerial |
CDbl | DatePart | GetAttr | IsError | MIRR | Seek | TimeValue |
CDec | DateSerial | GetAutoServerSettings | IsMissing | Month | Sgn | Trim |
Choose | DateValue | GetObject | IsNull | MsgBox | Shell | TypeName |
Chr | Day | GetSetting | IsNumeric | Now | Sin | UBound |
ChrB | DDB | Hex | IsObject | Nper | SLN | UCase |
ChrW | Dir | Hour | Lbound | NPV | Space | Val |
CInt | DoEvents | Iif | Lcase | Oct | Spc | VarType |
CLng | Environ | IMEStatus | Left | Partition | Sqr | Weekday |
Command | EOF | Input | LeftB | Pmt | Str | Year |
Cos |
AppActivate | DefDec | Error | Kill | Open | Randomize | Set |
Beep | DefInt | Event | Let | Option Base | ReDim | SetAttr |
Call | DefLng | Exit | Line Input # | Option Compare | Rem | Static |
ChDir | DefObj | FileCopy | Load | Option Explicit | Reset | Stop |
ChDrive | DefSng | For Each...Next | Lock | Option Private | Resume | Sub |
Close | DefStr | For...Next | LSet | Print # | Return | Time |
Const | DefVar | Function | Mid | Private | RmDir | Type |
Date | DeleteSetting | Get | MidB | Property Get | RSet | Unload |
Declare | Dim | GoSub...Return | MkDir | Property Let | SavePicture | Unlock |
DefBool | Do...Loop | GoTo | Name | Property Set | SaveSetting | While...Wend |
DefByte | End | If...Then...Else | On Error | Public | Seek | Width # |
DefCur | Enum | Implements | On...GoSub | Put | Select Case | With |
DefDate | Erase | Input # | On...GoTo | RaiseEvent | SendKeys | Write # |
DefDbl |
To help simplify the exposition, we will follow Microsoft’s lead and use square brackets to indicate optional parameters. Thus, for instance, the second parameter in the following procedure is optional:
Sub ChangeFieldType(sFieldName
, [NewSize
])
Note that we have also omitted the data type declarations, which will be discussed separately.
We have been using the MsgBox
function unofficially for some time now. Let us
introduce it officially. The MsgBox
function is used to display a
message and wait for the user to respond by pushing a button. The most
commonly used syntax is:
MsgBox(prompt
[,buttons
] [,title
])
(This is not the function’s complete syntax. There are some additional optional parameters related to help contexts that you can look up in the help documentation.)
prompt
is a String parameter
containing the message to be displayed in the dialog box. Note that a
multiline message can be created by interspersing the vbCrLf
constant within the message.
buttons
is a Long parameter giving
the sum of values that specify various properties of the message box.
These properties are the number and type of buttons to display, the
icon style to use, the identity of the default button, and the
modality of the message box. (A system modal
dialog box remains on top of all currently open windows
and captures the input focus systemwide, whereas an
application modal dialog box remains on top of the application’s windows
only and captures the application’s focus.) The various values of
buttons
that we can sum are shown in Table 12-3. (They are
officially defined in the VbMsgBoxStyle
enum.)
Purpose | Constant | Value | Description |
vbOKOnly | 0 | Display OK button only | |
vbOKCancel | 1 | Display OK and Cancel buttons | |
vbAbortRetryIgnore | 2 | Display Abort, Retry, and Ignore buttons | |
vbYesNoCancel | 3 | Display Yes, No, and Cancel buttons | |
vbYesNo | 4 | Display Yes and No buttons | |
vbRetryCancel | 5 | Display Retry and Cancel buttons | |
vbCritical | 16 | Display Critical Message icon | |
vbQuestion | 32 | Display Warning Query icon | |
vbExclamation | 48 | Display Warning Message icon | |
vbInformation | 64 | Display Information Message icon | |
vbDefaultButton1 | 0 | First button is default | |
vbDefaultButton2 | 256 | Second button is default | |
vbDefaultButton3 | 512 | Third button is default | |
vbDefaultButton4 | 768 | Fourth button is default | |
Modality | vbApplicationModal | 0 | Application modal message box |
vbSystemModal | 4096 | System modal message box |
For instance, the code:
MsgBox "Proceed?", vbQuestion + vbYesNo
displays the message box shown in Figure 12-1, which includes a question-mark icon and two command buttons, labeled Yes and No.
The title
parameter is a string
expression that is displayed in the title bar of the dialog box. If we
omit this argument, then Microsoft Access will be
displayed, as in Figure
12-1.
The MsgBox
function returns
a number indicating which button was selected. These return values are
given in Table 12-4.
(They are officially defined in the VbMsgBoxResult
enum.)
The InputBox
function is designed to get input from the user. The
most commonly used (but not complete) syntax is:
InputBox(prompt
[,title
] [,default
])
where prompt
is the message
in the input box, title
is the
title for the input box, and default
is the default value that is
displayed in the text box. For instance, the code:
sName = InputBox("Enter your name.", "Name", "Albert")
produces the dialog box in Figure 12-2.
The InputBox
function
returns the string that the user enters into the text box. Thus, in
our example, the string variable sName
will
contain this string.
Note that if we want a number from the user, we can still use
the InputBox
function and simply
convert the returned string (such as "12.55"
) to a number (12.55) using the Val
function, discussed later in the
chapter.
Here are a handful of useful functions that apply to strings (both constants and variables):
Len
functionThe Len
function
returns the length of a string, that is, the number of
characters in the string. Thus, the code:
Len("January Invoice")
returns the number 15.
UCase
and LCase
functionsThese functions return an all-uppercase or all-lowercase version of the string argument. The syntax is:
UCase(string
) LCase(string
)
For instance:
MsgBox UCase("Donna")
will display the string DONNA
.
Left
, Right
, and Mid
functionsThese functions return a portion of a string. In particular:
Left(string
,number
)
returns the leftmost number
characters in string
, and:
Right(string
,number
)
returns the rightmost number
characters in string
. For
instance:
MsgBox Right("Donna Smith", 5)
displays the string Smith
.
The syntax for Mid
is:
Mid(string
,start
,length
)
This function returns the first
length
number of characters of
string
, starting at character number
start
. For instance:
Mid("Library.xls",9,3)
returns the string xls
.
If the length
parameter is missing,
as in:
Mid("Library.xls",9)
the function will return the rest of the string, starting
at start
.
InStr
functionThe syntax for this very useful function is:
InStr(Start
,StringToSearch
,StringToFind
)
The return value is the position, starting at
Start
, of the first occurrence of
StringToFind
within
StringToSearch
. If
Start
is missing, then the function
starts searching at the beginning of
StringToSearch
. For instance:
MsgBox InStr(1, "Donna Smith", "Smith")
displays the number 7
,
because "Smith"
begins at the
seventh position in the string "Donna
Smith"
.
Str
and
Val
functionsThe Str
function
converts a number to a string. For instance:
Str(123)
returns the string 123
.
Conversely, the Val
function
converts a string that represents a number into a number (so
that we can do arithmetic with it, for instance). For
example:
Val("4.5")
returns the number 4.5
and:
Val("1234 Main Street")
returns the number 1234
. Note, however, that Val
does not recognize dollar signs or
commas. Thus:
Val($12.00)
returns 0
, not 12.00
.
The Str
and Val
functions have been replaced by
the more modern type-conversion functions: CBool
, CByte
, CCur
, CDate
, CDbl
, CDec
, CInt
, CLng
, CSng
, CVar
, and CStr
. For instance, the function
CStr
converts its argument
to a string, as in:
CStr(123)
One advantage of the newer type-conversion functions over
the older Str
and Val
functions is that the new
functions are international-aware. For instance, the
CCur function converts an expression to
currency format, taking into account the particular decimal
separators, thousands separators, and other currency options
that are determined by the locale setting of the computer upon
which the function is being used.
Trim
,
LTrim
, and RTrim
functionsThe LTrim
function
removes leading spaces from a string. Similarly, RTrim
removes trailing spaces, and
Trim
removes both leading
and trailing spaces. Thus:
Trim(" extra ")
returns the string extra
.
String
and
Space
functionsThe String
function
provides a way to create a string quickly that consists of a
single character repeated a number of times. For
instance:
sText
= String(25, "B")
sets sText
to a string
consisting of 25 B
s. Also,
the Space
function returns
a string consisting of a given number of spaces. For
instance:
sText
= Space(25)
sets sText
to a string
consisting of 25 spaces.
Like
operator
and StrCmp
functionThe Like
operator is
very useful for comparing two strings. Of course, we can use the
equal sign:
string1
=string2
which is true when the two strings are identical. However,
Like
will also make a
case-insensitive comparison or allow the use of pattern
matching.
The expression:
string
Likepattern
returns True
if
string
fits
pattern
and returns False
otherwise. (Actually, the
expression can also return Null
.) We will describe
pattern
in a moment.
The type of string comparison that the Like
operator uses depends upon the
setting of the Option
Compare
statement. There are
two possibilities:
Option Compare Binary Option Compare Text
one of which should be placed in the Declarations section
of a module (in the same place as Option
Explicit
). Note that the default is
Option
Compare
Binary
.
Under Option
Compare
Binary
, string comparison is in the
order given by the ANSI character code, as shown here:
A < B < . . . < Z < a < b < . . . < z < À < . . . < Ø < à < . . . <⊘
Under Option
Compare
Text
, string comparison is based on a
case-insensitive sort order (determined by your PC’s locale
setting). This gives a sort order as shown here:
A = a < À = à < B = b < . . . < Z = z < Ø =⊘
By the way, the last item in the Text sort order is the left bracket ( [ ) character, with ANSI value 91. This is useful to know if you want to place an item last in alphabetical order—just surround it by square brackets.
The pattern-matching features of the Like
operator allow the use of
wildcard characters, character lists, or character ranges. For
example:
?
Matches any single character
*
Matches zero or more characters
#
Matches any single digit (0-9)
charlist
]Matches any single character in
charlist
charlist
]Matches any single character not in
charlist
For more details, check the VBA help file.
The StrComp
function
also compares two strings. Its syntax is:
StrComp(string1
,string2
[,compare
])
and it returns a value indicating whether
string1
is equal to, greater than, or
less than string2
. For more details,
check the VBA help file.
We’ll conclude our discussion of Access VBA functions and statements by examining a hodgepodge of language constructs that perform such tasks as evaluating objects or variables, evaluating an expression, and altering program flow based on an expression’s values.
VBA has several Is
functions that return Boolean values indicating whether a certain
condition holds. We have already discussed the IsMissing
function in connection with
optional arguments. Here are some additional Is
functions.
This function indicates whether an expression can be converted to a date. For instance, the code:
Dim x As String x = "1/1/45" Debug.Print IsDate(x)
will print True
to the
Immediate window.
This function indicates whether a variable has been initialized. For example, the code:
Dim x As Variant If IsEmpty(x) Then . . .
tests whether the variable x
is
empty.
This function is used to test whether a variable or
field is Null
(that is,
contains no data). Note that code such as:
If var = Null Then
will always return False
because most expressions that involve Null
automatically return Null
. The proper way to determine if the
variable var
is Null
is to write:
If IsNull(var) Then
Here is a typical scenario:
Dim rs As Recordset Dim s As String Set rs = CurrentDb.OpenRecordset("Names") rs.MoveFirst If Not IsNull(rs!LastName) Then s = rs!LastName . . . End If
This function indicates whether an expression can be evaluated as a number. For instance, consider the code:
Dim s As String s = "123" If IsNumeric(s) Then Debug.Print "Number"
This will print the word “Number.” However, if we change the second line to:
s = "123 Main St"
then the Debug.Print
statement will not execute.
The Immediate
If
function has
the syntax:
IIf(Expression, TruePart, FalsePart
)
If Expression
is True
, then the function returns
TruePart
. If
Expression
is False
, the function returns
FalsePart
. For instance, consider the
following code:
Dim rs As Recordset Dim s As String Set rs = CurrentDb.OpenRecordset("Names") rs.MoveFirst If Not IsNull(rs!LastName) Then s = rs!LastName) End If
This code fills a string variable with a field value. We must make a distinction between a Null and non-Null field value because the code:
s = rs!Lastname
will produce the error “Invalid use of Null” if we try to assign a Null value to a string variable.
It is very important to note that the Immediate
If
function always evaluates both
TruePart
and
FalsePart
, even though it returns only
one of them. Hence, we must be careful about undesirable side
effects. For example, the following code will produce a “Division by
Zero” error because even though the IIf
function returns
1/x
only when
x
is not equal to 0, the expression
1/x
is evaluated in all cases, including
when x
= 0:
x = 0 y = IIf(x = 0, x ^ 2, 1 / x)
The syntax of the Switch
function is:
Switch(expr1, value1, expr2, value2
, ... ,exprn, valuen
)
where exprn
and
valuen
are expressions. Note that there
need only be one expression-value pair, but the function is more
meaningful if there are at least two such pairs.
The Switch
function
evaluates each expression exprn
. When it
encounters the first True
expression, it returns the corresponding value. As with the
IIf
function, Switch
always evaluates all of the
expressions. If none of the expressions is True
, the function returns Null
. This can be tested with the
IsNull
function.
The procedure in Example 12-1 displays the type of file based on its extension: Access database, text, or dbase database.
Sub ShowFileType(FileExt As String) Dim FileType As Variant FileType = Switch(FileExt = "mdb", "Database", _ FileExt = "txt", "Text", _ FileExt = "dbf", "dBase") ' Display result If Not IsNull(FileType) Then MsgBox FileType Else MsgBox "Unrecognized type" End If End Sub
There is one subtlety in this code. Since the Switch
function can return a Null
value, we cannot assign the return
value to a String variable, as we might first try to do:
Dim FileType As String FileType = Switch(FileExt = "mdb", "Database", _ FileExt = "txt", "Text", _ FileExt = "dbf", "dBase")
This will produce an error if
FileExt
is not
"mdb"
, "txt"
, or "dbf
“, in which case we will get the very
annoying error message, “Invalid use of Null.” The solution is
to declare FileType
as a Variant, which
can hold any data type, including no data type,
which is indicated by the Null
keyword. (This issue can be avoided by using a Select
Case
statement, discussed in Chapter 13.)
This simple statement, whose syntax is:
Beep
sounds a single tone through the computer’s speakers. It can be useful (when used with restraint) if we want to get the user’s attention. However, there is a caveat: the results are dependent upon the computer’s hardware, and so the statement may not produce a sound at all! Thus, if you use this statement in your code, be sure to warn the user.
I discussed the various types of errors in Chapter 9 ,
but I have scrupulously avoided the question of how to handle runtime
errors in code. Indeed, VBA provides several tools for handling errors
(On
Error
, Resume
, the Err object,
and so on), and we could include an entire chapter on the subject in
this book.
Proper error handling is extremely important. Indeed, if you are, or intend to become, a professional application developer, then you should familiarize yourself with error-handling procedures.
On the other hand, if your intention is to produce Access VBA code for your own personal use, then the reasons for adding error-handling routines are somewhat mitigated. When an error occurs within one of your own programs, VBA will stop execution, display an error message, and highlight the offending code. This should enable you to debug the application and fix the problem. (It would be unreasonable to expect another user of your program to debug your code, however.)
Let us undertake a brief discussion of the highlights of error handling. (For more details, may I suggest my book Concepts of Object-Oriented Programming in Visual Basic, published by Springer-Verlag. It has a detailed chapter on error handling.)
The On
Error
statement
tells VBA what to do when a runtime error occurs. The most common
form of the statement is:
On Error GoTolabel
where label
is a label. For instance, consider the following
code:
Sub RecordCt( ) On Error GoTo ERR_EXAMPLE Dim rs As Recordset Set rs = CurrentDb.OpenRecordset("Name") MsgBox rs.RecordCount Exit Sub ERR_EXAMPLE: MsgBox "Error " & Err.Number & " - " & Err.Description, vbCritical Exit Sub End Sub
The purpose of this procedure is simply to display the number
of rows in a table. However, the database does not happen to have a
table called Name
. Hence, when
VBA encounters the line:
Set rs = CurrentDb.OpenRecordset("Name")
a runtime error will occur.
To deal with this possibility in a friendly manner, we add some error checking. The line:
On Error GoTo ERR_EXAMPLE
tells VBA to move execution to the label ERR_EXAMPLE
if an error does occur. The
code following this label is called the error-handling
code. If an error should occur, the next line executed is
the MsgBox
line, in which case
the dialog box in Figure
12-3 will be displayed. This message gives a description of
the error, obtained from the error object, which we discuss in the
next section.
It is important to note the:
Exit Sub
line just before the ERR_EXAMPLE
label. Without this statement,
the error-handling code will always be executed, even when there is
no error! Omitting this line is a common mistake. Note also that
labels always end with a colon.
The process of adding error-handling code to a procedure is sometimes referred to as error-trapping .
Consider the following version of the RecordCt
function:
Function RecordCt(TableName As String) As Integer On Error GoTo ERR_EXAMPLE Dim rs As Recordset Set rs = CurrentDb.OpenRecordset(TableName) RecordCt = rs.RecordCount rs.Close Exit Function ERR_EXAMPLE: RecordCt = -1 ' Indicates error rs.Close Exit Function End Function
In this case, if there is an error, the function will simply
return the value -1
, rather than
displaying a message box. This behavior is better than that of the
previous version, because in this case the calling procedure can
decide what to do.
Here is a procedure that calls RecordCt
:
Sub Main( ) On Error GoTo Err_Main Dim rc As Long rc = RecordCt("Object") If rc = -1 Then ' code here to handle error Else ' code here for no error End If Exit Sub Err_Main: MsgBox "Error " & Err.Number & " - " & Err.Description, vbCritical Exit Sub End Sub
Note that a return value of -1
is not perceived by VBA as an error at
all, so we need to handle the error using code such as:
If rc = -1 Then
What happens if we do not trap errors in a procedure?
If the procedure was not called by another procedure, but rather was called directly by the user, or if the procedure is an event procedure—that is, code that executes in response to a user manipulating a control on a form (for instance, clicking on a command button)—then VBA just displays an error message and halts the program.
However, if the procedure in which the error occurred was called by another procedure, then VBA passes the error to the calling procedure, just as though the calling procedure had caused the error.
To illustrate this, consider the following procedures:
Function RecordCt2(TableName As String) As Integer Dim rs As Recordset Set rs = CurrentDb.OpenRecordset(TableName) RecordCt2 = rs.RecordCount rs.Close End Function ' ----- Sub Main2( ) On Error GoTo Err_Main Dim rc As Long rc = RecordCt2("Objects") ' More code here Exit Sub Err_Main: MsgBox "Error " & Err.Number & " - " & Err.Description, vbCritical Exit Sub End Sub
The RecordCt2
function
has no error-trapping code. If Main2
calls RecordCt2
with a bad table name, the
error in RecordCt2
will be
passed to Main2
, whose
error-trapping code will execute. Thus, we will get an error message
from Main2
. (This may be just
fine.)
More generally, if ProcedureA calls ProcedureB, which calls ProcedureC, and so on, then an error in any one procedure will be passed up the call stack (list of procedures in reverse order of execution) until a procedure with error-handling code is encountered. If none is encountered, then VBA will issue its own error message and terminate the program.
Incidentally, you can view the call stack while in break mode by choosing Call Stack from the View menu.
The error object, denoted by
Err
, belongs to the VBA object
model. The most important properties of this object are:
The VBA error number
The name of the current VBA project
A description of the error
Thus, for instance, the line:
MsgBox "Error " & Err.Number & " - " & Err.Description, vbCritical
displays the error number and its description.
The Err object has a Clear method:
Err.Clear
that will clear all of the properties of the Err
object, setting its Number
property to 0
(which indicates the absence of an
error).
On Error GoTo 0
turns off any previous On
Error
GoTo
label
statements. Any error occurring subsequently will be handled by VBA
in its own inimitable way.
The syntax:
On Error Resume Next
tells VBA to continue executing the code immediately following
the line that caused the error. There are two important uses for
this form of On
Error
. The first is to cause VBA to ignore
an error. For instance, the code:
Sub example( ) On Error Resume Next MsgBox rs.RecordCount End Sub
will report the record count when rs
is a valid recordset and do nothing
otherwise.
Another important use for the On
Error
Resume
Next
syntax is for in-line error
checking , where we check for errors immediately following the
line that may have caused an error. For instance, another way to
handle errors in the RecordCount property is as follows:
Sub example( ) On Error Resume Next MsgBox rs.RecordCount If Err.Number <> 0 Then ' code to handle error here End If End Sub
It is also possible to include the Resume
statement in the error-handling portion of the code.
This will cause VBA to resume execution at the line that follows the
one that caused the error. Thus, the previous code is equivalent to
the following:
Sub example( ) On Error GoTo ERR_EXAMPLE MsgBox rs.RecordCount ' An error will cause execution to resume here after ' displaying an error message Exit Sub ERR_EXAMPLE: MsgBox Err.Description, vbCritical Resume Next End Sub
There are three variations on the Resume
statement:
Resume
Resume
Next
Resume
ALabel
The first version will cause VBA to resume with the line that caused the error. This is useful if your error-handling code actually repairs the error condition and you want the line that caused the original error to be executed again.
To illustrate, if the procedure in Example 12-2 encounters an
error, it branches to an error handler. This handler checks for
error number 3078, which is the “Can’t find table” error. If this is
the error, then the procedure displays a dialog box asking for a new
table name. If the user enters a new name, the Resume
statement is executed, and so the
line:
Set rs = CurrentDb.OpenRecordset(TableName)
is repeated. Note that it is vital to give the user a way out, however. This is done by letting the user leave the dialog box blank. (Incidentally, I got the correct error number 3078 by simulating the error and reading the resulting error-message dialog box.)
Function RecordCt3(TableName As String) As Integer On Error GoTo ERR_EXAMPLE Dim rs As Recordset Set rs = CurrentDb.OpenRecordset(TableName) RecordCt = rs.RecordCount rs.Close Exit Function ERR_EXAMPLE: If Err.Number = 3078 Then ' Can't find table sTable = InputBox("Can't find table " & sTable & _ ". Please enter table name again or leave blank to end.") If sTable = "" Then rs.Close TableName = sTable Exit Function Else Resume End If Else ' Unknown error MsgBox "Error " & Err.Number & " - " & Err.Description, vbCritical rs.Close Exit Function End If End Function
The third variation:
Resume ALabel
3.133.161.153