Excel VBA – > Autocad Basics

Microsoft “deprecated” VBA and then Autodesk made the VBA module not a part of their massive install, convincing just about everybody it was no longer a viable platform to write any code. I am not going to write code for other people if i have to go sit at their computer and download and install a 100 mb file and do it on every release of autocad. But two things happened. Microsoft recanted. There must be a million people worldwide who code VBA excel. And it was never necessary to download the VBA module for autocad to run VBA code anyway. The download is just the code editor. The VBA objects are there. The code editor in excel works fine and in fact better because excel can hold data such as parameters, bills of material and cut-lists. For a few years now I have been making it a point not to download the autocad VBA module just to make sure other people could run my programs without adding anything.

To begin writing your Autocad VBA code in excel – start excel. Most of the time you have to add the developer tab to the ribbon. That is done with the File tab, Options, Customize Ribbon. Add the Developer tab. Start the Visual Basic editor. When you save an excel file with visual basic code, save it with XLSM file extension. In the visual basic editor, important first step, you must add a reference for excel to see autocad programming objects. This is under Tools, References. Look for Autocad Type Library. Move it up in the list as high as it will go.


now you need a standard module to connect to autocad. insert a module, add two public variables at the top.

Public acadApp As AcadApplication
Public acadDoc As AcadDocument

start your sub procedure by typing
sub connect_acad()
and hit return

generally you will want to start autocad yourself the way you always do. if autocad is running the vba code to obtain a reference to it is

Set acadApp = GetObject(, "AutoCAD.Application")

notice we use our global variable. type this in to your sub procedure. I find this works fine but most online help shows it with a version dependent string like this.

Set acadApp = GetObject(, "AutoCAD.Application.22")
22 is the version that autocad is using to designate version 2018

to demystify this, what is GetObject, why is there a comma, where does this come from, open up the Object Browser. you can double click the word GetObject, right click, and pick Definition. if you dont find it that way, search the VBA library, top box, for GetObject –

The Object Browser shows its parent, VBA.Interaction and it shows the parameter list PathName comma Class. Right click on GetObject in the Object Browser and click Help to open MS online help.



If Autocad is not running, GetObject will return an error. The code to start autocad in VBA is CreateObject or New AcadApplication. They both work for me, just as the version independent and dependent string both seem to work equally well.

We need to handle the error, then deal with either autocad running or not. Autodesk has the logic and a file to do it here.


this file is also in the Autodesk AutoCAD 2018: ActiveX Developer’s Guide, or whatever year you have, which is file acad_aag.chm somewhere in your autocad installation. find that and also acadauto.chm and copy them where you want them.

First we have to add the essential statement
acadApp.Visible = True
otherwise we will not see any autocad.

Autodesk has the commands and logic but not the best form. We have to make some improvements. The code to start autocad needs to be in its own sub procedure with the code to reference the active drawing. We want to call connect_acad once and we want to obtain a global variable for the active drawing.

When autocad opens, it probably opens to a blank drawing, or maybe its already open to a drawing, but we have to get that drawing into a VBA object. Generally I want it to be a blank drawing because I am going to use it. The connect_acad sub procedure will not run until we want it to, so it just gets the activedocument which is a property of AcadApplication. Those are our two global variables, the application and the active drawing, and active drawing is the main one we use.

Put connect_acad in your module as a stand-alone. Anytime you want to write a program in excel vba to draw or interact with autocad, the first thing in the program is

call connect_acad

at that point you have a global variable AcadDoc that you use for all of autocad’s vba objects.

here is the full acad_connect

Option Explicit
 Public acadApp As AcadApplication
 Public acadDoc As AcadDocument
 Sub Connect_Acad()
    On Error Resume Next
    Set acadApp = Interaction.GetObject(, "AutoCAD.Application")
    'Set acadApp = Interaction.GetObject(, "AutoCAD.Application.22")
    'both statements above behave without any discernible difference
    If Err Then
        Debug.Print "ERROR " & Err.Number
        Debug.Print Err.Description
        Debug.Print "starting autocad"
         Set acadApp = New AcadApplication
         'Set acadApp = Interaction.CreateObject("AutoCAD.Application.22")
         'both statements above behave without any discernible difference
        'essential statement
        acadApp.Visible = True
        If Err Then
             MsgBox Err.Description
             Exit Sub
        End If
    End If
    Debug.Print "Now running " + acadApp.Name + " version " + acadApp.Version
    Set acadDoc = acadApp.ActiveDocument
        If acadDoc Is Nothing Then
            Set acadDoc = acadApp.Documents.Add
        End If
    If acadDoc.ActiveSpace = 0 Then
       acadDoc.ActiveSpace = 1
    End If
End Sub

in bricscad, try these (i dont have bricscad loaded currently but these worked a couple releases back)

Set acadApp = GetObject(, "BricscadApp.AcadApplication")

Set acadApp = New AcadApplication

using autodesk’s example to draw a single line would be modified like this. I made no other changes except adding connect_acad

Sub testline()

Call Connect_Acad

Dim lineObj As AcadLine
Dim startPoint(0 To 2) As Double
Dim endPoint(0 To 2) As Double
startPoint(0) = 1
startPoint(1) = 1
startPoint(2) = 0
endPoint(0) = 5
endPoint(1) = 5
endPoint(2) = 0

Set lineObj = acadDoc.ModelSpace.AddLine(startPoint, endPoint)

End Sub

Using this method, any code on autodesk help website that uses Thisdrawing.object can be run from excel by adding connect_acad at the top and replacing the word Thisdrawing with AcadDoc.


Specific method for parametric drawing programs

The hard part of coding parametric drawing program whether in lisp or VBA is managing the large number of points. The program turns into many lines of hard to read data apparently randomly named. A sketch has to be made with points labeled and equations or formulas entered. It all might make sense during the coding, but probably won’t a few weeks later when a change has to be made even if the sketch(s) is found. It won’t be obvious how the points are calculated or why lines are drawn from pt7 to pt21 to pt3. There is no one right way but I have recently worked on both lisp and vba programs and have some specific but not comprehensive suggestions. This is a special theory for creating the xy data but not a general theory for the entire program.

There are two basic ways to manage your drawing subroutine. It can accept points or xy data. Try both ways. Both methods need xy data.

In Lisp I use Visual Lisp objects rather than the “command” method. The object method can draw directly in to a block definition, and it can directly change the layer property. It requires a point object, but that can be created and passed as a parameter or the xy data can be passed and the point created in the subroutine.

For lisp I made a point creation routine and passed points to the subroutine which runs the Addline method.

(defun pt ( x y ) (vlax-3d-point x y 0))

In a very simple box example this gets called as

(setq pt1 (pt 0 0) pt2 (pt L 0) pt3 (pt L W) pt4 (pt 0 W))

Then the line routine would be

(defun linep (pt1 pt2 obj lyr / lineobj)
(setq lineobj (vla-AddLine obj pt1 pt2))
(vla-put-layer lineobj lyr) )

And be called as

(linep pt1 pt2 ms "hidden")

Or you could pass xy data

(defun line (x1 y1 x2 y2 obj lyr / pt1 pt2 lineobj)
(setq pt1 (vlax-3d-point x1 y1 0)
pt2 (vlax-3d-point x2 y2 0))
(setq lineobj (vla-AddLine obj pt1 pt2))
(vla-put-layer lineobj lyr) )

In VBA every variable has to be declared previous to use, so you might lean towards passing xy data. Assume you want to draw a notched rectangle and make it a polyline. You make a sub specifically for this purpose. After setting the xy data coordinates, any six vertex closed polyline can be drawn with

Call p6_box(x1, y1, x2, y1, x2, y2, x3, y2, x3, y3, x1, y3)

Sub p6_box(p1 As Double, p2 As Double, p3 As Double, p4 As Double, p5 As Double, p6 As Double, _
p7 As Double, p8 As Double, p9 As Double, p10 As Double, p11 As Double, p12 As Double)

Dim objent As AcadLWPolyline
Dim pt(0 To 11) As Double
pt(0) = p1: pt(1) = p2
pt(2) = p3: pt(3) = p4
pt(4) = p5: pt(5) = p6
pt(6) = p7: pt(7) = p8
pt(8) = p9: pt(9) = p10
pt(10) = p11: pt(11) = p12
Set objent = acadDoc.ModelSpace.AddLightWeightPolyline(pt)
objent.Closed = True
Set obj_Acad_Entity = objent
End Sub

This makes no sense without a sketch but the sub p6_box can draw any closed polyline with 6 points configured any way you need it.


Our notched box is L X W with an A X B notch, drawn with the lower left corner at 0,0. There are 3 X coordinates and 3 Y coordinates.
X1=0 , X2=L-B , X3=L
Y1=0 , Y2=A , Y3=W

You can turn this box around any way you wish, move the notch to the middle, put a hole in the middle. Just label xy coordinates as needed in order from the origin. This is how you organize your xy data without duplication in a straightforward way. Sometimes its convenient to also label points, sometimes its not required, but the xy data must always be figured from the parameters as the first step.

In VBA we would probably draw in counterclockwise order.

Call p6_box(x1, y1, x2, y1, x2, y2, x3, y2, x3, y3, x1, y3)

Now it should make sense. The xydata starts at the origin. Subroutines can be written so declared point variables are not required, or required. If you have a lot of sub-routines, just declare your x1, x2, etc as public to avoid re-declaring.

In programming 101 they strongly suggest that your subroutines be simple and single purpose. Just about every autocad parametric program I have seen or written has been a mess at the actual geometry creation level. For instance in this example, the parameters A and B, L and W may need to have complicated formulas behind them. Put those upstream of the actual sub-routine that draws the geometry. Make the geometry creation as simple as possible. Pass the actual parameters if possible, do not develop them. Interface is top down thinking, but geometry is bottom up.
Such as

Sub draw_notch_box(W As Double, L As Double, A As Double, B As Double)
x1 = 0
x2 = L - B
x3 = L
y1 = 0
y2 = A
y3 = W
Call p6_box(x1, y1, x2, y1, x2, y2, x3, y2, x3, y3, x1, y3)
End Sub

You will be able to read that next year if you remember that xy data starts at the origin.

Make Block with ActiveX

code in VBA and Visual Lisp for comparison,
connect, establish application and document objects, draw line in modelspace, add a new block, draw line in the block, insert the block.

Public acadApp As AcadApplication
Public acadDoc As AcadDocument
Public mspace As AcadModelSpace

Sub demo()

'autocad is running and doc open
Set acadApp = GetObject(, "AutoCAD.Application")
Set acadDoc = acadApp.ActiveDocument
Set mspace = acadDoc.modelspace

Dim lineobj As AcadLine
Dim pt1(0 To 2) As Double, pt2(0 To 2) As Double
pt1(0) = 2: pt1(1) = 1: pt1(2) = 0
pt2(0) = 6: pt2(1) = 7: pt2(2) = 0

'Set lineobj = acadDoc.modelspace.AddLine(pt1, pt2)
Set lineobj = mspace.AddLine(pt1, pt2)

Dim pt3(0 To 2) As Double
pt3(0) = 0: pt3(1) = 0: pt3(2) = 0

Dim blks As AcadBlocks
Set blks = acadDoc.blocks

Dim blkdef As AcadBlock
Set blkdef = blks.Add(pt3, "Blk_Demo2")

Set lineobj = blkdef.AddLine(pt1, pt2)
Set lineobj = blkdef.AddLine(pt2, pt3)

Dim blkref As AcadBlockReference
Set blkref = mspace.InsertBlock(pt3, "Blk_Demo2", 1, 1, 1, 0)

End Sub

   (setq acadApp (vlax-get-acad-object))

   (setq acadDoc (vla-get-ActiveDocument acadApp))

   (setq mSpace (vla-get-ModelSpace acadDoc))

   (setq pt1 (vlax-3d-point 1 1 0)
         pt2 (vlax-3d-point 7 5 0))
    (setq lineObj (vla-AddLine mSpace pt1 pt2))

    (setq pt3 (vlax-3d-point 0 0 0))

    (setq blks (vla-get-blocks acadDoc))

    (setq blkdef (vla-Add blks pt3 "Blk_Demo1"))
    (setq lineobj (vla-AddLine blkdef pt1 pt2))
    (setq lineobj (vla-AddLine blkdef pt2 pt3))

    (setq blkref (vla-insertblock mspace pt3 "Blk_Demo1" 1 1 1 0 ))

side by side,

this is an intuitively different process than creating a block by manual drafting methods. the graphic methods such as addline are the same for drawing in modelspace and drawing in blockspace. modelspace is a type of block, autodesk tells us. we create a block with nothing in it, then use the object returned just as we obtain the modelspace object to put objects in modelspace.

this example is very similar to the autodesk activeX Insertblock help page. http://help.autodesk.com/view/OARX/2018/ENU/?guid=GUID-17F86FDD-B7FC-4F43-9F16-B4958F73A66D

AutoLisp 2.5

about 1986 R.C. Bradlee published a Xeroxed page book “Programming Autocad” which featured the Autolisp command set as of autocad version 2.5. Selection Sets had just been added. Entity names could be retrieved and properties modified but new entitities had to be added with the Command function (command “Line” pnt1 …). This would be a good starter section for learning the language, not overwhelming but not trivial. Some or most of the links seem to paste in from the Autodesk 2017 help reference. I rearranged and edited the entries into my own idea of fuzzy categories. A disclaimer – this is not an official list of Lisp 2.5 but just my reconstruction. Some functions i left out either inadvertently, or i assume they are obsolete, or simply i dont see a need for them.

Descriptions below all belong to Autodesk. Here is there notice cut / pasted from their help page.

Except where otherwise noted, this (Autodesk) work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.  Please see the Autodesk Creative Commons FAQ for more information.

Math and booleans are first and easiest to master.

+ (add)  (+ [number number …]) Returns the sum of all numbers
– (subtract)  (- [number number …]) Subtracts the second and following numbers from the first and returns the difference
* (multiply)  (* [number number …]) Returns the product of all numbers
/ (divide)  (/ [number number …]) Divides the first number by the product of the remaining numbers and returns the quotient
1+ (increment)  (1+ number) Returns the argument increased by 1 (incremented)
1- (decrement)  (1- number) Returns the argument reduced by 1 (decremented)
abs  (abs number) Returns the absolute value of the argument
atan  (atan num1 [num2]) Returns the arctangent of a number in radians
cos  (cos ang) Returns the cosine of an angle expressed in radians
exp  (exp number) Returns the constant e (a real) raised to a specified power (the natural antilog)
expt  (expt base power) Returns a number raised to a specified power
fix  (fix number) Returns the conversion of a real into the nearest smaller integer
float  (float number) Returns the conversion of a number into a real
gcd  (gcd int1 int2) Returns the greatest common denominator of two integers
log  (log number) Returns the natural log of a number as a real
max  (max [number number …]) Returns the largest of the numbers given
min  (min [number number …]) Returns the smallest of the numbers given
rem  (rem [num1 num2 …]) Divides the first number by the second, and returns the remainder
sin  (sin ang) Returns the sine of an angle as a real expressed in radians
sqrt  (sqrt number) Returns the square root of a number as a real
~ (bitwise NOT)  (~ int) Returns the bitwise NOT (1’s complement) of the argument
logand  (logand [int int …]) Returns the result of the logical bitwise AND of a list of integers
logior  (logior [int int …]) Returns the result of the logical bitwise inclusive OR of a list of integers
lsh  (lsh [int numbits]) Returns the logical bitwise shift of an integer by a specified number of bits
atom  (atom item) Verifies that an item is an atom
boundp  (boundp sym) Verifies whether a value is bound to a symbol
listp  (listp item) Verifies that an item is a list
not  (not item) Verifies that an item evaluates to nil
null  (null item) Verifies that an item is bound to nil
numberp  (numberp item) Verifies that an item is a real or an integer
minusp  (minusp number) Verifies that a number is negative
zerop  (zerop number) Verifies that a number evaluates to zero
= (equal to)  (= numstr [numstr …]) Returns T if all arguments are numerically equal, and returns nil otherwise
/= (not Equal to)  (/= numstr [numstr …]) Returns T if the arguments are not numerically equal, and nil if the arguments are numerically equal
> (greater than)  (< numstr [numstr …]) Returns T if each argument is numerically less than the argument to its right, and returns nil otherwise
>= (greater than or equal to)  (<= numstr [numstr …]) Returns T if each argument is numerically less than or equal to the argument to its right, and returns nil otherwise
< (less than)  (> numstr [numstr …]) Returns T if each argument is numerically greater than the argument to its right, and returns nil otherwise
<= (less than or equal to)  (>= numstr [numstr …]) Returns T if each argument is numerically greater than or equal to the argument to its right, and returns nil otherwise
and  (and [expr …]) Returns the logical AND of a list of expressions
boole  (boole func int1 [int2 …]) Serves as a general bitwise Boolean function
eq  (eq expr1 expr2) Determines whether two expressions are identical
equal  (equal expr1 expr2 [fuzz]) Determines whether two expressions are equal
or  (or [expr …]) Returns the logical OR of a list of expressions
angtos  (angtos angle [mode [precision]]) Converts an angular value in radians into a string
ascii  (ascii string) Returns the conversion of the first character of a string into its ASCII character code (an integer)
atof  (atof string) Returns the conversion of a string into a real
atoi  (atoi string) Returns the conversion of a string into an integer
chr  (chr integer) Returns the conversion of an integer representing an ASCII character code into a single-character string
itoa  (itoa int) Returns the conversion of an integer into a string
rtos  (rtos number [mode [precision]]) Converts a number into a string

Lists and Loops contain the core LIST Processing commands

type  (type item) Returns the type of a specified item
eval  (eval expr) Returns the result of evaluating an AutoLISP expression
quote  (quote expr) Returns an expression without evaluating it
repeat  (repeat int [expr …]) Evaluates each expression a specified number of times, and returns the value of the last expression
while  (while testexpr [expr …]) Evaluates a test expression, and if it is not nil, evaluates other expressions; repeats this process until the test expression evaluates to nil
if  (if testexpr thenexpr [elseexpr]) Conditionally evaluates expressions
cond  (cond [(test result …) …]) Serves as the primary conditional function for AutoLISP
foreach  (foreach name lst [expr …]) Evaluates expressions for all members of a list
progn  (progn [expr …]) Evaluates each expression sequentially, and returns the value of the last expression
command  (command [arguments] …) Executes an AutoCAD command
defun  (defun sym ([arguments] [/variables …]) expr … ) Defines a function
setq  (setq sym1 expr1 [sym2 expr2 …]) Sets the value of a symbol or symbols to associated expressions
getvar  (getvar “varname”) Retrieves the value of an AutoCAD system variable
setvar  (setvar “varname” value) Sets an AutoCAD system variable to a specified value
load  (load filename [onfailure]) Evaluates the AutoLISP expressions in a file
*error*  (*error* string) A user-definable error-handling function
list  (list [expr …]) Takes any number of expressions and combines them into one list
length  (length lst) Returns an integer indicating the number of elements in a list
last  (last lst) Returns the last element in a list
nth  (nth n lst) Returns the nth element of a list
reverse  (reverse lst) Returns a list with its elements reversed
car  (car lst) Returns the first element of a list
cdr  (cdr lst) Returns the specified list, except for the first element of the list
cons  (cons new-first-element lst) The basic list constructor
append  (append lst …) Takes any number of lists and runs them together as one list
member  (member expr lst) Searches a list for an occurrence of an expression and returns the remainder of the list, starting with the first occurrence of the expression
assoc  (assoc item alist) Searches an association list for an element and returns that association list entry
apply  (apply function lst) Passes a list of arguments to a specified function
mapcar  (mapcar function list1 … listn) Returns a list of the result of executing a function with the individual elements of a list or lists supplied as arguments to the function
lambda  (lambda arguments expr …) Defines an anonymous function


Strings and File I/O are pretty straighforward

read  (read [string]) Returns the first list or atom obtained from a string
strcat  (strcat [string1 [string2 …]) Returns a string that is the concatenation of multiple strings
strlen  (strlen [string …]) Returns an integer that is the number of characters in a string
substr  (substr string start [length]) Returns a substring of a string
vl-string-subst  (vl-string-subst new-str pattern string [start-pos]) Substitutes one string for another, within a string
open  (open filename mode) Opens a file for access by the AutoLISP I/O functions
read-char  (read-char [file-desc]) Returns the decimal ASCII code representing the character read from the keyboard input buffer or from an open file
read-line  (read-line [file-desc]) Reads a string from the keyboard or from an open file
write-char  (write-char num [file-desc]) Writes one character to the screen or to an open file
write-line  (write-line string [file-desc]) Writes a string to the screen or to an open file
close  (close file-desc) Closes an open file


The last category is Autocad graphic interfacing commands. ( I am leaving out a few things I think may be less useful or obsolete or inadvertently)

angle  (angle pt1 pt2) Returns an angle in radians of a line defined by two endpoints
distance  (distance pt1 pt2) Returns the 3D distance between two points
inters  (inters pt1 pt2 pt3 pt4 [onseg]) Finds the intersection of two lines
osnap  (osnap pt mode) Returns a 3D point that is the result of applying an Object Snap mode to a specified point
polar  (polar pt ang dist) Returns the UCS 3D point at a specified angle and distance from a point
getangle  (getangle [pt] [msg]) Pauses for user input of an angle, and returns that angle in radians
getdist  (getdist [pt] [msg]) Pauses for user input of a distance
getint  (getint [msg]) Pauses for user input of an integer, and returns that integer
getorient  (getorient [pt] [msg]) Pauses for user input of an angle, and returns that angle in radians
getpoint  (getpoint [pt] [msg]) Pauses for user input of a point, and returns that point
getreal  (getreal [msg]) Pauses for user input of a real number, and returns that real number
getstring  (getstring [cr] [msg]) Pauses for user input of a string, and returns that string
entsel  (entsel [msg]) Prompts the user to select a single object (entity) by specifying a point
entdel  (entdel ename) Deletes objects (entities) or restores previously deleted objects
entget  (entget ename [applist]) Retrieves an object’s definition data
entlast  (entlast) Returns the name of the last non-deleted main object in the drawing
entmod  (entmod elist) Modifies the definition data of an object
entnext  (entnext [ename]) Returns the name of the next object in the drawing
entupd  (entupd ename) Updates the screen image of an object
ssadd  (ssadd [ename [ss]]) Adds an object (entity) to a selection set, or creates a new selection set
ssdel  (ssdel ename ss) Deletes an object (entity) from a selection set
ssget  (ssget [mode] [pt1 [pt2]] [pt-list] [filter-list]) Prompts the user to select objects (entities), and returns a selection set
sslength  (sslength ss) Returns an integer containing the number of objects (entities) in a selection set
ssmemb  (ssmemb ename ss) Tests whether an object (entity) is a member of a selection set
ssname  (ssname ss index) Returns the object (entity) name of the indexed element of a selection set
grread  (grread [track] [allkeys [curtype]]) Reads values from any of the AutoCAD input devices
graphscr  (graphscr) Displays the AutoCAD graphics screen
grdraw  (grdraw from to color [highlight]) Draws a vector between two points, in the current viewport
grtext  (grtext [box text [highlight]]) Writes text to the status line or to screen menu areas
menucmd  (menucmd string) Issues menu commands, or sets and retrieves menu item status
prin1  (prin1 [expr [file-desc]]) Prints an expression to the command line or writes an expression to an open file
princ  (princ [expr [file-desc]]) Prints an expression to the command line, or writes an expression to an open file
print  (print [expr [file-desc]]) Prints an expression to the command line, or writes an expression to an open file
prompt  (prompt msg) Displays a string on your screen’s prompt area
redraw  (redraw [ename [mode]]) Redraws the current viewport or a specified object (entity) in the current viewport
terpri  (terpri) Prints a newline to the Command line