AutoCAD VBA Programming
AutoCAD VBA Programming
AutoCAD VBA Programming
Seven
Object Models
Every program that supports ActiveX automation has an object tree that
is similar to the AutoCAD object tree. The difference is that the tree for
the other application will be specific to that application just as the AutoCAD object library is dedicated to AutoCAD.
167
Autocad
VBA
PROGRAMMING
The object model in the other applications will follow the basic strategy
(figure 7.1) of having the application at the root. There will be various
properties, methods, and events associated with the application that may
interest you, but normally your first step is into the documents. Inside the
collection of documents will be the one
document the application wants to access.
Each of the applications in Office 97
has specific names for each of the levels
just described. In Microsoft Excel, a single
spreadsheet document is a work sheet.
Work sheets are contained inside of
Figure 7.1: Generic object tree
are at the document level. Inside the work sheets are cells that are the
individual data items.
When youre talking to a spreadsheet or some other automation interface system you will have to navigate through the object tree to get at
what you want. This is not as difficult as it may sound: it is just a matter
of learning the various branches and what they can do for you. The
online help and object browser can be very helpful in locating these items.
Before you can use the online browser to locate the items of interest,
you must first attach the object models you want to use. You link to an
object library through the references setting located in the Tools pulldown menu of VBAIDE. Selecting the References option in the Tools
menu brings up a menu of the available object references. You might be
surprised at the selection available. Its generally pretty obvious which
reference library to use when it comes to specific applications. For example, if you want to link in with Microsoft Excel, page through the list to
the Microsoft Excel option, then pick it. The selected object library will
now become available to your application. If more than one option is
available, pick the most recent edition (highest release number).
To manipulate the objects from the other environments, you should
have a good idea of what you want to do and how. That is, if you dont
168
know how to use the Excel spreadsheet system, you will have difficulty
understanding all that you can do with the objects that are available. It
will be worth your while to spend time learning how to manipulate the
system in its native mode in order to gain an appreciation of what the
tool can do for an application. The objects and manipulation tools
available to ActiveX are generally quite extensive, and your application
programs can take advantage of virtually every feature of the other
software system.
For you to have access to these tools they must be installed on the
computer you are using. That is, you cannot possibly access the object
model of Excel without Excel first being installed. For object models to
work, the parent task or host must be available to service the requests.
The same is true of AutoCAD and any other program of such
sophistication. The term automation means just that automation of the
tasks on your computer so it does them more quickly and efficiently.
169
Autocad
VBA
PROGRAMMING
170
user can maintain the table of data using a table-editing tool (Excel) and
does not have to work in a text editor or in the source code of an
AutoLISP routine.
The following is an example spreadsheet that has the first column as a
key name and the next two entries as scalar values. These scalars could
represent dimensions, tolerances, physical properties, or some other data
of interest to our application. The goal of this example is to access them
by name and retrieve the values from the spreadsheet, thereby
demonstrating the basic strategies of opening a spreadsheet and obtaining
information from one via VBA.
In the following spreadsheet (figure 7.3), columns are labeled with letters (A,B,C) and rows are numbered (1,2,3,4,5) just as found in Excel.
Name
Dim1
Dim2
AA-1
1.5
1.5
AA-2
2.5
1.75
AA-3
3.5
1.85
AA-4
4.0
2.0
You will create the XLS file ahead of time using normal Excel operations. The file name to use for our example is EXAMPLE1.XLS and the
sheet name will be Sheet 1, the default. You can use normal Excel
operations to define this table in the system if you want to try it yourself.
Link to Excel
The remainder of the programming takes place inside VBA in AutoCAD.
When youre starting a new project, the first step is to link the Excel
object library using the Tools-Resources menu selection with the project.
The specific library to select will vary depending on what version of Excel
you have installed. For example, the name may appear as Microsoft Excel
171
Autocad
VBA
PROGRAMMING
8.0 Object Library if you have Office 97 installed. Now our VBA program
will know how to talk with Excel. (Remember that you must have Excel
installed on your computer to access the library.)
Variable Declarations
After adding the linkage references for Excel, the next step is to start coding the interface. After inserting a new module into the project, we type
in the following declarations.
Dim ExL As Object
Dim XLWorkBook as Object
Dim excelSheet As Object
The primary advantage of using early binding is that the system can
assist you in typing in object references. As you type the variable name
used, a list box of available selections will appear. When the generic
172
object definition (late binding) is used, the system doesnt know what
kind of object you are referencing during the coding and cannot possibly
assist in the typing.
These definitions are global to our module, and you can access them by
any of the subroutines or functions we include in that module. If we
wanted to make these variables available to other modules, we would
change the Private statement to Public. This allows the variables to be
addressed as part of our module (named Module1 by default) by other
source code modules. The visibility, or scope, of variables from one module to another is covered in more detail in a later chapter.
If speed of execution is an issue, and it normally is, be aware that
running Excel from inside AutoCAD will not be as fast as if the same VBA
module were running inside Excel. Within larger applications, you may
want to have the VBA in Excel handle the Excel side of the problem and
have the VBA in AutoCAD simply launch the macro in Excel. The reason
is that the VBA code inside Excel is tightly coupled to Excel while the VBA
code running inside AutoCAD is bound at runtime with Excel. That means
that every transaction that takes place between AutoCAD and Excel must
go through a series of interfaces before getting to the other application. For
simple manipulations this will not be an issue, but when working with
more advanced applications the time savings could be significant.
VBA fills in the rest for us, adding in a new subroutine definition to
the module. The parentheses are added, as is the End Sub statement. All
we have to do is fill in the middle part. When you type in the Sub statement with the name of a new function, the code editor will contain the
following after you press the Enter key.
173
Autocad
VBA
PROGRAMMING
Sub Set_Up_Excel ()
End Sub
The following is what we filled in for the function. This function will
start Microsoft Excel with the EXAMPLE1.XLS workbook (must be
located in the default Excel document directory) and Sheet1 worksheet loaded and ready to go. Each line of the code is explained in the
text that follows. All the code is presented together for those just scanning for examples.
Sub Set_Up_Excel()
On Error Resume Next
Set ExL = GetObject(, Excel.Application)
ExL.Visible = True
If (Err.Number <> 0) Then
Err.Clear
MsgBox You must have Excel loaded on your computer!
Exit Sub
End If
Set XLWorkBook = Workbooks.Open(EXAMPLE1.XLS)
Sheets(Sheet1).Select
Set excelSheet = ExL.ActiveWorkbook.Sheets(Sheet1)
End Sub
The first part of the program will attach the Excel application object.
GetObject() is a VBA function that retrieves an application object given
the executable file name or the class name of the application. If Excel is
running, the class name Excel.Application will find a link and return
the application object for our program to use. If Excel is not running, the
GetObject() function will start Excel. If Excel is not available on the computer, the program will fail. Thats why the error handler is turned on
before the call to GetObject(). If the error handler was not enabled and
Excel was not available, the program would crash in an ugly manner.
174
This way we can control the exit situation and issue a proper error message that makes sense to the user.
By including the empty string as the first parameter to the getObject()
function, we are instructing VBA to run Excel if it is not already running
in the computer. Leaving the first parameter blank will cause an error if
an instance of Excel does not already exist in the machine.
Another way to access the workbook object is through the createObject() function. This function behaves just like getObject() except that
it will start the application. If your application knows it will be starting
Excel when it is going to run, then the createObject() function is a better
choice.
The next step is to open the workbook using the Open method of the
Workbooks object in Excel. Youll supply the complete file name, with
extension, to the Open function. No directory was specified in the example routine. The file is assumed to be in the Excel or system search path.
If its not in the search path, youll need to provide a complete path name.
The extension XLS will not be appended automatically, which means that
your application can use alternative extensions as a minimal form of data
security. The result of a successful open call is a workbook object.
Workbooks contain worksheets, so the next level into the object tree
takes our application to the specific sheet we wish to have available. The
example program selects Sheet1, the default sheet name in a new
workbook. The worksheet object is then assigned to the variable
excelSheet.
The result of running this function is that the workbook and worksheet are opened and their respective object references are stored in the
variables defined in the declaration section.
175
Autocad
VBA
PROGRAMMING
The value returned from the find method is a Range object. The
address method of the Range object is used to obtain the location of the
cell that was located and is returned as a string. When the address
method is used, a dollar sign will proceed each part of the address so that
if a match was made with column A and row 3, the result would be
$A$3. You can change the response of the address method by using the
variable parameters; however, the default approach is good enough for
our purposes.
When you are searching a table using a specific column, you know the
column value and can remove it from the result string to extract just the
row number where the match was made. In our example, we are searching
the A column for a key name match and can thus remove the first three
characters of the resulting address (the $A$ part) to obtain only the row
number (3). Now if our application wants to obtain the value of a particular column member of that row, it can access the Cells().Value directly.
The following code segment searches the open worksheet saved in the
object variable excelSheet. It searches column A between rows 1 and 10
to find a match with the value AA-3. When the match is found, the
data value found in column B is accessed and placed in the variable
Resulting_Value.
Dim Fnd As Excel.Range
Set Fnd = excelSheet.Range(A1:A10).Find(AA-3)
R$ = Fnd.Address
Rw = Val(Mid$(R$, 4))
Resulting_Value = excelSheet.Cells(Rw, 2).Value
To search the entire column labeled as A, you can specify the range as
(A:A) instead of limiting the range search to rows 1 through 10 as in
the code above. In Excel, this format is considered A1 notation. Most
spreadsheet users are used to denoting areas or ranges in the document
with A1 notation. A1 notation uses letters to represent the columns
and numbers to represent the rows. In this notation the value B5 would
refer to the fifth entry in the second column.
177
Autocad
VBA
PROGRAMMING
When using the A1 notation, you can shortcut the code entry by just
specifying the desired range inside of square brackets as in [A1:A10]
instead of Range(A1:A10). Note that the quotation marks are not
needed when defining the range using the shortcut approach.
There are other ways to denote ranges of cells inside Excel from VBA.
An easy alternative is to use the Rows and Columns properties of a worksheet. You can use these properties to define a range based on an entire
row or column. Using the column property, you would rewrite the code
as follows.
Set Fnd = excelSheet.Columns(1).Find(AA-3)
At the end of the loop, the variable R$ would either hold the value
from the second column of the row in which the AA-3 match was made
or it will be an empty string.
So which style should your application use? Use whatever makes sense
for the application at hand. The Find() method will locate a match inside
of a range faster than a direct iteration loop; however, it is limited to finding exact matches. If you need to test values to see if they fit a particular
range for a selection to be made, then the direct iteration approach will
178
179
Autocad
VBA
PROGRAMMING
strates just how simple it is to build a powerful reporting tool for AutoCAD with Excel.
The application is broken down into small modules to make the code
easier to read. The first listing contains the global variable declarations for
the module. There are two variables that we will declare as global (available to all functions and subroutines in the module), and they are a link
to the active Excel spreadsheet and an AutoCAD selection set collection.
Dim excelSheet As Object
Dim SS As AcadSelectionSet
The main program macro is called Holes and it is defined in the following listing. The Holes function calls other functions that perform the
detail operations. First the Excel object is found and linked. (Note that we
have removed the normal error checking from this example to keep the
code simple and brief.) Once the link has been established with Excel, the
function Get_Circles is called to obtain a selection set collection of all the
circles found in the drawing. The collection is then used by the
subroutine Send_Holes_To_Excel that reads each object and places the
coordinates and radius in a spreadsheet.
We sort the spreadsheet using the Sort() method of Excel. The Sort()
method implements the standard sorting available in the Excel system. In
this application we will sort by the X, Y, and Radius values in ascending
order. After the sort, the spreadsheet data is read back into the program
in sorted order. A text note is then placed at each hole indicating its position in the sorted list, and the handle is replaced with the hole number in
the spreadsheet. When this function is completed, the holes will be labeled
in AutoCAD and a supporting report is ready to be finished in Excel.
Sub holes()
Dim Excell As Object
Set Excell = GetObject(, Excel.Application)
Set excelSheet = Excell.ActiveWorkbook.Sheets.Add
Set SS = Get_Circles
Send_Holes_To_Excel
180
excelSheet.Range(A1).Sort _
key1:=excelSheet.Range(B1), _
key2:=excelSheet.Range(C1), _
key3:=excelSheet.Range(D1)
Set_Hole_Numbers
End Sub
The Sort() method can be a tad confusing at a quick glance. From the
example above, it appears as though the sort would do only a single
cellcertainly not the desired operation. Actually, the value supplied is
either a range or the first cell of a region. In this case, we are sorting a
region that is designated as starting at A1. The sort fields are defined by
assigning the parameter variables Key1, Key2, and so forth to the first
sort fields in the spreadsheet. For our example we are sorting by the X,
then Y, then the radius values. You can sort the fields in ascending (the
default) order or in descending order. To have a field, such as the Y values,
sort in descending order, use the Order2 parameter variable and set it to a
value of xlDescending. Order1 will change the sort order of the first key
field, and Order3 will change the order of the third set. There are up to
three sort fields that can be defined in the Sort() method.
Lets turn our attention to the first of the subroutines called from the
main function. The Get_Circles function will build a selection set collection of circles found in the drawing.
Function Get_Circles() As AcadSelectionSet
Dim Cir1, Cir2 As Variant
Dim CirA(0 To 0) As Integer
Dim CirB(0 To 0) As Variant
CirA(0) = 0
CirB(0) = CIRCLE
Cir1 = CirA: Cir2 = CirB
On Error Resume Next
Set Get_Circles = ThisDrawing.SelectionSets.Add(HOLES)
181
Autocad
VBA
PROGRAMMING
182
This function loops through the selection set collection (stored in global
variable SS, which was set in the main program as the result of calling
Get_Circles). Each entity is assumed to be a circle object, meaning that
certain values are known to be available such as the center point and
radius. As this subroutine reads through the selection set collection each
entity is placed in the variable Ent for processing. The first column of the
spreadsheet is set to the entity handle. Handles are the best way to link
entity objects with external data structures; we will be discussing them in
more detail later in this chapter. The second and third columns of the
spreadsheet are then set to the X and Y values of the circle center point.
The fourth column is set to the radius value found in the circle object.
The variable R holds the row number as the program adds each hole
location to the spreadsheet. After the user writes the hole location and
size information to the spreadsheet, the subroutine ends and control is
returned back to the main program.
The main program then sorts the spreadsheet as already discussed. The
sorted spreadsheet contains the data sequenced by X, Y, and Radius values.
The next subroutine will read the spreadsheet and place hole numbers at
each circle location. There are two operations that will take place as this
function iterates through the spreadsheet. The first is to place the hole
number in the drawing. The second is to replace the handle entry in the
spreadsheet with the sequential hole number.
Sub Set_Hole_Numbers()
Dim PTV As Variant
Dim PTC(0 To 2) As Double
I = 1
TH = ThisDrawing.GetVariable(TEXTSIZE)
While excelSheet.Cells(I, 1).Value <>
PTC(0) = excelSheet.Cells(I, 2).Value
PTC(1) = excelSheet.Cells(I, 3).Value
PTC(2) = 0#
ThisDrawing.ModelSpace.AddText Str$(I), PTC, TH
183
Autocad
VBA
PROGRAMMING
I = I + 1
Wend
End Sub
The function begins by obtaining the current default text size for the
drawing. This value will be used when adding the text objects for the
hole number. A While loop is started that will iterate so long as the cell in
the first column of the current row number (in variable I) has a nonblank entry. Handles are never blank, thus when the program encounters
a blank cell, it has hit the end of the list. This version of the function
doesnt do anything with the handle other than test to see if one is there.
The X and Y values for the center of the circle are retrieved from the
spreadsheet and placed into an array of doubles. This array is set into the
variant variable PTV which is needed by the addCircle() function. You
must use variants when sending and getting points from objects. The reason has to do with the way the BASIC language (as implemented in
VBA) passes parameters internally and the fact that array references are
handled better using a variant pointer.
The row number is incremented, and the loop continues until the last
hole has been read and the text placed in the drawing, at which point the
subroutine finishes.
This function set demonstrates the basics and performs a very useful
operation as well (should you need hole charts and a numbering system
for holes). VBA provides a powerful way to tie various tools together so
that each can be used in its own way to make the application dream a
reality.
Using Handles
The last example used handles but didnt really do anything with them
other than look for a blank handle indicating the end of the list in the
spreadsheet. Handles are strings that uniquely identify each object in a
drawing. AutoCAD assigns handles as the objects are created and never
reuses the same handle in the drawing. There are tools in AutoCADs
184
185
Autocad
VBA
PROGRAMMING
186
187
Autocad
VBA
PROGRAMMING
application, the more you will be able to exploit its abilities when interfacing AutoCAD with it. The reason is that the same software that
services the operator commands processes the automation tools you can
use. Objects are a powerful way for one application to link up with
another.
Dictionary Object
A dictionary is a collection of objects; it can contain any objects the
application requires. Dictionary objects are members of the dictionaries
collection, as we discussed in Chapter 6. The objects that make up a dictionary can be of our own custom design (you must use ObjectARX to
create a truly custom object). Dictionary objects can also be other AutoCAD drawing objects such as lines, arcs, and circles. In this manner, a
dictionary can be used to store a selection set between edit sessions,
much like a group but without any operator control options. Lastly,
there is an object available in AutoCAD that is found only in dictionaries
188
called an Xrecord. Dictionaries can be made up of Xrecords or any combination of entity objects.
Xrecord Objects
When interfacing with external systems or using custom design software,
Xrecords present an interesting method of storing data inside a drawing
that is not directly attached to an entity object. An Xrecord is a nongraphical object that can contain any number of parameters, just like a
graphic object. That means you can store points, strings, integers, real
numbers, and so forth. Xrecords are located using a dictionary and a key
name. The key name is a string that is stored with the Xrecord inside the
dictionary. Since your application stores the key name, it should also
know how to retrieve it for later use.
Xrecords provide a way to store the values of various variables that
might be used in an application. A program can write Xrecords containing
the values of key variables in response to a save-event taking place. That
way, when the program is started again in the same drawing at a later
time, the variable values can be retrieved.
Accessing a Dictionary
Creating a dictionary object in VBA is simply a matter of adding a new
member to the dictionary collection of the drawing. When the drawing is
saved, the dictionary is saved with it. The following code segment will
create a new dictionary object in the current drawing.
Dim Dict As AcadDictionary
Set Dict = ThisDrawing.Dictionaries.Add( My Dictionary)
Autocad
VBA
PROGRAMMING
The dictionary object can be used to reference the objects within the
dictionary. What you will find in a dictionary is entirely up to the application that maintains the dictionary. In most cases, the dictionary will
contain object references. The objects in the dictionary are accessed just
like a collection in that you use names or the index number to obtain
the reference.
For most applications the dictionary serves as a wonderful place to
store variable values and parametric data. A dictionary can also be used to
store tables of data reflecting standards in effect when the drawing was
created. As a result, the most common member of a custom dictionary is
the Xrecord. Because Xrecords can contain any data and make use of the
same numbering system as AutoCAD objects, they are very easy for most
AutoCAD programmers to use.
Xrecord Contents
An Xrecord contains variable data: one Xrecord does not have to look like
the next in the dictionary collection. The data for an Xrecord is supplied
in two arrays. The first array contains integer group codes that specify the
kind of data that will be found at the same offset in the second array. The
second array is of type variant, meaning that it can contain anything.
When accessing or writing an Xrecord, these two arrays are used. They
must be the same size, and the data contents are entirely up to the appli190
cation. The only integer codes not permitted are 5 and 105, as these are
reserved for AutoCAD handles. New Xrecords will have handles added
automatically when they are first created. All of the remaining group
codes are at the disposal of the application. That is, you can use group
code 40 over and over again for a sequence of real numbers, or you can
use 40, 41, and so forth. Whatever works best for the application.
To add an Xrecord object to a dictionary, first open the dictionary. We
recommend that you dimension objects such as dictionaries in global
memory so that they may be accessed by all modules in the application
that need to get at them. With the dictionary already open, the next step
is to add the Xrecord object to it with the AddXRecord() method. The
AddXrecord() method creates an Xrecord object that is attached to the
dictionary collection. The Xrecord data is then written to the object using
the SetXRecordData method.
To learn how an Xrecord object is created, consider the following code.
A new Xrecord object is attached to an already open dictionary object referenced by the variable Dict as in the previous code segment. The subroutine will store a real number, an integer, and a string, which are all
provided as parameters to the function. The key parameter is the key
name that will be used for accessing the Xrecord in the dictionary at some
time in the future.
Sub WriteXrecord(Key As String, R As Double, I As Integer, S As String)
Dim Xtyp(2) As Integer
Dim Xvar(2) As Variant
Xtyp(0) = 40: Xvar(0) = R
Xtyp(1) = 60: Xvar(1) = I
Xtyp(2) = 1: Xvar(2) = S
Dim Xrec As AcadXRecord
Set Xrec = Dict.AddXRecord(Key)
Xrec.SetXRecordData Xtyp, Xvar
End Sub
191
Autocad
VBA
PROGRAMMING
Reading Xrecords
To read an Xrecord you must first know the key name it was stored
under. The only alternative is to loop through the dictionary one item at
a time and examine the contents of each. In either case, the Item()
method is used with the dictionary object to obtain the Xrecord object.
From there, youll use the GetXRecordData() method to get at the group
codes and data stored inside.
<XrecordObject>.GetXRecordData() uses two variant parameters.
When the function returns, these two will reference arrays of data. The
first will contain an array of integer values, and the second will be an
array of variants.
The next code example will read the Xrecord created in the previous
example given the key name as a parameter. The values read from the
Xrecord are put back into the parameter variables to be used by the
calling program. Youll use the function GetXRecordData() method to
return the Xrecord contents given the Xrecord object reference. To obtain
the Xrecord object reference, the Item() method is applied against the
already open dictionary object Dict.
Sub ReadMyXrecord(Key As String, R As Double, I As Integer, S As String)
Dim Xtyp, XVar As Variant
Dim XR As AcadXRecord
Set XR = Dict.Item(Key)
XR.GetXRecordData Xtyp, XVar
192
In this function, the values returned from the Xrecord read are processed into variables that are passed back to the calling function. They
could just as easily been placed into global variable locations for the application or returned as a result of a function instead. The key item to
remember is that Xrecords are under the control of the application
program manipulating them. They can contain any type of data desired
and can be of any length. Note that they are not protected from other
programmers who understand how to navigate the object system of
AutoCAD, but they are well protected from the normal AutoCAD user.
10-29
Point lists.
30-39
40-49
Real number scalar values. Typically used for sizes and scale factors.
50-59
60-69
70-79
193
Autocad
VBA
PROGRAMMING
In the previous examples, the group codes used were 1 for the string,
40 for the real number, and 60 for the integer. They could just as well
have been 1, 2, 3 if the application creating them wanted the codes in
that order.
Xrecords may use group code numbers 1 through 369. You should not
use group codes 5 and 105, as they are for AutoCAD. AutoCAD will add
the entity handle information required to these codes. You may use all
other codes in any manner you desire.
Extended Data
The other way to attach data that is non-graphical to a drawing is to use
extended data. You attach extended data directly to objects in the
drawing database; these can contain numbers, points, and strings. The
only real limit to keep in mind with extended data is that an object can
have only about 16 kilobytes of data attached to it. Now, that is a lot of
data to attach to an object in a drawing, and it would not be good to
attach even half that amount to objects throughout the database. Operators will most definitely complain about the excessive disk space being
consumed.
When setting up extended data, youll use a group code system much
like the Xrecord objects with the exception that extended data group
codes are all numbered in the 1000 series. Extended data attachments
must use these group codes when attaching data to an entity object.
Code
1000
String.
1001
Application name.
1002
Control string for nested information. Open bracket for start of the
nest, close bracket for the end of it.
1003
Layer name. If the layer name is changed in the drawing, this field
will be updated to reflect that change.
1004
194
Code
1005
1010
Point.
1040
Real number.
1041
1042
1070
Integer.
1071
Long integer.
Autocad
VBA
PROGRAMMING
group code with the data type that matches the data being suppliedotherwise, the extended data will not be written properly.
When retrieving extended data from an object, youll use two variant
objects. These objects are actually arrays containing the extended data in
the same fashion as it was supplied to the object in the first place. Applications can rely on the order of group codes being preserved in extended
data, unless some other application changes them because it used the
same name.
196
As in Xrecords, extended data records use two arrays that contain the
integer group codes and variant data values that comprise the extended
data. The next section of code initializes the arrays, retrieves the CDATE
value, adds the registered application name to the drawing, and places
the extended data onto the line object.
Dim PT(1) As Integer
Dim PV(1) As Variant
Dim RR As Double
RR = ThisDrawing.GetVariable(CDATE)
PT(0) = 1001: PV(0) = CREATED
PT(1) = 1040: PV(1) = RR
ThisDrawing.RegisteredApplications.Add CREATED
Lin.SetXData PT, PV
The SetXData method is used with the line object to add the extended
data arrays to the object. Group codes 1001 and 1040 were used to hold
the registered application name and the real number value for the CDATE
system variable. If you need to store an additional real number, the group
code 1040 is used again. One problem that can come up is if multiple
applications are working with the same extended data. In those cases, it is
possible that the order of the data elements could get confused. We
highly recommend that you keep extended data grouped in small data
packs if there is a requirement for multiple applications to be messing
with the data. Each of the data packs is assigned a registered application
name, making it easy to retrieve just the data sought after.
This next section of code will locate the line with the CREATED
extended data and read the saved value for the CDATE system variable.
In order to accomplish the task, the function must first locate the line.
This is done with the selection set utility function. The function Getdate()
is presented in pieces, followed by a description of each section of code.
Function Getdate()As Double
Dim SS As AcadSelectionSet
197
Autocad
VBA
PROGRAMMING
The function starts by defining a selection set object named SS. When
you add the selection set TEMP to the selection set collection of the
drawing, an error will result if it is already in there. Thus we enable the
On Error trap to force VBA to continue in the event an error is reported.
If the error number is not 0, then VBA had a problem adding the name
TEMP to the selection set collection. That means it is already in there,
and we must use the Item() method to get it. Because selection set is
already defined, it may also contain objects. The Clear method is employed
to clear out any objects that may be in the selection set named TEMP.
At this point in the code, the variable SS is a selection set object with
nothing in it. The next step is to locate the LINE object that contains
the extended data.
function continues
Dim FT(1) As Integer, FV(1) As Variant
Dim vFT As Variant, vFV As Variant
FT(0) = 0: FV(0) = LINE
FT(1) = 1001: FV(1) = CREATED
vFT = FT: vFV = FV
SS.Select acSelectionSetAll, , , vFT, vFV
function continues
Youll use the Select method from the selection set object to locate the
desired graphics. We are interested in finding a LINE object that has
extended data attached to it. The technique involved in building the filter
198
for extended data is somewhat different in VBA compared with AutoLISP/Visual LISP. Instead of searching for a 3 group code, it searches for
the 1001 group code. The 1001 group code contains the application name
that will be exactly the same as the application name supplied in the
function that wrote the extended data in the first place. For our application, that name is CREATED.
The filter list is built by first defining two arrays. The first array contains the integer group codes for the data we are filtering. In this example
we are looking for a LINE, thus group code 0 is used. Entity names are
always associated with group code 0. The extended data application name
is represented by the 1001 value. In the second array, the values for these
variables are set. The second array is of the type Variant so that it can
hold any type of data: string, double, or integer.
Due to the way variants are stored in the VBA system, a second assignment is made for the arrays to another pair of Variant variables (vFT,
vFV). All the input elements for the selection set building function are
ready, and Select() is called on to put entities into the selection set SS.
The acSelectSetAll is an AutoCAD constant that tells the select method to
search the entire drawing database.
At the end of this section of code, the selection set variable SS now
contains a LINE object that has the extended data attached to it. If the
LINE object is not found in the drawing, the selection set will be empty.
function continues
If SS.Count > 0 Then
Dim Ent As AcadLine
Set Ent = SS.Item(0)
Ent.GetXData CREATED, vFT, vFV
GetSavedDate = vFV(1)
Else
GetSavedDate = 0#
End If
End Function
199
Autocad
VBA
PROGRAMMING
The selection set Count property tells us how many objects were found
matching the filter description we just used. If the Count is greater than
0, then the Select() method was successful in locating objects that fit our
criteria.
We know that the object we want is a line, so we define an AcadLine
object. If the extended data had been attached to any AutoCAD object, a
better choice would have been to use the AcadObject definition instead.
However, due to the way VBA works with libraries and objects, the general rule of thumb is that when you know exactly what object type will
be encountered, use that definition. This will improve the performance of
the applications.
The application looks at the first line in the selection set with Item(0).
The GetXData() method is used with that entity object to retrieve two
variants that are arrays containing the extended data. Since our application wrote only two pieces of data to the extended data, the name of the
application and the data value, we know for certain that the value saved
can be found in the second element. Thus, use vFV(1) to get the saved
value and place it in the return value of the function.
If the selection set count was 0, the function didnt find any LINE
objects with the extended data attached. At this point, the return result of
the function is set to 0.
A more advanced look at extended data manipulations is presented in
a later chapter. Extended data provides a powerful way to associate nongraphical data to existing graphical objects. There are other techniques
that can be used as well, including Xrecords with hard ownership relationships. However, these are more difficult to manage under VBA, and
most applications will find extended data to suffice.
200
C h a p t e r T w e lv e
If you dont know what some of these things are, do not despair. Some
of these are advanced topics and we will endeavor to explain them as we
get into the various nuances of the VBA programming environment.
Comments
Byte
0 to 255
Boolean
-1 or 0
Yes or No values.
Currency
-922,337,203,685,477,648 to
922,337,203,685,477,647
Date
Decimal
Variant subtype
Double
Integer
-32768 to 32676
Long
-2,147,483,648 to
2,147,483,647
289
Autocad
VBA
PROGRAMMING
Data type
Comments
Single
String
0 to 2 billion characters
Text
Variant
Most of the data types are self-explanatory, except the Variant type.
Variant is very special in VBA. It can hold any of the other data types so
you can use it as a general catch-all. There are some things to watch out
for with the Variant, however. Since it can contain any type, you might
be tempted to use it almost to the exclusion of the others. This will cause
problems such as divergent data type comparison. Other disadvantages
of using it exclusively are that it takes more storage space, and maintaining the code is more difficult because it is not obvious what type of data
you are holding.
Despite these disadvantages, there are many good uses of Variant variables. One is for passing pointers to arrays as arguments. Since VBA does
not allow you to pass an array into a subroutine or function, you pass in
a pointer to the array, and the subroutine or function can then access
the array through the pointer. Another advantage of a Variant variable is
its ability to contain a NULL or Empty value. This is very useful when
dealing with database records since the Microsoft Jet engine returns all
data in variant form. Switching it to other data types and back would be
a waste of time. Another good use is when youre working with listbox
values. A listbox value is a variant. You need to declare a variant type to
take its value and use it. If the user has not selected anything in the listbox, you could get a NULL value. You should check that you have a
value before proceeding.
When you are not sure what type of data your variant might contain
and you need to find out before processing it; you can use a special VBA
function to determine the data type of the data stored. The function is
called VarType. Pass your Variant variable to the function and it will
return the data type of the data in the variable.
290
Two special values (Null and Empty) are used with Variant variables.
To test for those special values you must use the IsNull or IsEmpty tests.
Once you get a response from the test, you can then do further tests or
process the data.
One advantage of the Null value is how it behaves with VBA
operators. When you use an operator like plus (+) with a NULL value
variable and a string or number, you get NULL as a result. But when you
use the ampersand (&) operator with a NULL value variable and a string
or number you get the string or number back. This is what allows you to
get around the problem of divergent data types that could contain NULL
values. An example is shown below.
Dim groupname As Variant
groupname = frmGroupNames.1stGroupNames.Value
#1 - this works
In the example, the Variant variable group name is loaded with the
contents of the listbox (lstGroupNames) selection. There is no guarantee
that anything is selected or that a value was passed. If you were to check
for the length of the variable and it contained NULL, the program would
stop with an error. The Len function must have a string passed to it. Line
#1 is the workaround to the problem. VBA binds the zero-length string
with the Variant variable. When the variable is NULL, it returns a zerolength string that the Len function can deal with. When the variable has
something in it, a zero-length string is added, but that does not affect the
original string. This same method can be used with database recordsets.
You can also use it with numbers instead of strings. Just remember that
you must use the ampersandnot a plus sign.
Autocad
VBA
PROGRAMMING
any Microsoft product. There are a few special VBA control keys
sequences that can really make your life easier when programming.
Finding code in your project is one of the most difficult things about
object-oriented code. Other common useful tools are the ability to shift
(indent) blocks of code and the ability to step through your code one
line at a time in debug mode. VBA has given you some shortcuts for
accomplishing these things. These control keys sequences are
Tab
Shift + Tab
{F2}
Shift + {F2}
Ctrl + Shift + {F2}
{F8}
We indent our code for maintenance purposes. It is easier to read the
code when it is lined up vertically. To indent code blocks you use the Tab
or shift + Tab key sequence. The process is simple. Just highlight the
lines of code, then use the key sequence. The Tab key moves the code
block to the right while the Shift + Tab key sequence moves the code
block to the left.
The {F2} key takes you to the Object browser covered in Chapter 3.
One of the most useful key sequences is the Shift + {F2} key sequence.
You use this to bounce around in the module definition, variable
declaration, or object browser. You place your cursor over a variable or
procedure and use the key sequence. VBA will take you to the
declaration of the variable or to the declaration of the procedure. When
you place your cursor over an object, the key sequence takes you to that
objects definition in the Object Browser.
The Ctrl + Shift + {F2} key sequence returns you to the last position of
your cursor (unless you are in the Object Browser). These features are
very handy for moving quickly around the code modules, checking that
292
you have properly declared a variable within the current scope, or seeing
an objects definition and getting back to where you started.
When debugging your code, the {F8} function key is of primary importance to you. It allows you to step into (through) your code one line at a
time. You can place a break point on a line of code near the suspect
code, and the program will halt processes at that line in a wait state.
You can then use the {F8} key to move one line at a time through the
problem area, viewing variables or watching the flow of your program.
There are other key sequences that work with {F8}. You can see them on
the Debug pull-down menu.
293
Autocad
VBA
PROGRAMMING
to the parent object are not reflected automatically in the child objects
derived from the parent. This is why VBA is not a true object-oriented
programming language; although it may seem like a small difference, it
can be important when dealing with large families of objects.
Still, you can create your own object with VBA. You can create a person
object that has properties like age, height, weight, race, religion, and any
other information you need to access about a person in a class module.
That object is then available to hold information in your program. You
need only declare the object in your procedure, then load the information
into it. Objects can be very handy when working with database recordsets.
Other uses for your own created objects include but are not limited to:
Wrapping API procedures
Wrapping Registry calls
Handling reading and writing with ASCII files
Creating new properties for forms
Defining real world components of your designs such as structural
members or survey data
Holding standard calculations used to solve engineering problems
Implementing individual client or government styles and standards
A form is a special class module VBA provided for us. It contains
collections of controls that we can use. When you create a form, you are
filling in details about the form and adding your own methods (subroutines) and properties.
To show how this all works, we have provided a small example project. Open up the Employee.dvb provided on the sample CD and call the
TestEmp module from your immediate window. The module has a stop
in it to allow you to step though the code one line at a time using the
{F8} key. You can see the values assigned to the properties of the
employee by placing your cursor over the property as you press the {F8}
key. This method of stepping through the code is also useful because you
see how the program moves through the class module code.
294
The module has hard lined an age and name into a newly created
object called employee. It does nothing more than put up a message box
with the employees name and age in it to show that the object took the
data. Look at the Employee class module to see the property and method
definitions relating to the employee object. The declarations in the class
module are the properties, and the procedures in the class module are
the methods. VBA even recognizes them and uses them in the modules
with the Intellisense feature. After you create a new instance of the
employee object in a module and set it, you can see the properties and
methods by typing in the objects name and a period (.). The Intellisense
feature of VBA shows the properties and methods defined in the class
module for employee.
'Line #1
BDate = "1/27/93"
MyStr = Format(BDate, "Long Date")
'Line #2
MyDate = #1/27/93#
MyStr = Format(MyDate, "Long Date")
'Line #3
295
Autocad
VBA
PROGRAMMING
and the Format function will handle a Variant data type containing a
string date. Line #3 also returns MyStr as the correct date with a value of
Wednesday, January 27, 1993. This is because MyDate uses the special
date characters around it, and the Format function handles a Variant
data type containing special date characters.
Collecting a date from a user on a form and converting it can cause
unpredictable results unless you know the data type of the control you
got the data from. A text box will return a string that works with the
Format function, but a listbox value returns a variant, and it might not
be in string format. It is safest to use the special date characters around
data that you collect and want to convert into the date format with the
Format function. You can see the example by opening the Dates.dvb project supplied on the CD and stepping through it by calling the PlayDate
subroutine in the Immediate window.
296
Passing in the Declared Order The default way to call the subroutine would be to call it and pass the argument in the order you
defined them. The call for this method might look like this.
Call ShowEmp (My Name, 22, #2/3/76#)
Or
ShowEmp My Name, 22, #2/3/76#
Passing by Name You can pass the arguments into the procedure by
name in any order you wish. The method is to give the name of the argument in the procedures declaration followed by a colon, equal sign, and
the value. For the same procedure call to ShowEmp listed above, the call
would look like this:
Call ShowEmp (bDate:=#2/3/76#, Name:=My Name, Age:=22)
Or
ShowEmp Age:=22, bdate:=#2/3/76#, Name:=My Name
The position of the argument does not matter when you use the
named argument method.
Optional Arguments The sample routine supplied for the two previous argument methods shows how the third optional argument method
works. Each of the examples shown will work fine even though the
height argument was not present. That is because the Optional declaration was used for the height argument. When you want to pass the
height into the routine, just add it to the list of arguments.
There is only one rule for optional arguments. They must be declared
last in the procedures declarations. You can put in as many as you wish
as long as they are last in the list. When you use the optional height
argument, your call might appear like this:
297
Autocad
VBA
PROGRAMMING
Or
ShowEmp Age:=22, bdate:=#2/3/76#, Height:=72, Name:=My Name
Notice that even though the declaration had to be last in the procedure,
the call does not have to make it last when using named arguments.
298
in C). This method simply does not work in VBA. Since there is no
pointer capability in VBA, the passed-in variant is treated as the array,
and the original array is never filled. This is because the variant is
passed-in by reference as its own variable and not as a pointer to the
array it was set equal to. This whole process exists because you cannot
pass arrays into procedures imposed by VBA on the procedures declaration list. Below we have illustrated a typical situation of passing a
Variant into a procedure. The called procedure can then use variant as
an array and fill it using subscripts. The filled array is then available in
the calling routine after the called procedure is finished.
Dim Pnt As Variant
Dim Ent As Object
Call GetEnt(Ent, Pnt)
Public Sub GetEnt(Ent As Object, Pnt As Variant)
299
Autocad
VBA
PROGRAMMING
GetAngle
GetCorner
GetDistance
GetEntity
GetOrientation
PolarPoint
TranslateCoordinates
300
colOne.Add 3
Dim i As Long
For i = 1 To colOne.Count
Debug.Print "colOne(" & i; ") = " & colOne.Item(i)
Next i
Autocad
VBA
PROGRAMMING
Pass_Collection
Pass_Declared_Collection
Pass_Undeclared_Collection
Return_Collection
You can load the project and run any of the procedures in the immediate window. The Return_Collection procedure will fail. This is done to
illustrate that Collections cannot be passed back from a function.
' #1 -
' #2 - first
302
Autocad
VBA
PROGRAMMING
Given the following code, you could access the preferences while working on the document (same level).
Dim preferences As AcadPreferences
Set preferences = ThisDrawing.Application.Preferences
'line #1
The code in the example sets the AcadPreferences object to the Preferences object by starting from the document (ThisDrawing), and moving
up the hierarchy to the application object, then down to the Preferences
object, which is at the same level as the document object. It can then
access any object under the Preferences object as shown in line #1. You
can see how the object path is going up and then back down by reviewing the object model. Using the explicit definition of the whole object
trail or using the With statement allows you to access any object in the
object model at any time.
304
Listbox Columns
Listbox columns are one of those things that you would think obvious,
but even intermediate level programmers can stumble on it the first
time. Its not hard, but columns in lists are very flexible, and you can do
many things with them. For example:
You can have up to ten columns in a listbox.
Columns can be different sizes.
Columns can be hidden.
You can specify which column will return a value on selection.
You can load data into the columns or specify a two-dimensional
array that is linked to the listbox and its columns.
You can create a column header within the listbox.
Multiple columns
Creating the columns in a listbox is easy. Simply enter how many
columns you want into the ColumnCount property of the listbox object.
Column Size
Controlling the width and visibility of the columns is not as obvious,
even though there is a ColumnWidth property on the listbox. The
ColumnWidth property uses points per inch (much like a font) to
measure the width. When you set a column position to 0, the column is
not shown. If you do not specify a width, the default is to split up the
columns into equal sizes. The minimum size is 72 points, but you can
make them smaller. A sample of column widths for a listbox with three
columns is shown below.
ColumnWidth
| 40 pt; 40 pt; 40
305
Autocad
VBA
PROGRAMMING
It should be noted that you could use inches instead of points in the
above example. Consult your VBA online help for ColumnWidths to see
a more detailed discussion of this property.
Column Visibility
To make the second column invisible the ColumnWidths property would
be set as shown below.
AddItem Method
The AddItem method allows you to add data to a single column or multiple columns in a listbox. Given a listbox called lstItemWindow with a
ColumnCount of 1, the code to load an array of data into the listbox
would look like that shown below.
306
Column(1,.ListCount - 1) = SampleArray(intI, 1)
Next intI
End With
Notice that the only difference between the single loading example
and the one for two columns is the line of code marked in italic. You
must first use the AddItem method to put the data into the first column,
then immediately use the Column property to add the next piece of data.
Just setting the listbox property equal to the array is enough to access
all of the data in the array. The Column property behaves the same way
307
Autocad
VBA
PROGRAMMING
except for the fact that it inverts the X,Y of the array to Y,X. When you
have need of this feature, you should use the column property instead of
the List property. The load would look like that shown below.
1stColumnWindow.Column = SampleArray
property
Column Headers
Column headers are available when you set the ColumnHeads property
of the listbox to True. But they will work only with a rowsources
attached. This is not a feature that can be currently used with AutoCAD.
To review the various column features, you can open the
Columns.dvb project (supplied with this book) and run the ColumnSamples macro.
Multiselect in a Listbox
There are three types of selection you can use in a listbox:
fmMultiSelectSingle to select a single item at a time.
fmMultiSelectSingle to select multiple items one at a time.
fmMultiSelectSingle to select multiple items in groups using the
shift key.
Single item selection is the default, but you can select any of the three
using the MultiSelect property of the listbox. The supplied sample project
Columns.dvb has three list boxes on the form. The left one is a single
select box, the middle one is a multiselect box, and the right one is an
extended multiselect box. To review the various multiselect features, you
can open the Columns.dvb project (supplied with this book) and run the
ColumnSamples macro. After loading some data into the boxes, try
selecting in each box to see the difference in how they work.
308
309
Autocad
VBA
PROGRAMMING
The Problem
The linkage code in VBA behaves much like a DDE linkage in VB on the
surface. There is no way to monitor whats happening on the command
line. You are stuck with jamming a command down the pipeline and
hoping for the best. To further complicate the matter, you cannot work
around the problem like you can in VB because while VBA is running,
the command you sent down the pipeline is not executed. It happens
only when VBA is done.
In VB with DDE, a common method was to stay in a loop until a semaphore was set. You would launch the code to AutoCAD, then switch to
a wait state subroutine looking for the semaphore. The launched code
would set the semaphore as the last thing it did and your wait state code
in VB would see the change and allow the program to continue. This
worked because VB was in its own memory pool, not AutoCADs, and it
did not have to close down to run the command. Because the command
will not run until the VBA macro ends, you cannot put VBA into a wait
state looking for a semaphore. It would just hang there since the semaphore would never get set because the launched code would not run
until VBA stopped. A perfect catch-22. Fortunately, there is a solution.
The Solution
The supplied project called Commands.dvb has a sendcommand macro
definition that illustrates how you can use the workaround. The workaround in VBA is to send one string to AutoCAD to be processed and
tack on the AutoLISP (command -vbarun sendcommand) line at the
end of whatever code youre sending down the pipeline. The result is
that VBA sends the command and dies. Then the command line is
executed in AutoCAD, and the last line of the stream is the call to load
the sendcommand macro again. To use this in production would mean
that you would need to save the variable state in VBA before closing
down and restore it again when starting up (a simple enough procedure
using the registry or a temporary file).
310
One problem that arises when trying to manipulate a form or its controls from a procedure that is outside the forms module is that you dont
get the benefit of the Intellisense design feature in the outside module as
you design. This means that you need to know the objects properties
and methods to access them (no hints as you type). One method of getting around this problem is to design the module inside the form procedure and then move it over to the support module. Naturally, you will
311
Autocad
VBA
PROGRAMMING
need to make adjustments after the move, but these are minor compared
with trying to work without that great Intellisense crutch.
Another thing to remember about the design mode is that the Properties window doesnt always show all of the objects properties. Some
properties are present only at runtime and are not in the window at
design time. This little fact can drive you to distraction when designing.
You know that there was a property but it is not on the list. Thats why
it is important to use the online help and the object browser.
Conclusion
Even if you are an experienced VB programmer, you should have found
one or two tips in this chapter that you didnt know before. These solutions and tips are all based on fighting through these problems directly.
We hope your adventures in VBA will be all the more enjoyable with
these road hazards and features off to the side called out in advance and
out of the way.
312