AutoCAD Driving Lessons30 Apr, 2003 By: Bill Kramer
When programming AutoCAD applications using VBA, there is a conflict that sometimes manifests itself. I say sometimes because it is not applicable to all situations--every incident is unique. It has to do with your background in using AutoCAD and programming. You may be a seasoned VB programmer who's been asked to write an application for AutoCAD. You may be an expert AutoCAD operator who has just started learning VBA. Or you may be new to both VB and AutoCAD. The conflict is how to drive AutoCAD.
Driving an application means that you have a program issue commands to another program so the latter can react accordingly. This is often known as the client-server relationship. The client program makes requests while the server accommodates them. In the case of driving AutoCAD, your program is the client and AutoCAD is the server. The client program makes requests such as "create a new graphic object" or "change this graphic object" to the server program, AutoCAD. AutoCAD then responds. That's essentially driving AutoCAD from another application.
Driving AutoCAD also describes an operator's task. That is where you sit in front of the computer and issue commands to accomplish something. In this case, you can think of yourself as the client and AutoCAD, once again, as the server.
The conflict comes up when one considers the correlation between a human driving AutoCAD and a computer program driving AutoCAD. The goal is to create a program that does essentially the same thing you do as a user. So the first place to look is the command sequence that you actually use to manually drive the client program. VBA provides a method called SendCommand that is included in the AcadDocument object.SendCommand
The SendCommand method accepts a string as a single parameter. The string is sent to the command line as if you have typed it in. There are a few notable differences but, for the most part, you drive AutoCAD by sending the same commands you already know. If you want AutoCAD to draw a circle with a radius of 1 and a center point of 2,2, then you use the following statement:
"CIRCLE 2,2 1 "
Spaces are inserted between the command elements just as you would when defining a macro for a menu. Unlike a menu macro, this method does not accept semicolons (;) as placers between the components. As an alternative, you may use the VB constant vbCr (or vbCrLf) when concatenating a string. Let us assume, instead of a constant radius, the value comes from a real number variable.
Dim Rad as Double
Rad = 1.25
"CIRCLE 2,2 " & Format(Rad) & vbCr
In the example above, the variable "Rad" contains the value 1.25, which must be converted from a double-precision real number into a string. There are two functions in VBA that can be used for this purpose: Format and Str$. Format has an advantage in this particular case since Str$ will insert a space as the first character for all non-negative numbers. For negative numbers, the first character is always a dash (minus sign). Since spaces are considered the same as pressing the Enter key, this will terminate the Circle command prematurely. A space has already been provided following the center point value. Thus, Format is the preferred way to convert numbers to strings in similar cases.
An important thing to remember when using the SendCommand method is to finish the command before ending the parameter string. You cannot start a command, do some more processing, and continue the command. You must do the processing first and then issue the command sequence--all at once. Rewriting the command sequence used above in the following manner will NOT work.
"CIRCLE 2,2 "
ThisDrawing.SendCommand Format(Rad) & vbCr
If you run the line above in VBA, the circle command will start but it won't have enough data to complete. So it'll issue the prompt to the user for the radius. Pressing the Enter key will then cause the program evaluation to continue and the next thing the program sees is the radius value. Since AutoCAD has finished the command, a number typed in response to the Command prompt will be considered an illegal command.
More importantly, you should not use SendCommand when a method already exists to accomplish the task. Instead of the VBA programming sending a string to the command handler, it is much faster to directly access the subroutines that do the job. Besides, you will avoid possible internal problems inside the AutoCAD system from certain tasks being considered busy due to the VBA program that's already loaded and running. These busy states may result in the request being put on hold until the system is no longer busy and, from a programming point of view, you can "hang up the system" or cause it to appear to have stopped running (it's running--just not getting anywhere).
The processing sequence of the computer when using SendCommand in a VBA program can be somewhat confusing if you are not careful. When the VBA program encounters SendCommand, the string is sent to the AutoCAD system and control is not returned until the processing is completed. The key is in knowing when the processing has stopped. If command process is not completely filled out, then the command is left running, waiting for input, or is terminated early. In either case, the control is immediately returned to the VBA program, which then proceeds with the task. If the command processor is left waiting for user input, the two processes are now running simultaneously. AutoCAD is waiting for user input and your program is running ahead, assuming the SendCommand has been completed as required. If you follow the rules and always make sure that SendCommand has a complete string to work with, then you will not run into this particular problem.
The SendCommand method can also be used to issue Visual LISP commands. The same rules apply. As soon as the Visual LISP routine requests user input, the VBA routine resumes, thus causing both to run at the same time. Except for very advanced applications, this may not be the desired result. In most cases, you will simply want to take advantage of a particular feature from Visual LISP that can be defined into a self-contained function.Creating and Manipulating Objects
With the rich command system of AutoCAD and the SendCommand method, you would think we don't really need much more; however, that cannot be farther from the truth. Some applications will only create new entities while others will want to manipulate existing entities. So long as these operations are very simple in scope, the SendCommand approach may work just fine. But there is a better method for most activities of this type in the exposed object system.
The reason many AutoCAD users do not care for the object system at first is it seems somewhat foreign. After you have used AutoCAD for a number of years, the command system is very comfortable, and you learn to structure your work to make the most of it. But the object system is not really all that foreign from a conceptual point of view--just in the way things are written in the code.
Consider the circle object creation. In the SendCommand approach, you supply the command, the center point, and the radius. All other properties that the circle will assume--such as the layer name, linetype, color, and thickness--are from the defaults inside AutoCAD. If you want to change them, you need to run one of the commands to either adjust the defaults ahead of time or edit the objects created.
In the object approach, the thinking is the same. Instead of a string denoting the type of command, you use a method that is associated with the proper object. To add a circle to the drawing database, one needs to attach it to a block object such as the Model Space block object. Using the base object in VBA, ThisDrawing, select the block object by either getting it from the blocks collection or, in the case of Model Space, just use the direct reference. The following line of code adds a circle object to Model Space. The center point and radius values are housed in the variables CP and Rad. We will look at these variables after understanding what this line does.
AddCircle CP, Rad
This line creates a circle object in Model Space. We could set the default properties for layers, colors, elevations, and linetypes ahead of time, or we can use the object system to simplify that activity greatly. For example, the following code will adjust the layer property of the circle after it has been created to a new value of 1.
Dim aCircle As AcadCircle
Set aCircle = ThisDrawing.
aCircle.Layer = "1"
The variable "aCircle" is an object reference. This means that "Set" is used when assigning a value to it and, once established, you have full access to the properties of the object. As soon as the layer assignment is made, the circle object is updated. You don't have to tell AutoCAD to do anything else. The circle object is moved to the other layer. When manipulating complex objects, you may need to issue a display regeneration request in order to see the results of the manipulation. Internally in AutoCAD these changes take place fast.
Now let's look at the center point and radius quickly. The radius value is a double-precision real number, as we have seen. What is different is that it does not require conversion to a string. The CP value on the other hand is new. It is a variant that points to an array of three numbers (a data point). To pass the numbers into the AddCircle method we must first assign them to an array, then assign the array to the variant. Here's a quick example.
Dim CP as Variant
Dim PT(0 to 2) as Double
PT(0) = 1.5
PT(1) = 2.5
PT(2) = 0#
CP = PT
I realize this may seem like a lot, but consider what it takes to move data from an array of this type to a string suitable for output to the SendCommand method. It really is easier. In most cases you will be working with point arrays to convert them to a string and send them to the command interpreter, which in turn converts them back to a number. This is just not as efficient as sending a pointer to the array in the first place.
With practice you will get used to the object approach instead of the command approach. Both are very similar in the way they approach a problem--they just use different coding. So, with that, keep on programming!