Jeff Webb, Mike McKelvy, Ronald Martinsen, Taylor Maxwell, Michael Regelski September 1995 Special Edition Using Visual Basic 4 - Chapter 22 1-56529-998-1 Computer Programming computer programming Visual Basic OLE database applications ODBC VB VBA API This book is an all-in-one reference that provides extensive coverage of every topic and technique for creating optimized and customized applications with Visual Basic.

Chapter 22

Debugging Applications That Provide Objects


Designing and writing code is only a portion of the process of creating an application. Debugging prior to release can take more time and effort than most programmers are willing to admit—or schedule. Applications that provide objects can be especially problematic, because they expose a whole new (programmatic) interface with a whole new set of problems. This chapter discusses those problems and supplies approaches for solving them.

In this chapter, you learn how to do the following:

Problems Unique to Objects

Debugging an application that uses objects internally or provides objects to other applications presents these new problems:

The following sections discuss these problems in greater detail and explain how you solve them.

Finding the Instance of an Object

When you try to place a watch on an object variable, Visual Basic displays the value “Object doesn't support this action” in the Watch window, as shown in figure 22.1.

Fig. 22.1

Visual Basic cannot watch object variables directly.

This is a huge problem, because objects might have multiple instances and you often must know which instance you are dealing with when a bug occurs. To find an instance of an object, you must use a watch expression on some property of the object. When debugging objects, it helps if you’ve defined two standard properties:

Placing a watch expression on Object.Name tells you whether you’re looking at the correct object. Checking Object.Parent helps you look backward in code to discover how the object was created. Figure 22.2 shows how to use the Name and Parent properties in the Watch window when tracing execution.

Fig. 22.2

Use object properties to determine the instance of an object.

The Name and Parent properties aren’t automatic for all objects. Programmers must define the properties in code. Although defining these properties for all objects might seem like extra work, the properties can save you much trouble when tracing through complex object interactions.

Multiple Instances of Object Applications

Visual Basic applications can have multiple instances of the same application loaded in memory at the same time. Visual Basic’s OLE Automation features, however, deal with only a single instance of any application. If you have multiple instance of an executable file, you can’t accurately predict from which instance Visual Basic will get an object, as shown in figure 22.3.

Fig. 22.3

CreateObject and GetObject functions don't discern among multiple instances of an object's application.

After getting an object from multiple instances of an application, Visual Basic always seems to use the same instance. Therefore, you can’t enumerate through the instances until you find the one that you want.

The only sure way to avoid bugs from connecting to the wrong instance of an object’s application is to limit the object’s application to a single instance. Listing 22.1 shows how to use objects provided in the SYSTEM.VPJ, from this book’s companion CD, to check whether an application is already loaded in memory. Using these lines as the startup procedure of an application that provides objects prevents more than one instance of the application from loading.

Listing 22.1 SYSTEM.VBP Checking for Loaded Applications

Sub Main()
' Use the WinAPI object in SYSTEM.VPJ
Dim WinAPI As Object
Set WinAPI = CreateObject("WinAPI.Application")
' Check if application is running.
If WinAPI.Information.IsRunning("Object Application") Then
' If it is, switch to that application.
AppActivate "Object Application"
' And end...
End
End If
' Otherwise, start as normal...
End Sub

You can’t depend on AppActivate returning an error if an application is not running; OLE Automation can start applications invisibly. You must use the Windows API calls contained in SYSTEM.VBP to detect applications started for OLE Automation reliably.

Problems with Multiple Initialization

If you are already running an application that provides objects, Visual Basic’s CreateObject function might not start a new instance of the application. Instead, the function calls the code in the running instance of the executable file to create the new object. This can cause problems if the application initializes global object variables at startup. Figure 22.4 illustrates how this problem can occur.

Fig. 22.4

If the object's application is already running, CreateObject does not run the Sub Main procedure.

In figure 22.4, the CreateObject function causes an error in the object’s application, because the object’s first Initialize event procedure released the gINI object the first time that it ran. The second Initialize event procedure can no longer get gINI, so an “Object variable not set” error occurs.

This problem isn’t limited to the use of CreateObject. Visual Basic actually has four ways to create a new instance of a Public, Creatable object within an already running application:

To avoid problems with initializing objects, don’t initialize an object’s dependent data outside of its Initialize event procedure unless you preserve that data for the life of the application. If possible, make each Public, Creatable object in your application self-contained.

Multiple Applications Can Access Global Data

In figure 22.4, both Application objects share the same address space. This can lead to other bugs, because both objects have access to the same global variables and functions. You can’t see from the other applications the global data in the object’s application, but you can affect the data through the object’s methods and properties, as shown in figure 22.5.

Fig. 22.5

All the objects that an application provides share global variables.

By causing Object1 to change a global variable that Object2 uses, Application1 can inadvertently affect Application2. This problem underscores the importance of not using global variables or procedures in applications that provide objects. Objects that must share data with their subordinate objects should do so through instances of a Private object. Figure 22.6 illustrates how you use a Private object to share data among subordinate objects.

Fig. 22.6

Create a Private data object instead of using global variables.

Using a Private data object ensures that each new top-level object has its own, private data that other top-level objects created by other applications will not affect.

An Application Can Remain Loaded Invisibly

Sometimes an object’s application can remain loaded invisibly in memory. This sometimes happens when a CreateObject or GetObject function succeeds in starting the application but fails before returning the reference to the object. This can result in a lost reference to the object; you can’t destroy the reference, because your object variable isn’t Set.

If a CreateObject or GetObject function fails while you are debugging an application, check the Windows task list to make sure that the object’s application isn’t left in memory. If it is, you can close the application from the Task List.

In compiled code, you should not call CreateObject or GetObject again when handling errors from these functions. Doing so might simply create more lost references and, eventually, fill available memory.

Multiple Applications Can Access the Same Object

If you use the following form of GetObject, Visual Basic returns a reference to an existing instance of an object:

Set objvar = GetObject(,”projectname.objectname”)

The object can’t detect that more than one application has access to it, although OLE increments its reference count and keeps the object from being destroyed until the last reference goes out of scope or is set to Nothing.

Therefore, one application can affect another through a shared object. Sometimes you want this is to happen. However, in other instances, it causes unexpected results.

Because an object has no built-in knowledge of which application will use it, you either need to trust that other programmers will be careful when getting running instances of objects, or you must to limit access to subordinate objects through some sort of password mechanism. The following code shows a method that returns a subordinate object only when the appropriate KeyValue argument is provided:

Const ACCESS_KEY = &h1234
Public Function Secure(KeyValue As Integer) As Object
If KeyValue = ACCESS_KEY Then
Dim SubordinateObject As New clsSubObj
Set Secure = SubordinateObject
End If
End Function

Problems with the Registration Database

When creating OLE Automation objects during debugging, Visual Basic seems to prefer compiled versions of the object over the object’s project loaded in another instance of Visual Basic. During debugging, an application might actually have two entries in the system registration database, as shown in figure 22.7.

Fig. 22.7a

The compiled version of the object exists at the root level; the debug version exists under the VBKeySave entry.

Fig. 22.7b

The compiled version of the object exists at the root level; the debug version exists under the VBKeySave entry.

When attempting to create the object, another application searches for the object in this order:

  1. In memory. If the object is already running, Visual Basic creates a new instance of the running object.
  2. At the root level of the registration database. Visual Basic uses the object's programmatic ID to find the object’s class ID, then finds the path to the executable file from the class ID’s entry.
  3. In the VBKeySave entry of the registration database.

To ensure that you are using the right object when debugging, close all instances of the object’s application. Make sure to check the Windows Task List to ensure that no hidden instances of the application are running. Then start the correct version of the object’s application in another instance of Visual Basic.

As an added precaution, avoid creating an executable file for an object until you have completely debugged it. This saves you much work maintaining correct system registration database entries on your system.

Strategies for Debugging Objects

Approaches to debugging are as varied and personal as driving habits. Some programmers debug their applications defensively and others are more aggressive. The following list constitutes the “rules of the road” for debugging applications that provide objects, but, like the rules of the paved road, they are subject to interpretation:

  1. Debug objects in-process.
  2. Debug objects cross-process. Use each of the following start modes:
  3. Debug multiple access cross-process, using each of the preceding start modes.
  4. Test the application before release by running through the preceding three steps, using the compiled executable on systems that simulate each of your target platforms.

Debugging In-Process

When designing an application that provides objects, consider starting it from a Sub Main procedure rather than from a form. In addition to helping you think clearly about how the application starts up, this technique gives the application a place to add some conditional code that tests the application in-process, whether it has a visual interface or not.

Because applications that provide objects shouldn’t use global variables or procedures or initialize dependent objects outside of a class’ Initialize event procedure, you should dedicate almost all the code in your Sub Main procedures to debugging the application in-process, as shown in Listing 22.2, which is from the companion CD’s OUTLINE.VPJ sample.

Listing 22.2 Use a Main Procedure for In-Process Testing

#Const DebugBuild = -1
Option Explicit
' Module-level variable.
Dim Outline As New Topic
Sub Main()
#If DebugBuild Then
Dim strLine As String, strName As String
Dim Topic As Topic
Open "org.txt" For Input As #1
Do Until EOF(1)
Line Input #1, strLine
AddLevel strLine, Outline
Loop
Close 1
strName = InputBox("Enter your name")
Set Topic = SearchTree(strName, Outline)
MsgBox "Your boss is " & Topic.Parent.Value
Set Outline = Nothing
#End If
End Sub
#If DebugBuild Then
Sub AddLevel(strLine As String, objTopic As Topic)
' If the line starts with a tab...
If Mid(strLine, 1, 1) = Chr$(9) Then
' Trim off the character and call again.
AddLevel Right(strLine, Len(strLine) - 1), _
objTopic.Topics.Item(objTopic.Topics.Count)
Else
objTopic.AddSubtopic.Value = strLine
End If
End Sub
#End If
#If DebugBuild Then
Function SearchTree(strName As String, objTopic As Topic) As Topic
Dim Item As Topic
If objTopic.Topics.Count > 0 Then
For Each Item In objTopic.Topics
If Item.Value = strName Then
Set SearchTree = Item
Else
Set SearchTree = SearchTree(strName, Item)
End If
Next Item
End If
End Function
#End If

The #Const DebugBuild = -1 setting at the beginning of the module includes all the subsequent debugging code. The code in Sub Main and the AddLevel and SearchTree procedures are designed to go through all the possible code paths for the Topic object. Placing this code in Sub Main rather than doing ad hoc testing builds a consistent debugging path that you can use cross-process and for platform testing later.

When you compile the release version of the executable file, change the DebugBuild option to 0. This prevents the debug code from being built in to the executable file, and thus saves code space.

Debugging Cross-Process

To debug an object for cross-process access, follow these steps:

  1. Check the Windows Task List and end any running instances of the object’s application.
  2. Load the object’s application project (.VBP) in Visual Basic.
  3. Choose Tools, Options. Visual Basic displays the Options properties pages (see fig. 22.8).

Fig. 22.8

The StartMode options of the Options properties pages have an effect only while debugging an application within Visual Basic.

  1. In the Options Project property page, select the Object Application from the StartMode group. Click OK.
  2. Start the application.
  3. Start another instance of Visual Basic and load the project that tests cross-process access to the previous application.
  4. Choose Tools, Options. Visual Basic displays the Options property pages (see fig. 22.9).

Fig. 22.9

The Error Trapping options of the Advanced Options property page determine which application Visual Basic stops in if an error occurs.

  1. In the Advanced Options property page, deselect the Break on All Errors check box and select the Break in Class Module check box. This selection enables you to step from the current application to the object’s application if an error occurs.
  2. Run the application loaded in step 6.

As you debug an object’s application, test the logic of your objects under different access paths. One of the most critical areas for objects is their initialization. Therefore, it is important to test the different forms of access that can result in different types of initialization.

In addition to run-time errors, you must watch for unexpected results that can result from uninitialized data or from multiple initialization of the same object. Comparing expected results to actual results is important.

Creating New Objects from a Running Application

If you created debug code in Sub Main while debugging in-process, you can use very similar code to debug cross-process access. Listing 22.3 contains procedures that show the modifications made to the in-process debug code for OUTLINE.VBP.

Listing 22.3 Debugging OUTLINE.VBP Cross-Process

Option Explicit
Sub Main()
Dim strLine As String, strName As String
Dim Outline As Object, Topic As Object
' << changed to generic Object type
Set Outline = CreateObject("outline.topic")
‘ << added CreateObject
Open "org.txt" For Input As #1
Do Until EOF(1)
Line Input #1, strLine
AddLevel strLine, Outline
Loop
Close 1
strName = InputBox("Enter your name")
Set Topic = SearchTree(strName, Outline)
MsgBox "Your boss is " & Topic.Parent.Value
Set Outline = Nothing
End Sub
Sub AddLevel(strLine As String, objTopic As Object)
' << Changed to Object type
' If the line starts with a tab...
If Mid(strLine, 1, 1) = Chr$(9) Then
' Trim off the character and call again.
AddLevel Right(strLine, Len(strLine) - 1), _
objTopic.Topics.Item(objTopic.Topics.Count)
Else
objTopic.AddSubtopic.Value = strLine
End If
End Sub
Function SearchTree(strName As String, objTopic As Object) _
As Object ' << Changed to Object type
Dim Item As Object ' << Changed to Object type
If objTopic.Topics.Count > 0 Then
For Each Item In objTopic.Topics
If Item.Value = strName Then
Set SearchTree = Item
Else
Set SearchTree = SearchTree(strName, Item)
End If
Next Item
End If
End Function

The test code in listing 22.3 uses the generic Object data type and CreateObject to avoid having to establish a reference to the object’s application. References to the object’s application type library aren’t available until the application is compiled. You should not compile object applications before debugging them for cross-process access, because creating an executable file results in registering with your system two versions of the applications.

Getting Existing Objects from a Running Application

To ensure that the way that you get an existing object from a running application works correctly, add a new procedure call before the end of your cross-process-debug Sub Main. The new procedure, GetRunningObject (see listing 22.4), should use GetObject to manipulate the object that you created earlier.

Listing 22.4 The GetRunningObject Procedure

Sub Main()
' Code omitted here...
' Call procedure that debugs GetObject access
GetRunningObject
Set Outline = Nothing
End Sub
Sub GetRunningObject()
Dim RunningObject As Object
' Get a reference to the Outline object created earlier.
Set RunningObject = GetObject(,"outline.topic")
' Add a new topic to the outline.
RunningObject.AddSubtopic.Value = "New Name"
' Search the outline for the topic.
Set RunningObject = SearchTree("New Name", RunningObject)
' Display the result
MsgBox “New name’s boss is ” & RunningObject.Name
End Sub

The GetRunningObject procedure uses the GetObject function, rather than a passed-in variable, to get an existing object reference. This tests the code path for returning and modifying an existing object.

Starting the Object’s Application

You should test starting the application by using CreateObject, because by doing so you run the object application’s Sub Main procedure. Using CreateObject with the object already running doesn’t test this path for cross-process access.

The Visual Basic documentation implies that you can start an object’s application for cross-process debugging by using CreateObject. Currently, that technique doesn’t work. You must first start the object’s application in Visual Basic before attempting to use CreateObject.

If the documentation is correct, and the product is fixed by release time, you should be able to simply stop the application started in step 5 in the preceding section, then run the cross-process test again.

However, if the documentation is wrong, you must use a compiled version of the object’s application to test whether CreateObject correctly starts the object’s application. Be sure to register the object’s application before testing with the compiled application.

Debugging Multiple Access to Objects

You can run as many instances of Visual Basic as your machine’s memory can hold. Therefore, you can debug an object’s application using two or more accessing applications at the same time. You can’t have more than two object applications running in instances of Visual Basic, however. This is because the VBKeySave entry that Visual Basic creates in the system registration database is limited to two applications.

You can use the same debug code that you used for cross-process debugging when debugging multiple access, although it is better to change the order in which object’s are used among the accessing projects. Having different orders of access is more likely to uncover conflicts among objects running multiple cross-process access down parallel paths.

Testing on Target Platforms

After completing the preceding steps, you can build your debug executable file to start testing for errors on target platforms. You should not rely on debugging performed on developer’s machines as a final test, because developers tend to have more available memory and faster processors than many of the users for whom they develop.

Try to get a couple unused machines that are representative of those used by the users for whom you develop. You might be surprised at the diversity of problems that can reveal themselves when running the same code on computers from different manufacturers. Some manufacturers are more fastidious than others at following standards and doing quality checks. You’ll soon learn which types of machines, video cards, and disk drives are “tolerant” and which ones aren’t.

If you’ve followed the steps in this chapter to this point, you probably have a pretty good code base for creating some automated testing on these platforms. If not, you should return to the section “Strategies for Debugging Objects” and start over. When testing on target platforms, you should use compiled objects and test code. If a problem develops on a particular machine, you can install Visual Basic and step through the code to find the error.

Testing on target platforms also gives you a good chance to test your Setup procedure. Object applications must be properly registered in the system registration database. As this book has frequently mentioned, getting the registration right can pose problems.

Maintaining Compatibility with Released Versions

After releasing an application that provides objects, you have a whole new problem: maintenance. Some applications just keep chugging away like old Volvos. Others seem more like MGs. Whichever type of application you’re responsible for, you don’t want to kill the driver every time that you make a few changes under the hood.

Visual Basic gives you a handy feature for maintaining compatibility with released object libraries: the Compatible Object Application text box on the Options properties pages (see fig. 22.10).

Fig. 22.10

Type the name of the released executable file to which to maintain compatibility in the Compatible OLE Server text box.

Entering a name in the Compatible OLE Server text box compares the loaded application to the existing executable file’s object library when you run or compile your application. If you’ve broken compatibility with the released version, you get a warning message.

These types of changes cause a warning to occur, because they break compatibility with released object libraries:

These aren’t the only types of changes that can break compatibility with released object libraries. Changes in behavior, memory requirements, or software requirements can be just as deadly.

Guidelines for Avoiding Bugs

You should maintain guidelines for naming variables, procedures, constants, and other user-created items in the code that you write. Naming these items in a consistent way helps you distinguish among Visual Basic keywords and items defined somewhere in code. Such guidelines are especially useful in large programs or those in which more than one user uses the code.

At first, following a set of guidelines seems to require much extra work. It quickly becomes second-nature, however. Style guidelines not only make it easier for other programmers to read your code, they also make it easier for you to remember what you wrote. Also, following guidelines can give your work a professional-looking polish. Other programmers will be jealous when they see how consistently you can produce bug-free software by doing some simple housekeeping.

The guidelines that this section presents are a good starting point and are used throughout this book in the sample code. You might choose to develop your own or follow someone else’s. Only a couple rules are absolute:

Requiring Variable Declarations (Option Explicit)

Visual Basic enables you to work in a very free-form way. You can start writing code without ever worrying about reserving space for variables or assigning the correct data types. This is a great way to start learning and enables you to begin doing useful work almost immediately. However, it also means that typos in variable names are very hard to catch.

For example, the following code does not display the message box, because the If statement misspells bAnswer. Visual Basic considers Anwser a new variable and initializes it to an empty value.

Sub SpellingError()
bAnswer = True
If bAnwser = True Then MsgBox("True")
' Oops! Message box never displays.
End Sub

If you add an Option Explicit statement to the beginning of each module, form, and class, Visual Basic checks to ensure that you have declared each variable that you use. This is an effective way to check the spelling of variable names throughout your code.

Here's the same code with an Option Explicit statement:

Option Explicit
Sub SpellingError()
Dim bAnswer As Boolean ' This line is now required.
bAnswer = True
If bAnwser = True Then MsgBox("True")
' This line causes an error when run!
End Sub

You can still have typos, but Visual Basic alerts you when you try to run the code.

Using Option Explicit is more than good form; many contracts that request software written in Visual Basic require the use of the statement.

Identifying Scope with Prefixes

Scope is the range within code where you can use a variable, constant, or procedure. Visual Basic has three levels of scope:

Scope Description Prefix
Local The variable or constant is valid only within the procedure that defines it. None
Module The variable or constant is valid only within the module that defines it. m
Global The variable or constant is valid within any module in a project. g

Local Scope

Any variable or constant defined in a procedure has local scope by default, as in the following example:

Sub LocalScope()
' Define a local variable of data type integer.
Dim iCount As Integer
For iCount = 0 to 200
' Do something...
Next iCount
End Sub

If another procedure uses the variable iCount, the variable is new and doesn’t reflect the value from the procedure LocalScope. You cannot change local variables outside the procedure.

Constants behave the same way, except that their values never change (hence their name). Note the following example:

Sub LocalScope()
' Local constants for RGB colors.
Const RED = &HFF, GREEN = &HFF00, BLUE = &HFF0000
' Following code would cause an error, so it’s commented out.
' RED = 32
End Sub

Again, other procedures can’t see the value of RED defined in the procedure LocalScope.

The names of constants are usually typed in uppercase characters. The exception to this rule is a product-defined constant that begins with a product prefix, as in xlMaximize.

When using local variables, the Dim statement is optional. Visual Basic automatically creates a variable whenever you use a word that it does not recognize, as in the following example.

Sub AutomaticVariable()
' Create a local variable that contains a string.
vAutoString = "This variable was not explicitly declared"
' Create a local variable that contains a number.
vAutoNumber = 10
End Sub

When creating a variable automatically, Visual Basic uses the default data type. Visual Basic uses the Variant data type as the default unless you change it by using the Deftype statement.

Automatic variables make it hard to catch variable name spelling errors. It is safer to turn off this feature by using the Option Explicit statement.

Module Scope

Any variable or constant defined outside a procedure has module scope by default. Listing 22.5 shows such a procedure.

Listing 22.5 A Procedure with Module Scope

' Define a module-level variable of data type Boolean (true/false)
Dim mbFlag As Boolean
' Run this procedure to see how module-level variables work.
Sub ModuleVariable()
' Assign a value to the module-level variable.
mbFlag = True
' Call another procedure to display the value of the variable
DisplayVariable
' Displays False in a message box.
MsgBox bFlag
End Sub
' This procedure is called by ModuleVariable.
Sub DisplayFlag()
' Displays True in a message box.
MsgBox mbFlag
' Change the value to False.
mbFlag = False
End Sub

The ModuleVariable and DisplayFlag procedures can see and change the mbFlag variable. If you move DisplayFlag to another module, however, this is no longer true; the procedures can see the value mbFlag only within the module that declares it.

Constants behave the same way. If you declare them outside a procedure, every procedure in that module can see them.

Global Scope

Variables and constants defined outside a procedure with the Public keyword have global scope, as in the following examples:

' Define a global variable of data type Boolean (true/false).
Public gbFlag As Boolean
' Define a global constant for the RGB color red.
Public Const gRED = &HFF

The variable gbFlag and the constant gRED are available to all procedures in all modules. Notice that you can’t use Public inside a procedure; this keeps global and module scope declarations together at the beginning of each module.

Identifying Data Types with Prefixes

Data type indicates the kinds of data that a variable can contain. Visual Basic has 12 built-in data types, which table 22.1 lists.

Table 22.1 Recommended Visual Basic Fundamental Type Prefixes

Data Type Prefix
Boolean b
Byte by
Currency c
Date/time dt
Double d
Error err
Integer i
Long l
Object obj
Single s
String str
User-defined type u
Variant v

In addition to the built-in data types, Visual Basic has many types for objects and collections. It is important to use a set of prefixes for these object types and for the types of objects that you create in your own application. Table 22.2 lists recommended prefixes for the Visual Basic object types.

Table 22.2 Recommended Visual Basic Object Variable Prefixes

Object Type Object Prefix
Form frm
MDIForm mdi
CheckBox chk
ComboBox cbo
CommandButton cmd
CommonDialog dlg
Data dat
DBCombo dbc
DBList dbl
DBGrid dbg
DirListBox dir
DriveListBox drv
FileListBox fil
Frame fra
HScrollBar hsb
Image img
Label lbl
Line lin
ListBox lst
Menu mnu
OLE ole
OptionButton opt
PictureBox pic
Shape shp
TextBox txt
Timer tmr
VScrollBar vsb

Choosing Descriptive Names

The names of variables, constants, and procedures should tell you something about them. This is especially important for procedures and for variables that refer to objects or have module or global scope.

For example, if you have a variable that refers to a button with the caption OK, you should name the variable cmdOK. A procedure that sorts arrays should be named SortArray.

Using shorter names for frequently used items is convenient. For example, iCount is a convenient name for a counter in a For...Next loop. Many programmers simply use i or j for counters in For...Next loops.

If a name seems too long, being less descriptive is better than using a series of abbreviations. Wherever you declare the item, you can always add comments to describe fully the item's use.

Difficulty naming a procedure is sometimes a good tip-off that the code is too general or complex. You might consider breaking a procedure into several smaller ones if you can't come up with a descriptive name.

Table 22.3 presents some guidelines for naming procedures.

Table 22.3 Procedure-Naming Guidelines

Good Name Poor Name Reason
iCount, i, j IndexvCounter, LoopCounter Counters that you use in For...Next and Do...Loop statement blocks should be easy to type and identify. Using Integer variables makes loops execute faster.
SortArray,Display Result, AdjustMargins DoThings, ShowIt,FormatOutputFor Printing Procedure names should be descriptive and specific. Using "It" or "Thing" is far too general. Try to use verb/noun pairs.
butOK,chrtInventory, rngSourceData Button1, chrtInv, rMyData You should name object variables consistently and cleary identify what they represent. If an object has a descriptive caption or name, use that name as part of the variable name.

Formatting Code

Use tabs to indicate the relationships among blocks of code. For constructions with a beginning and end, such as loops, indent the contents of the construction once for each level of nesting. Listing 22.6 shows a well-indented code block.

Listing 22.6 A Well-Indented Code Block

' Takes a string and reverses it.
Sub ReverseString(vInput)
' Begin body of Sub..End Sub, so indent once.
If TypeName(vInput) = "String" Then
' Begin body of If...Then, so indent again.
Dim sOutput As String, iCount As Integer
For iCount = Len(vInput) To 1 Step -1
' Begin body of For...Next loop, so indent again.
sOutput = sOutput & Mid$(vInput, iCount, 1)
Next iCount
vInput = sOutput
End If
End Sub

In listing 22.6, you can easily see where loops and conditional statements begin and end. This is critical in long passages of conditional code, such as long Select Case statements or a series of If...Then statements.

Commenting Code

Good comments are the most important step to writing code that other programmers can understand and maintain. This practice is extremely important if you want to take occasional vacations from work. Try writing comments one procedure at a time, using the following general form:

Listing 22.7 shows a block of code that demonstrates these commenting guidelines.

Listing 22.7 A Well-Commented Code Block

' (1) Procedure Description
' Adds a button to the Standard toolbar
‘ when this workbook is opened.
' Use this command line to install button:
' EXCEL.EXE VISIO.XLS
' Make sure "InsertVisioDrawing" macro is
‘ available (see VISIO.XLA).
' Written by: A. Wombat
' Revisions: None.
' The bitmap picture embedded on Sheet 1 determines
‘ the picture that appears on the button.
Sub Auto_Open()
' (2) Variable Declarations
Dim tbutCount As ToolbarButton, tbutVisio As ToolbarButton
Application.ScreenUpdating = False
' (3) Descriptive Comments (from here on)
' Check if toolbar button already exists
For Each tbutCountIn Toolbars("Standard").ToolbarButtons
If tbutCount.Name = "Insert Visio drawing" Then Exit Sub
Next tbutCount
' Add a blank toolbar button to the Standard toolbar.
Toolbars("Standard").ToolbarButtons.Add Button:=231
' Create an object variable for the button
' (it is the last button on the toolbar).
Set tbutVisio = Toolbars("Standard"). _
ToolbarButtons(Toolbars("Standard").ToolbarButtons.Count)
' Set the macro the toolbar button will run.
tbutVisio.OnAction = "InsertVisioDrawing"
' Give the button a name to display in the Tool Tip balloon.
tbutVisio.Name = "Insert Visio drawing"
' Copy the bitmap.
Sheets("Sheet1").DrawingObjects("Visio Toolbar Button"). _
CopyPicture
' Paste it into the button.
tbutVisio.PasteFace
Application.ScreenUpdating = True
' Close this workbook.
(4) Assumptions and Undones.
' UNDONE: Uncomment this line to cause
‘ workbook to automatically close.
' Makes it hard to edit this macro, though!
'ActiveWorkbook.Close
End Sub

From Here...

For more information on the following topics, see the indicated chapters:


© 1996, QUE Corporation, an imprint of Macmillan Publishing USA, a Simon and Schuster Company.