Working with Extended Data in VBA and AutoLISP1 Jan, 2000 By: Bill Kramer
Extended data is nongraphic information that is attached to AutoCAD entity objects in your drawing. It is very useful when you are writing programs that need additional information on top of the data already present in an object. For example, suppose you wanted to draw a fence line on a map and along with the data points you wanted to include information about the last time the fence section was repaired or updated. Extended data would work well for this application.
This month we are going to explore extended data manipulated by a VBA program as compared to one manipulated in AutoLISP. There are some important differences in the way you go about such an activity depending on the language you select. They are summarized in the fact that in VBA we manipulate objects while in AutoLISP we manipulate entity data lists. Using objects for such manipulations is much easier to fathom than navigating convoluted entity data lists as will be seen. The only potential uncomfortable part for beginners involves the use of variant arrays in VBA. But once that concept is under control, the rest is easy.
Extended data is stored in an object using group codes just as with all other data found in entity objects. From the AutoLISP and DXF programming perspective, this is the normal way entity objects are accessed. That is, each component in an entity object has a group
Common Extended Data Group Codes
Code Represents Data type 1000 Application name Application name 1001 Text value String 1005 Entity handle string String 1010 Data point X Real 1020 Data point Y Real 1030 Data point Z Real 1040 Number Real 1041 Scaled distance value Real 1070 Integer Integer value
Group codes can be repeated over and over again in the extended data. Should your application require five real numbers, then five different instances of the 1040 group code would be used. The group codes, their order and their associated data values are completely controlled by your application. In fact, the only restriction you will face in the use of group codes is that each data object can only hold up to 16 kilobytes of extended data. That's a lot of information and most applications will not even come close to this requirement. If they do, then another data storage strategy is strongly recommended.
Multiple applications can also store extended data in a single entity object. Your program may store data and so may others at the same time. And AutoCAD does not mix them up. The way in which AutoCAD keeps the information intact is actually your responsibility.
Using a common name can lead to confusion where the extended data is concerned.
Using a unique name, your data is differentiated from that of other programs. The extended data name is of your own design and should be unique for your application.
One way to obtain a unique name for your extended data is to search through the registered applications with a given name and see if it has been used. In AutoLISP you accomplish this using the (TBLSEARCH) subr, as shown in Listing 1 (All listings are at the bottom of this page). The function New_AppID has one argument-a seed name as devised by your application. Use any name you want. Based on internal storage and searching requirements in AutoCAD, a registered application name should not be too long, so I recommend that you keep the name to about 15 or 20 characters in length. Of course it can be longer, but why use up the space for a longer name?
Listing 2 shows the same in VBA. The extended data application names are stored in a collection named RegisteredApplications. In VBA a DO loop is used to access the collection until a new name is found. Item() is used inside the loop with the key name (a string) containing the seed string and the incrementing counter, just as in the AutoLISP code. After the function finds a new application name, it is added to the collection. The addition of the name to the collection is not required in VBA as it is in AutoLISP. VBA will add a new name the first time it is used as part of an entity object update.
After an application name has been determined, your program can now add extended data to AutoCAD entity objects. At first glance, the approach is a bit confusing. Since the extended data is of your own design and AutoCAD has no way of knowing what you may want to store, general-purpose storage containers have been devised for the purpose.
In AutoLISP, all extended data is appended to objects using a -3 group code in the entity list. Within the group code you can find all of the extended data that has been added to the object. It is a nested list within the entity list and thus the confusion for many beginning AutoLISP programmers. Sometimes the issue is cleared up a little when looking at what is required when adding extended data.
Adding extended data to an object in AutoLISP is simply a matter of appending it to an existing (or new) entity data list. The extended data is stored in a list with the group codes appropriate to the data type. The following is a simple example of a string, a real number and an integer stored in an extended data list format.
((1001 . "String") (1040 . 1.234) (1070 . 12))The data appears in the same style as a regular entity data list. The only differences are that the group codes used are in the 1000 series, and the data itself is of your own design. But this list represents just the data itself. To add it to an entity data list, you must add the application name to the front. If our registered application name is "EXAMPLE", then the list will look similar to the following:
("EXAMPLE" (1001 . "String") (1040 . 1.234) (1070 . 12))Now you add the group code for extended data. There can be any number (up to the memory limits of extended data) of Xdata lists contained inside the -3 group code set. Each must have a unique name as the first member of the list. And each name must be registered first using (REGAPP) so that AutoCAD recognizes it as valid extended data.
(-3 ("EXAMPLE" (1001 . "String") (1040 . 1.234) (1070 . 12)))The extended data list with group code -3 is then appended to the end of an entity data list that is in turn written to the drawing database. Appending the list can be accomplished through the use of the (APPEND) subr with an existing entity data list and then writing the data to the drawing with (ENTMOD). New entities can have the extended data list included as part of the new entity data list that is written to the drawing database using the (ENTMAKE) subr. Because of the nested lists involved, many novice AutoLISP programmers tend to shy away from the use of extended data or use utility routines that shield them from the nasty details. (Note that extended data utility routines for AutoLISP programmers can be obtained from the Web site listed at the end of this column.)
By comparison, the addition of extended data in VBA involves a different approach. In VBA, the extended data is added to an object after it has been added to the drawing database. The entity is created first to obtain an entity object that can be used to reference the method available for storing the extended data. All AutoCAD drawing objects support the method SetXData. This method allows a program to affix extended data to an existing drawing object. Listing 3 demonstrates how the SetXData method works with an existing object supplied as the type AcadEntity. In this simple example, an entity object is supplied along with three data elements (a string, a real number and an integer). The object's SetXData method is used to write the three data elements to the drawing database after they are placed into a variant array along with an application name ("MyData"in the example listing).
Integer and Variant Arrays
Two arrays are required for the SetXData method. The first array contains only integer values that are the matching group codes for the data being attached. The other array is a variant array that contains the data itself. Thus to add a string one must put a 1001 group code into the integer array and the string value into the variant array at the same position.
At the head of the variant/integer array combination is the application name stored with a 1000 group code. Without this value, the extended data does not have a valid registered application name and will not be added. The function shown in Listing 2 can be used to obtain a unique name for your extended data, or you can simply make one up as shown in Listing 3. One item of note is that you do not have to register the application name being used in the extended data when using VBA. All you need to do is use it, and it will be added to the registered names for you automatically. But if you want to guarantee that the name you are using is your own, registering it first is the only way to go. Otherwise you could be carelessly overwriting someone else's extended data!
Variant arrays are used in the writing and retrieving of extended data in VBA. Variants allow for multiple data storage types all referenced with a common name. This is much like lists in AutoLISP. The member of a list or variant array can be of any supported data type. When the SetXData member is used with a valid entity object, the extended data is written to the object in the drawing database. Internally the system converts the data in the variant array using the group codes supplied in the integer array to store the data in proper containers. Although the data type is found in the variant data elements themselves, your application may want to treat the values differently and thus the group codes are required to further classify the data. For example, group code 1040 signifies a normal real number, but you use group code 1041 when you want the number to be automatically updated whenever the object is scaled. When AutoCAD sees a group code 1041 being added as extended data, it sets up the scaling operation for us thereby relieving our application of the burden.
Getting extended data from an object requires the use of the registered application name. Since more than one application may attach data to an object, the data you are interested in must be specifically requested by name. In AutoLISP this is achieved in the (ENTGET) subr with the inclusion of a list of application names following the entity name to get. To illustrate, the following expression is supplied with an entity name (EN) and will return an entity list (stored in EL) with extended data that has the application name "EXAMPLE".
(setq EL (entget EN ,("EXAMPLE")))The application name is stored in a list so that multiple applications can be retrieved in a single statement. Extended data is then found using the -3 group code in the entity data list. The (cdr (assoc -3 EL)) expression will return the extended data list. If multiple extended data lists are being used, the (ASSOC) subr can then be used to get them from within the extended data set. If only one set of extended data is requested, the (ASSOC) can be used, but the common approach is to use composite primitives (CAR) and (CDR) instead. Using the example I started earlier in this column, the variable XD is set to the extended data found in the entity list EL.
(setq XD (cdr (assoc -3 EL)))The above code results in the following when used with our previous example data.
(("EXAMPLE" (1001 . "String") (1040 . 1.234) (1070 . 12)))The extended data can now be further reduced to something more useful. Taking the (CAR) of this list will return the "EXAMPLE" extended data and then taking the (CDR) will result in just the extended data without the application name "EXAMPLE" at the beginning. The next expression places just the extended data into the list XD.
(setq XD (cdar XD)) ((1001 . "String") (1040 . 1.234) (1070 . 12))This is why retrieving extended data looks complicated in AutoLISP. The operations just outlined are often bunched together into a single expression, such as the following:
(setq XD (cdadr (assoc -3 EL)))The composite primitive (CDAR) was expanded to include the (CDR) used to remove the -3 group code obtained from the (ASSOC) subr. Although elegant in the conservative usage of code, this sort of data manipulation can be complicated to follow in the source code. That is where VBA is superior. The access of extended data from an object in VBA is accomplished with the GetXData method. Given the application name as the first parameter to GetXData, the method will fill in two variants with array data. These variant arrays contain the group code integers and the data values respectively. The data will be returned in the same order as it was placed into the entity object.
Listing 4 contains a portion of code that will ask the operator to select an entity and will then retrieve the extended data for an application named "MyData". (Again, a more complete example using a dialog box to display extended data can be found at the Web site listed at the end of the column.)
Retrieving Extended Data in VBA
Retrieval of extended data in VBA relies heavily on variant data types. The values in XdT and XdV from Listing 4 can be retrieved using LBound and UBound. LBound(XdT) will return the index of the first entry in the array; UBound (XdT) will result in the last index in the array. An incrementing counter can be used to loop from one value to the next and obtain the extended data elements.
Both AutoLISP and VBA provide generic methods for obtaining extended data since AutoCAD cannot possibly know what your intentions may be when developing an application that uses them. Some applications may even use multiple registered application names to help keep the extended data separated based on the demands of various interrelated components. As an example, a beam centerline object may contain details about the cross section of the beam as a set of extended data and information about the current loading on the beam as another. That way the application interested in only the cross section can obtain that information directly without having to wade through the loading data and so forth.
It is the generic nature of extended data that scares many people away until they find a need for it. But it is also that same nature that gives extended data the power and flexibility needed to be used in the engineering and architectural computer graphics arena. Your applications and those of other developers can utilize this tool set to build intelligent objects. The next step is into reactors that are tied to these objects so that your application runs as they are manipulated. Using extended data to hold the data and reactors to maintain the integrity of the overall system greatly enhances the way your applications will work with the users. Until next time, keep on programming!
(All code along with more complete examples can be found at www.autocode.com, the author's primary Web site.)
Find a Unique Application Name(defun New_AppID (
SeedName ;String name for application / Cnt ;incrementing counter ) (setq CNT 1) ;;initialize counter (while (tblsearch "APPID" (strcat SeedName (itoa Cnt))) ;;Found one, increment counter (setq Cnt (1+ Cnt))) ;;register the application (regapp (strcat SeedName (itoa Cnt))) ;;return name used (strcat SeedName (itoa Cnt))
Add New Application in VBAPrivate Function NewAppID( _
Seed As String) _ As String Dim I As Integer I = 0 On Error Resume Next With ThisDrawing.RegisteredApplications Do I = I + 1 Err.Clear .Item(Seed & Str(I)) Loop Until Err.Number <> 0 .Add (Seed & Str(I)) End With NewAppID = Seed & Str(I) End Function
Adding Xdata in VBAPrivate Sub XDataAdd( _
Obj As AcadEntity, _ Item1 As String, _ Item2 As Double, _ Item3 As Integer _ ) Dim TypArray(0 To 3) As Integer Dim ValueArray(0 To 3) As Variant TypArray(0) = 1001 'Application name ValueArray(0) = "MyData" ' TypArray(1) = 1000 'string item ValueArray(1) = Item1 ' TypArray(2) = 1040 'real number ValueArray(2) = Item2 ' TypArray(3) = 1070 'integer number ValueArray(3) = Item3 ' Obj.SetXData TypArray, ValueArray End Sub
Retrieve Xdata in VBADim Ob As AcadEntity 'Entity object
Dim PP As Variant 'Selection point Dim XdT As Variant 'Group codes Dim XdV As Variant 'Data values On Error Resume Next ThisDrawing.Utility.GetEntity Ob, PP, "Select" If Err.Number = 0 Then Ob.GetXData "MyData", XdT, XdV ' process continues==